• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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