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