1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*******************************************************************************
3 * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG
4 * All rights reserved.
5 ******************************************************************************/
6
7 #ifdef HAVE_CONFIG_H
8 #include <config.h>
9 #endif
10
11 #include <stdio.h>
12 #include <string.h>
13
14 #include "tss2_fapi.h"
15 #include "fapi_int.h"
16 #include "fapi_util.h"
17 #include "tss2_esys.h"
18 #define LOGMODULE fapi
19 #include "util/log.h"
20 #include "util/aux_util.h"
21
22 /** One-Call function for Fapi_Quote
23 *
24 * Given a set of PCRs and a restricted signing key, it will sign those PCRs and
25 * return the quote.
26 *
27 * @param[in,out] context The FAPI_CONTEXT
28 * @param[in] pcrList The list of PCRs that are to be quoted
29 * @param[in] pcrListSize The size of pcrList in bytes
30 * @param[in] keyPath The path to the signing key
31 * @param[in] quoteType The type of quote. May be NULL
32 * @param[in] qualifyingData A nonce provided by the caller. May be NULL
33 * @param[in] qualifyingDataSize The size of qualifyingData in bytes. Must be 0
34 * if qualifyingData is NULL
35 * @param[out] quoteInfo A JSON-encoded structure holding the inputs to the
36 * quote operation
37 * @param[out] signature The signature of the PCRs
38 * @param[out] signatureSize The size of the signature in bytes. May be NULL
39 * @param[out] pcrLog The log of the PCR. May be NULL
40 * @param[out] certificate The certificate associated with the signing key. May
41 * be NULL
42 *
43 * @retval TSS2_RC_SUCCESS: if the function call was a success.
44 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, pcrList, keyPath,
45 * quoteInfo or signature is NULL.
46 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
47 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if path does not map to a FAPI entity.
48 * @retval TSS2_FAPI_RC_BAD_KEY: if the entity at path is not a key, or is a key
49 * that is unsuitable for the requested operation.
50 * @retval TSS2_FAPI_RC_BAD_VALUE: if qualifyingData is invalid or if
51 * qualifyingDataSize is zero.
52 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
53 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
54 * operation already pending.
55 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
56 * internal operations or return parameters.
57 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
58 * config file.
59 * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
60 * this function needs to be called again.
61 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
62 * during authorization.
63 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
64 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
65 * is not set.
66 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
67 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
68 * was not successful.
69 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
70 */
71 TSS2_RC
Fapi_Quote(FAPI_CONTEXT * context,uint32_t * pcrList,size_t pcrListSize,char const * keyPath,char const * quoteType,uint8_t const * qualifyingData,size_t qualifyingDataSize,char ** quoteInfo,uint8_t ** signature,size_t * signatureSize,char ** pcrLog,char ** certificate)72 Fapi_Quote(
73 FAPI_CONTEXT *context,
74 uint32_t *pcrList,
75 size_t pcrListSize,
76 char const *keyPath,
77 char const *quoteType,
78 uint8_t const *qualifyingData,
79 size_t qualifyingDataSize,
80 char **quoteInfo,
81 uint8_t **signature,
82 size_t *signatureSize,
83 char **pcrLog,
84 char **certificate)
85 {
86 LOG_TRACE("called for context:%p", context);
87
88 TSS2_RC r, r2;
89
90 /* Check for NULL parameters */
91 check_not_null(context);
92 check_not_null(pcrList);
93 check_not_null(keyPath);
94 check_not_null(quoteInfo);
95 check_not_null(signature);
96
97 /* Check whether TCTI and ESYS are initialized */
98 return_if_null(context->esys, "Command can't be executed in none TPM mode.",
99 TSS2_FAPI_RC_NO_TPM);
100
101 /* If the async state automata of FAPI shall be tested, then we must not set
102 the timeouts of ESYS to blocking mode.
103 During testing, the mssim tcti will ensure multiple re-invocations.
104 Usually however the synchronous invocations of FAPI shall instruct ESYS
105 to block until a result is available. */
106 #ifndef TEST_FAPI_ASYNC
107 r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
108 return_if_error_reset_state(r, "Set Timeout to blocking");
109 #endif /* TEST_FAPI_ASYNC */
110
111 r = Fapi_Quote_Async(context, pcrList, pcrListSize, keyPath, quoteType,
112 qualifyingData, qualifyingDataSize);
113 return_if_error_reset_state(r, "PCR_Quote");
114
115 do {
116 /* We wait for file I/O to be ready if the FAPI state automata
117 are in a file I/O state. */
118 r = ifapi_io_poll(&context->io);
119 return_if_error(r, "Something went wrong with IO polling");
120
121 /* Repeatedly call the finish function, until FAPI has transitioned
122 through all execution stages / states of this invocation. */
123 r = Fapi_Quote_Finish(context, quoteInfo, signature, signatureSize,
124 pcrLog, certificate);
125 } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
126
127 /* Reset the ESYS timeout to non-blocking, immediate response. */
128 r2 = Esys_SetTimeout(context->esys, 0);
129 return_if_error(r2, "Set Timeout to non-blocking");
130
131 return_if_error_reset_state(r, "PCR_Quote");
132
133 LOG_TRACE("finished");
134 return TSS2_RC_SUCCESS;
135 }
136
137 /** Asynchronous function for Fapi_Quote
138 *
139 * Given a set of PCRs and a restricted signing key, it will sign those PCRs and
140 * return the quote.
141 *
142 * Call Fapi_Quote_Finish to finish the execution of this command.
143 *
144 * @param[in,out] context The FAPI_CONTEXT
145 * @param[in] pcrList The list of PCRs that are to be quoted
146 * @param[in] pcrListSize The size of pcrList in bytes
147 * @param[in] keyPath The path to the signing key
148 * @param[in] quoteType The type of quote. May be NULL
149 * @param[in] qualifyingData A nonce provided by the caller. May be NULL
150 * @param[in] qualifyingDataSize The size of qualifyingData in bytes. Must be 0
151 * if qualifyingData is NULL
152 *
153 * @retval TSS2_RC_SUCCESS: if the function call was a success.
154 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, pcrList or keyPath
155 * is NULL.
156 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
157 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if path does not map to a FAPI entity.
158 * @retval TSS2_FAPI_RC_BAD_KEY: if the entity at path is not a key, or is a key
159 * that is unsuitable for the requested operation.
160 * @retval TSS2_FAPI_RC_BAD_VALUE: if pcrListSize is 0, qualifyingData is
161 * invalid or if qualifyingDataSize is zero.
162 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
163 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
164 * operation already pending.
165 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
166 * internal operations or return parameters.
167 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
168 * config file.
169 */
170 TSS2_RC
Fapi_Quote_Async(FAPI_CONTEXT * context,uint32_t * pcrList,size_t pcrListSize,char const * keyPath,char const * quoteType,uint8_t const * qualifyingData,size_t qualifyingDataSize)171 Fapi_Quote_Async(
172 FAPI_CONTEXT *context,
173 uint32_t *pcrList,
174 size_t pcrListSize,
175 char const *keyPath,
176 char const *quoteType,
177 uint8_t const *qualifyingData,
178 size_t qualifyingDataSize)
179 {
180 LOG_TRACE("called for context:%p", context);
181 LOG_TRACE("pcrListSize: %zi", pcrListSize);
182 for (size_t i = 0; i < pcrListSize; i++) {
183 LOG_TRACE("PCR list entry %zu: %ul", i, pcrList[i]);
184 }
185 LOG_TRACE("keyPath: %s", keyPath);
186 LOG_TRACE("quoteType: %s", quoteType);
187 if (qualifyingData) {
188 LOGBLOB_TRACE(qualifyingData, qualifyingDataSize, "qualifyingData");
189 } else {
190 LOG_TRACE("qualifyingData: (null) qualifyingDataSize: %zi", qualifyingDataSize);
191 }
192
193 TSS2_RC r;
194
195 /* Check for NULL parameters */
196 check_not_null(context);
197 check_not_null(pcrList);
198 check_not_null(keyPath);
199
200 /* Check for invalid parameters */
201 if (pcrListSize == 0) {
202 LOG_ERROR("pcrListSize must not be NULL");
203 return TSS2_FAPI_RC_BAD_VALUE;
204 }
205 if (qualifyingData == NULL && qualifyingDataSize) {
206 LOG_ERROR("QualifyingData is NULL but qualifyingDataSize is not 0");
207 return TSS2_FAPI_RC_BAD_VALUE;
208 }
209
210 /* Helpful alias pointers */
211 IFAPI_PCR * command = &context->cmd.pcr;
212
213 /* Reset all context-internal session state information. */
214 r = ifapi_session_init(context);
215 return_if_error(r, "Initialize Quote");
216
217 if (quoteType && strcmp(quoteType, "TPM-Quote") != 0) {
218 return_error(TSS2_FAPI_RC_BAD_VALUE,
219 "Only quote type TPM-Quote is allowed");
220 }
221
222 /* Store parameters in context */
223 strdup_check(command->keyPath, keyPath, r, error_cleanup);
224
225 command->pcrList = malloc(pcrListSize * sizeof(TPM2_HANDLE));
226 goto_if_null2(command->pcrList, "Out of memory", r, TSS2_FAPI_RC_MEMORY,
227 error_cleanup);
228 memcpy(command->pcrList, pcrList, pcrListSize);
229
230 command->pcrListSize = pcrListSize;
231 command->tpm_quoted = NULL;
232 if (qualifyingData != NULL) {
233 FAPI_COPY_DIGEST(&command->qualifyingData.buffer[0],
234 command->qualifyingData.size, qualifyingData, qualifyingDataSize);
235 } else {
236 command->qualifyingData.size = 0;
237 }
238
239 /* Initialize the context state for this operation. */
240 context->state = PCR_QUOTE_WAIT_FOR_GET_CAP;
241 LOG_TRACE("finished");
242 return TSS2_RC_SUCCESS;
243
244 error_cleanup:
245 /* Cleanup duplicated input parameters that were copied before. */
246 SAFE_FREE(command->keyPath);
247 SAFE_FREE(command->pcrList);
248 return r;
249 }
250
251 /** Asynchronous finish function for Fapi_Quote
252 *
253 * This function should be called after a previous Fapi_Quote_Async.
254 *
255 * @param[in,out] context The FAPI_CONTEXT
256 * @param[out] quoteInfo A JSON-encoded structure holding the inputs to the
257 * quote operation
258 * @param[out] signature The signature of the PCRs
259 * @param[out] signatureSize The size of the signature in bytes. May be NULL
260 * @param[out] pcrLog The log of the PCR. May be NULL
261 * @param[out] certificate The certificate associated with the signing key. May
262 * be NULL
263 *
264 * @retval TSS2_RC_SUCCESS: if the function call was a success.
265 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, quoteInfor or signature
266 * is NULL.
267 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
268 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
269 * operation already pending.
270 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
271 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
272 * internal operations or return parameters.
273 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
274 * complete. Call this function again later.
275 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
276 * the function.
277 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
278 * during authorization.
279 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
280 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
281 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
282 * is not set.
283 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
284 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
285 * was not successful.
286 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
287 */
288 TSS2_RC
Fapi_Quote_Finish(FAPI_CONTEXT * context,char ** quoteInfo,uint8_t ** signature,size_t * signatureSize,char ** pcrLog,char ** certificate)289 Fapi_Quote_Finish(
290 FAPI_CONTEXT *context,
291 char **quoteInfo,
292 uint8_t **signature,
293 size_t *signatureSize,
294 char **pcrLog,
295 char **certificate)
296 {
297 LOG_TRACE("called for context:%p", context);
298
299 TSS2_RC r;
300 IFAPI_OBJECT *sig_key_object;
301 const IFAPI_PROFILE *profile;
302 ESYS_TR auth_session;
303
304 /* Check for NULL parameters */
305 check_not_null(context);
306 check_not_null(quoteInfo);
307 check_not_null(signature);
308
309 /* Helpful alias pointers */
310 IFAPI_PCR * command = &context->cmd.pcr;
311
312 switch (context->state) {
313 statecase(context->state, PCR_QUOTE_WAIT_FOR_GET_CAP);
314 command->pcr_selection = context->profiles.default_profile.pcr_selection;
315
316 r = ifapi_filter_pcr_selection_by_index(&command->pcr_selection,
317 command->pcrList,
318 command->pcrListSize);
319 goto_if_error_reset_state(r, "Filtering banks for PCR list.", error_cleanup);
320
321 /* Get a session for authorization of the quote operation. */
322 r = ifapi_get_sessions_async(context,
323 IFAPI_SESSION_GENEK | IFAPI_SESSION1,
324 TPMA_SESSION_DECRYPT, 0);
325 goto_if_error_reset_state(r, "Create sessions", error_cleanup);
326
327 fallthrough;
328
329 statecase(context->state, PCR_QUOTE_WAIT_FOR_SESSION);
330 /* Retrieve the profile information. */
331 r = ifapi_profiles_get(&context->profiles, command->keyPath, &profile);
332 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
333
334 r = ifapi_get_sessions_finish(context, profile, profile->nameAlg);
335 return_try_again(r);
336 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
337
338 /* Load the key into the TPM. */
339 r = ifapi_load_keys_async(context, command->keyPath);
340 goto_if_error(r, "Load keys.", error_cleanup);
341
342 fallthrough;
343
344 statecase(context->state, PCR_QUOTE_WAIT_FOR_KEY);
345 r = ifapi_load_keys_finish(context, IFAPI_FLUSH_PARENT,
346 &command->handle,
347 &command->key_object);
348 return_try_again(r);
349 goto_if_error_reset_state(r, " Load key.", error_cleanup);
350
351 fallthrough;
352
353 statecase(context->state, PCR_QUOTE_AUTHORIZE);
354 /* Authorize the session for use with the key. */
355 r = ifapi_authorize_object(context, command->key_object, &auth_session);
356 return_try_again(r);
357 goto_if_error(r, "Authorize key.", error_cleanup);
358
359 /* Perform the Quote operation. */
360 r = Esys_Quote_Async(context->esys, command->handle,
361 auth_session, ESYS_TR_NONE, ESYS_TR_NONE,
362 &command->qualifyingData,
363 &command->key_object->misc.key.signing_scheme,
364 &command->pcr_selection);
365 goto_if_error(r, "Error: PCR_Quote", error_cleanup);
366
367 fallthrough;
368
369 statecase(context->state, PCR_QUOTE_AUTH_SENT);
370 command->tpm_signature = NULL;
371 SAFE_FREE(command->tpm_signature);
372 r = Esys_Quote_Finish(context->esys, &command->tpm_quoted,
373 &command->tpm_signature);
374 return_try_again(r);
375 goto_if_error(r, "Error: PCR_Quote", error_cleanup);
376
377 /* Flush the key used for the quote. */
378 r = Esys_FlushContext_Async(context->esys, command->handle);
379 goto_if_error(r, "Error: FlushContext", error_cleanup);
380
381 fallthrough;
382
383 statecase(context->state, PCR_QUOTE_WAIT_FOR_FLUSH);
384 r = Esys_FlushContext_Finish(context->esys);
385 return_try_again(r);
386 goto_if_error(r, "Error: Sign", error_cleanup);
387
388 sig_key_object = command->key_object;
389 /* Convert the TPM-encoded signature into something useful for the caller. */
390 r = ifapi_tpm_to_fapi_signature(sig_key_object,
391 command->tpm_signature,
392 signature, signatureSize);
393 SAFE_FREE(command->tpm_signature);
394 goto_if_error(r, "Create FAPI signature.", error_cleanup);
395
396 /* Compute the quote info; i.e. the data that was actually
397 signed by the TPM. */
398 r = ifapi_compute_quote_info(sig_key_object,
399 command->tpm_quoted,
400 quoteInfo);
401 goto_if_error(r, "Create compute quote info.", error_cleanup);
402
403 /* Return the key's certificate if requested. */
404 if (certificate) {
405 strdup_check(*certificate, sig_key_object->misc.key.certificate, r, error_cleanup);
406 }
407
408 /* If the pcrLog was not requested, the operation is done. */
409 if (!pcrLog) {
410 context->state = PCR_QUOTE_CLEANUP;
411 return TSS2_FAPI_RC_TRY_AGAIN;
412 }
413
414 /* Retrieve the eventlog for the PCRs for the quote. */
415 r = ifapi_eventlog_get_async(&context->eventlog, &context->io,
416 command->pcrList,
417 command->pcrListSize);
418 goto_if_error(r, "Error getting event log", error_cleanup);
419
420 fallthrough;
421
422 statecase(context->state, PCR_QUOTE_READ_EVENT_LIST);
423 r = ifapi_eventlog_get_finish(&context->eventlog, &context->io, pcrLog);
424 return_try_again(r);
425 goto_if_error(r, "Error getting event log", error_cleanup);
426 fallthrough;
427
428 statecase(context->state, PCR_QUOTE_CLEANUP)
429 /* Cleanup the session used for authorization. */
430 r = ifapi_cleanup_session(context);
431 try_again_or_error_goto(r, "Cleanup", error_cleanup);
432
433 context->state = _FAPI_STATE_INIT;
434 break;
435
436 statecasedefault(context->state);
437 }
438
439 error_cleanup:
440 /* Cleanup any intermediate results and state stored in the context. */
441 SAFE_FREE(command->tpm_signature);
442 SAFE_FREE(command->tpm_quoted);
443 SAFE_FREE(command->keyPath);
444 SAFE_FREE(command->pcrList);
445 ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
446 ifapi_cleanup_ifapi_object(context->loadKey.key_object);
447 ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
448 ifapi_cleanup_ifapi_object(command->key_object);
449 ifapi_session_clean(context);
450 LOG_TRACE("finished");
451 return r;
452 }
453