• 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 <stdlib.h>
12 #include <errno.h>
13 #include <unistd.h>
14 #include <errno.h>
15 #include <string.h>
16 
17 #include "tss2_fapi.h"
18 #include "fapi_int.h"
19 #include "fapi_util.h"
20 #include "tss2_esys.h"
21 #include "fapi_crypto.h"
22 #include "fapi_policy.h"
23 #include "ifapi_json_serialize.h"
24 #include "ifapi_json_deserialize.h"
25 #include "tpm_json_deserialize.h"
26 #define LOGMODULE fapi
27 #include "util/log.h"
28 #include "util/aux_util.h"
29 
30 /** One-Call function for Fapi_ExportKey
31  *
32  * Given a key it will (if the key is a storage key) duplicate the key and
33  * package up the duplicated key and all keys below it into a file ready to move to
34  * a new TPM.
35  *
36  * @param[in,out] context The FAPI_CONTEXT
37  * @param[in] pathOfKeyToDuplicate The path to the root of the subtree to
38  *            export.
39  * @param[in] pathToPublicKeyOfNewParent The path to the public key of the new
40  *            parent. May be NULL
41  * @param[out] exportedData The exported subtree
42  *
43  * @retval TSS2_RC_SUCCESS: if the function call was a success.
44  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, pathOfKeyToDuplicate
45  *         or exportedData is NULL.
46  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
47  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if pathOfKeyToDuplicate or
48  *         pathToPublicKeyOfNewParent does not map to a . FAPI object.
49  * @retval TSS2_FAPI_RC_BAD_KEY: if the key at pathToPublicKeyOfNewParent is not
50  *         suitable for the requeste operation.
51  * @retval TSS2_FAPI_RC_KEY_NOT_DUPLICABLE: if the key is not a duplicable key.
52  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
53  *         operation already pending.
54  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
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_PATH_NOT_FOUND if a FAPI object path was not found
60  *         during authorization.
61  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
62  *         the function.
63  * @retval TSS2_FAPI_RC_BAD_PATH if the used path in inappropriate-
64  * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
65  *         this function needs to be called again.
66  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
67  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
68  *         is not set.
69  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
70  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
71  *         was not successful.
72  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
73  */
74 TSS2_RC
Fapi_ExportKey(FAPI_CONTEXT * context,char const * pathOfKeyToDuplicate,char const * pathToPublicKeyOfNewParent,char ** exportedData)75 Fapi_ExportKey(
76     FAPI_CONTEXT *context,
77     char   const *pathOfKeyToDuplicate,
78     char   const *pathToPublicKeyOfNewParent,
79     char        **exportedData)
80 {
81     LOG_TRACE("called for context:%p", context);
82 
83     TSS2_RC r, r2;
84 
85     /* Check for NULL parameters */
86     check_not_null(context);
87     check_not_null(pathOfKeyToDuplicate);
88     check_not_null(exportedData);
89 
90     /* Check whether TCTI and ESYS are initialized */
91     return_if_null(context->esys, "Command can't be executed in none TPM mode.",
92                    TSS2_FAPI_RC_NO_TPM);
93 
94     /* If the async state automata of FAPI shall be tested, then we must not set
95        the timeouts of ESYS to blocking mode.
96        During testing, the mssim tcti will ensure multiple re-invocations.
97        Usually however the synchronous invocations of FAPI shall instruct ESYS
98        to block until a result is available. */
99 #ifndef TEST_FAPI_ASYNC
100     r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
101     return_if_error_reset_state(r, "Set Timeout to blocking");
102 #endif /* TEST_FAPI_ASYNC */
103 
104     r = Fapi_ExportKey_Async(context, pathOfKeyToDuplicate,
105                              pathToPublicKeyOfNewParent);
106     return_if_error_reset_state(r, "ExportKey");
107 
108     do {
109         /* We wait for file I/O to be ready if the FAPI state automata
110            are in a file I/O state. */
111         r = ifapi_io_poll(&context->io);
112         return_if_error(r, "Something went wrong with IO polling");
113 
114         /* Repeatedly call the finish function, until FAPI has transitioned
115            through all execution stages / states of this invocation. */
116         r = Fapi_ExportKey_Finish(context, exportedData);
117     } while ((r & ~TSS2_RC_LAYER_MASK) == TSS2_BASE_RC_TRY_AGAIN);
118 
119     /* Reset the ESYS timeout to non-blocking, immediate response. */
120     r2 = Esys_SetTimeout(context->esys, 0);
121     return_if_error(r2, "Set Timeout to non-blocking");
122 
123     return_if_error_reset_state(r, "ExportKey");
124 
125     LOG_TRACE("finished");
126     return TSS2_RC_SUCCESS;
127 }
128 
129 /** Asynchronous function for Fapi_ExportKey
130  *
131  * Given a key it will (if the key is a storage key) duplicate the key and
132  * package up the duplicated key and all keys below it into a file ready to move to
133  * a new TPM.
134  *
135  * Call Fapi_ExportKey_Finish to finish the execution of this command.
136  *
137  * @param[in,out] context The FAPI_CONTEXT
138  * @param[in] pathOfKeyToDuplicate The path to the root of the subtree to
139  *            export.
140  * @param[in] pathToPublicKeyOfNewParent The path to the public key of the new
141  *            parent
142  *
143  * @retval TSS2_RC_SUCCESS: if the function call was a success.
144  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or pathOfKeyToDuplicate
145  *         is NULL.
146  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
147  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if pathOfKeyToDuplicate or
148  *         pathToPublicKeyOfNewParent does not map to a . FAPI object.
149  * @retval TSS2_FAPI_RC_BAD_KEY: if the key at pathToPublicKeyOfNewParent is not
150  *         suitable for the requeste operation.
151  * @retval TSS2_FAPI_RC_KEY_NOT_DUPLICABLE: if the key is not a duplicable key.
152  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
153  *         operation already pending.
154  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
155  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
156  *         internal operations or return parameters.
157  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
158  *         config file.
159  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
160  *         during authorization.
161  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
162  *         the function.
163  */
164 TSS2_RC
Fapi_ExportKey_Async(FAPI_CONTEXT * context,char const * pathOfKeyToDuplicate,char const * pathToPublicKeyOfNewParent)165 Fapi_ExportKey_Async(
166     FAPI_CONTEXT *context,
167     char   const *pathOfKeyToDuplicate,
168     char   const *pathToPublicKeyOfNewParent)
169 {
170     LOG_TRACE("called for context:%p", context);
171     LOG_TRACE("pathOfKeyToDuplicate: %s", pathOfKeyToDuplicate);
172     LOG_TRACE("pathToPublicKeyOfNewParent: %s", pathToPublicKeyOfNewParent);
173 
174     TSS2_RC r;
175 
176     /* Check for NULL parameters */
177     check_not_null(context);
178     check_not_null(pathOfKeyToDuplicate);
179 
180     /* Helpful alias pointers */
181     IFAPI_ExportKey * command = &context->cmd.ExportKey;
182 
183     /* Reset all context-internal session state information. */
184     r = ifapi_session_init(context);
185     return_if_error(r, "Initialize NV_CreateNv");
186 
187     /* Copy parameters to context for use during _Finish. */
188     command->pathOfKeyToDuplicate = NULL;
189     command->pathToPublicKeyOfNewParent = NULL;
190     strdup_check(command->pathOfKeyToDuplicate, pathOfKeyToDuplicate,
191                  r, error_cleanup);
192     strdup_check(command->pathToPublicKeyOfNewParent,
193                  pathToPublicKeyOfNewParent, r, error_cleanup);
194 
195     if (!pathToPublicKeyOfNewParent) {
196         /* Only public key of KeyToDuplocate will be exported */
197         r = ifapi_keystore_load_async(&context->keystore, &context->io,
198                                       pathOfKeyToDuplicate);
199         return_if_error2(r, "Could not open: %s", pathOfKeyToDuplicate);
200 
201         /* Initialize the context state for this operation. */
202         context->state = EXPORT_KEY_READ_PUB_KEY;
203     } else {
204         /* The public key of the new parent is needed for duplication */
205         r = ifapi_keystore_load_async(&context->keystore, &context->io,
206                                       pathToPublicKeyOfNewParent);
207         return_if_error2(r, "Could not open: %s", pathToPublicKeyOfNewParent);
208 
209         /* Initialize the context state for this operation. */
210         context->state = EXPORT_KEY_READ_PUB_KEY_PARENT;
211     }
212     LOG_TRACE("finished");
213     return r;
214 
215 error_cleanup:
216     /* Cleanup duplicated input parameters that were copied before. */
217     SAFE_FREE(command->pathOfKeyToDuplicate);
218     SAFE_FREE(command->pathToPublicKeyOfNewParent);
219     return r;
220 }
221 
222 /** Asynchronous finish function for Fapi_ExportKey
223  *
224  * This function should be called after a previous Fapi_ExportKey_Async.
225  *
226  * @param[in,out] context The FAPI_CONTEXT
227  * @param[out] exportedData The exported subtree
228  *
229  * @retval TSS2_RC_SUCCESS: if the function call was a success.
230  * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or exportedData is NULL.
231  * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
232  * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
233  *         operation already pending.
234  * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
235  * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
236  *         internal operations or return parameters.
237  * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
238  *         complete. Call this function again later.
239  * @retval TSS2_FAPI_RC_BAD_PATH if the used path in inappropriate-
240  * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
241  *         the function.
242  * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
243  * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
244  *         during authorization.
245  * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
246  * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
247  *         is not set.
248  * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
249  * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
250  *         was not successful.
251  * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
252  *         config file.
253  * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
254  */
255 TSS2_RC
Fapi_ExportKey_Finish(FAPI_CONTEXT * context,char ** exportedData)256 Fapi_ExportKey_Finish(
257     FAPI_CONTEXT *context,
258     char        **exportedData)
259 {
260     LOG_TRACE("called for context:%p", context);
261 
262     TSS2_RC r;
263     json_object *jsoOut = NULL;
264     int sizePem;
265     TPM2B_ENCRYPTED_SECRET *encryptedSeed = NULL;
266     TPM2B_PRIVATE *duplicate = NULL;
267     IFAPI_OBJECT commandObject = {0};
268     IFAPI_OBJECT parentKeyObject = {0};
269     ESYS_TR auth_session;
270 
271     /* Check for NULL parameters */
272     check_not_null(context);
273     check_not_null(exportedData);
274 
275     /* Helpful alias pointers */
276     IFAPI_ExportKey * command = &context->cmd.ExportKey;
277     IFAPI_OBJECT *pubKey = &command->pub_key;
278     IFAPI_OBJECT *exportTree = &command->export_tree;
279     IFAPI_DUPLICATE * keyTree = &exportTree->misc.key_tree;
280     pubKey->misc.ext_pub_key.certificate = NULL;
281 
282     switch (context->state) {
283         statecase(context->state, EXPORT_KEY_READ_PUB_KEY);
284             /* This is the entry point if only the public key shall be exported
285                because no new parent key for encrypting the private portion was
286                provided by the caller. */
287             r = ifapi_keystore_load_finish(&context->keystore, &context->io,
288                                            &commandObject);
289             return_try_again(r);
290             return_if_error_reset_state(r, "read_finish failed");
291 
292             if (commandObject.objectType != IFAPI_KEY_OBJ) {
293                 /* No key object was loaded */
294                 ifapi_cleanup_ifapi_object(&commandObject);
295                 goto_error(r, TSS2_FAPI_RC_BAD_PATH, "%s is not a key object.",
296                            cleanup, command->pathOfKeyToDuplicate);
297             }
298 
299             pubKey->objectType = IFAPI_EXT_PUB_KEY_OBJ;
300             pubKey->misc.ext_pub_key.public = commandObject.misc.key.public;
301 
302             /* Convert the TPM key format to PEM. */
303             r = ifapi_pub_pem_key_from_tpm(&pubKey->misc.ext_pub_key.public,
304                                            &pubKey->misc.ext_pub_key.pem_ext_public,
305                                            &sizePem);
306             goto_if_error(r, "Convert public TPM key to pem.", cleanup);
307 
308             r = ifapi_json_IFAPI_OBJECT_serialize(pubKey, &jsoOut);
309             goto_if_error(r, "Error serialize FAPI KEY object", cleanup);
310 
311             *exportedData = strdup(json_object_to_json_string_ext(jsoOut,
312                                                                   JSON_C_TO_STRING_PRETTY));
313             goto_if_null2(*exportedData, "Converting json to string", r,
314                           TSS2_FAPI_RC_MEMORY, cleanup);
315 
316             break;
317 
318         statecase(context->state, EXPORT_KEY_READ_PUB_KEY_PARENT);
319             /* This is the entry point if a new parent key was provided and
320                the private portion shall be re-encrypted. */
321             r = ifapi_keystore_load_finish(&context->keystore, &context->io,
322                     &parentKeyObject);
323             if (r != TSS2_RC_SUCCESS) {
324                 ifapi_cleanup_ifapi_object(&parentKeyObject);
325             }
326             return_try_again(r);
327             return_if_error_reset_state(r, "read_finish failed");
328 
329             if (parentKeyObject.objectType != IFAPI_EXT_PUB_KEY_OBJ) {
330                 goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "No public key in %s",
331                            cleanup, command->pathToPublicKeyOfNewParent);
332             }
333 
334             /* Store the public information of the new parent in the context
335                and cleanup all other metadata for this key. */
336             command->public_parent = parentKeyObject.misc.ext_pub_key.public;
337             ifapi_cleanup_ifapi_object(&parentKeyObject);
338 
339             /* Initialize a session used for authorization and parameter encryption. */
340             r = ifapi_get_sessions_async(context,
341                                          IFAPI_SESSION_GENEK | IFAPI_SESSION1,
342                                          TPMA_SESSION_DECRYPT, 0);
343             goto_if_error_reset_state(r, "Create sessions", cleanup);
344 
345             fallthrough;
346 
347         statecase(context->state, EXPORT_KEY_WAIT_FOR_KEY);
348             /* Load the key to be duplicated. */
349             r = ifapi_load_key(context, command->pathOfKeyToDuplicate,
350                                &command->key_object);
351             return_try_again(r);
352             goto_if_error(r, "Fapi load key.", cleanup);
353 
354             context->duplicate_key = command->key_object;
355 
356             /* Load the new parent key. */
357             r = Esys_LoadExternal_Async(context->esys,
358                                         ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE,
359                                         NULL,   &command->public_parent,
360                                         TPM2_RH_OWNER);
361             goto_if_error(r, "LoadExternal_Async", cleanup);
362 
363             fallthrough;
364 
365         statecase(context->state, EXPORT_KEY_WAIT_FOR_EXT_KEY);
366             r = Esys_LoadExternal_Finish(context->esys,
367                                          &command->handle_ext_key);
368             try_again_or_error_goto(r, "Load external key.", cleanup);
369 
370             fallthrough;
371 
372         statecase(context->state, EXPORT_KEY_WAIT_FOR_AUTHORIZATON);
373             /* Authorize against the key to be exported. */
374             r = ifapi_authorize_object(context, command->key_object, &auth_session);
375             return_try_again(r);
376             goto_if_error(r, "Authorize key.", cleanup);
377 
378             TPM2B_DATA encryptionKey;
379             TPMT_SYM_DEF_OBJECT symmetric;
380 
381             symmetric.algorithm = TPM2_ALG_NULL;
382             encryptionKey.size = 0;
383 
384             /* Duplicate the key; i.e. re-encrypt the private key with
385                the public key of the new parent. */
386             r = Esys_Duplicate_Async(context->esys,
387                                      command->key_object->handle,
388                                      command->handle_ext_key,
389                                      auth_session,
390                                      ESYS_TR_NONE, ESYS_TR_NONE,
391                                      &encryptionKey, &symmetric);
392             goto_if_error(r, "Duplicate", cleanup);
393 
394             fallthrough;
395 
396         statecase(context->state, EXPORT_KEY_WAIT_FOR_DUPLICATE);
397             exportTree->objectType = IFAPI_DUPLICATE_OBJ;
398             r = Esys_Duplicate_Finish(context->esys, NULL, &duplicate, &encryptedSeed);
399             try_again_or_error_goto(r, "Duplicate", cleanup);
400 
401             /* Store and JSON encode the data to be returned. */
402             /* Note: keyTree = &exportTree->misc.key_tree */
403             keyTree->encrypted_seed = *encryptedSeed;
404             SAFE_FREE(encryptedSeed);
405             keyTree->duplicate = *duplicate;
406             SAFE_FREE(duplicate);
407             keyTree->public =
408                 command->key_object->misc.key.public;
409             keyTree->public_parent = command->public_parent;
410 
411             /* For the policy added no cleanup is needed. The cleanup will
412                be done with the object cleanup. */
413             keyTree->policy = command->key_object->policy;
414             r = ifapi_get_json(context, exportTree, exportedData);
415             goto_if_error2(r, "get JSON for exported data.", cleanup);
416 
417             fallthrough;
418 
419         statecase(context->state, EXPORT_KEY_WAIT_FOR_FLUSH1);
420             /* Flush the key to be exported from the TPM. */
421             r = ifapi_flush_object(context, command->key_object->handle);
422             return_try_again(r);
423             goto_if_error(r, "Flush key", cleanup);
424 
425             fallthrough;
426 
427         statecase(context->state, EXPORT_KEY_WAIT_FOR_FLUSH2);
428             /* Flush the new parent key from the TPM. */
429             r = ifapi_flush_object(context, command->handle_ext_key);
430             return_try_again(r);
431             goto_if_error(r, "Flush key", cleanup);
432 
433             fallthrough;
434 
435         statecase(context->state, EXPORT_KEY_CLEANUP)
436             /* Cleanup the sessions used for authorization. */
437             r = ifapi_cleanup_session(context);
438             try_again_or_error_goto(r, "Cleanup", cleanup);
439 
440             break;
441 
442         statecasedefault(context->state);
443     }
444 
445 cleanup:
446     /* Cleanup any intermediate results and state stored in the context. */
447     if (command->key_object) {
448         ifapi_cleanup_ifapi_object(command->key_object);
449     }
450     if (jsoOut != NULL) {
451         json_object_put(jsoOut);
452     }
453     context->duplicate_key = NULL;
454     context->state = _FAPI_STATE_INIT;
455     ifapi_cleanup_ifapi_object(&parentKeyObject);
456     ifapi_cleanup_ifapi_object(&commandObject);
457     ifapi_session_clean(context);
458     ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
459     ifapi_cleanup_ifapi_object(context->loadKey.key_object);
460     ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
461     SAFE_FREE(pubKey->misc.ext_pub_key.pem_ext_public);
462     SAFE_FREE(pubKey->misc.ext_pub_key.certificate);
463     SAFE_FREE(command->pathOfKeyToDuplicate);
464     SAFE_FREE(command->pathToPublicKeyOfNewParent);
465     LOG_TRACE("finished");
466     return r;
467 }
468