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 "ifapi_json_serialize.h"
22 #include "fapi_policy.h"
23 #define LOGMODULE fapi
24 #include "util/log.h"
25 #include "util/aux_util.h"
26 #include "fapi_crypto.h"
27
28 #define IV_SIZE 16
29
30 /** One-Call function for Fapi_Encrypt
31 *
32 * Encrypt the provided data for the target key using the TPM encryption
33 * schemes as specified in the crypto profile.
34 * This function does not use the TPM; i.e. works in non-TPM mode.
35 *
36 * @param[in,out] context The FAPI_CONTEXT
37 * @param[in] keyPath THe path to the encryption key
38 * @param[in] plainText The plaintext data to encrypt
39 * @param[in] plainTextSize The size of the plainText in bytes
40 * @param[out] cipherText The encoded cipher text.
41 * @param[out] cipherTextSize The size of the encoded cipher text.
42 *
43 * @retval TSS2_RC_SUCCESS: if the function call was a success.
44 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, keyPath, plainText, or
45 * cipherText is NULL.
46 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
47 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if keyPath does not map to a FAPI key.
48 * @retval TSS2_FAPI_RC_BAD_KEY: if the key at keyPath is unsuitable for
49 * encryption.
50 * @retval TSS2_FAPI_RC_BAD_VALUE: if plainTextSize is 0.
51 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
52 * operation already pending.
53 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
54 * internal operations or return parameters.
55 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
56 * config file.
57 * @retval TSS2_FAPI_RC_IO_ERROR if an error occurred while accessing the
58 * object store.
59 * @retval TSS2_FAPI_RC_NOT_IMPLEMENTED if the encryption algorithm is not available.
60 * @retval TSS2_FAPI_RC_TRY_AGAIN if an I/O operation is not finished yet and
61 * this function needs to be called again.
62 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
63 * during authorization.
64 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
65 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
66 * is not set.
67 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
68 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
69 * was not successful.
70 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
71 */
72 TSS2_RC
Fapi_Encrypt(FAPI_CONTEXT * context,char const * keyPath,uint8_t const * plainText,size_t plainTextSize,uint8_t ** cipherText,size_t * cipherTextSize)73 Fapi_Encrypt(
74 FAPI_CONTEXT *context,
75 char const *keyPath,
76 uint8_t const *plainText,
77 size_t plainTextSize,
78 uint8_t **cipherText,
79 size_t *cipherTextSize)
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(keyPath);
88 check_not_null(plainText);
89 check_not_null(cipherText);
90
91 /* Check whether TCTI and ESYS are initialized */
92 return_if_null(context->esys, "Command can't be executed in none TPM mode.",
93 TSS2_FAPI_RC_NO_TPM);
94
95 /* If the async state automata of FAPI shall be tested, then we must not set
96 the timeouts of ESYS to blocking mode.
97 During testing, the mssim tcti will ensure multiple re-invocations.
98 Usually however the synchronous invocations of FAPI shall instruct ESYS
99 to block until a result is available. */
100 #ifndef TEST_FAPI_ASYNC
101 r = Esys_SetTimeout(context->esys, TSS2_TCTI_TIMEOUT_BLOCK);
102 return_if_error_reset_state(r, "Set Timeout to blocking");
103 #endif /* TEST_FAPI_ASYNC */
104
105 r = Fapi_Encrypt_Async(context, keyPath, plainText, plainTextSize);
106 return_if_error_reset_state(r, "Data_Encrypt");
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_Encrypt_Finish(context, cipherText, cipherTextSize);
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, "Data_Encrypt");
124
125 LOG_TRACE("finished");
126 return TSS2_RC_SUCCESS;
127 }
128
129 /** Asynchronous function for Fapi_Encrypt
130 *
131 * Encrypt the provided data for the target key using the TPM encryption
132 * schemes as specified in the crypto profile.
133 * This function does not use the TPM; i.e. works in non-TPM mode.
134 *
135 * Call Fapi_Encrypt_Finish to finish the execution of this command.
136 *
137 * @param[in,out] context The FAPI_CONTEXT
138 * @param[in] keyPath The path to the encryption key
139 * @param[in] plainText The plainText data to encrypt
140 * @param[in] plainTextSize The size of the plainText in bytes
141 *
142 * @retval TSS2_RC_SUCCESS: if the function call was a success.
143 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context, keyPath or plainText is
144 * NULL.
145 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
146 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND: if keyPath does not map to a FAPI key.
147 * @retval TSS2_FAPI_RC_BAD_KEY: if the key at keyPath is unsuitable for
148 * encryption.
149 * @retval TSS2_FAPI_RC_BAD_VALUE: if plainTextSize is 0.
150 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
151 * operation already pending.
152 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
153 * internal operations or return parameters.
154 * @retval TSS2_FAPI_RC_NO_TPM if FAPI was initialized in no-TPM-mode via its
155 * config file.
156 */
157 TSS2_RC
Fapi_Encrypt_Async(FAPI_CONTEXT * context,char const * keyPath,uint8_t const * plainText,size_t plainTextSize)158 Fapi_Encrypt_Async(
159 FAPI_CONTEXT *context,
160 char const *keyPath,
161 uint8_t const *plainText,
162 size_t plainTextSize)
163 {
164 LOG_TRACE("called for context:%p", context);
165 LOG_TRACE("keyPath: %s", keyPath);
166 if (plainText) {
167 LOGBLOB_TRACE(plainText, plainTextSize, "plainText");
168 } else {
169 LOG_TRACE("plainText: (null) plainTextSize: %zi", plainTextSize);
170 }
171
172 TSS2_RC r;
173
174 /* Check for NULL parameters */
175 check_not_null(context);
176 check_not_null(keyPath);
177 check_not_null(plainText);
178
179 /* Helpful alias pointers */
180 IFAPI_Data_EncryptDecrypt * command = &(context->cmd.Data_EncryptDecrypt);
181
182 r = ifapi_session_init(context);
183 return_if_error(r, "Initialize Encrypt");
184
185 /* Copy parameters to context for use during _Finish. */
186 uint8_t *inData = malloc(plainTextSize);
187 goto_if_null(inData, "Out of memory", r, error_cleanup);
188 memcpy(inData, plainText, plainTextSize);
189 command->in_data = inData;
190
191 strdup_check(command->keyPath, keyPath, r, error_cleanup);
192
193 command->in_dataSize = plainTextSize;
194 command->key_handle = ESYS_TR_NONE;
195
196 /* Initialize the context state for this operation. */
197 context->state = DATA_ENCRYPT_WAIT_FOR_PROFILE;
198 LOG_TRACE("finished");
199 return TSS2_RC_SUCCESS;
200
201 error_cleanup:
202 SAFE_FREE(inData);
203 SAFE_FREE(command->keyPath);
204 return r;
205 }
206
207 /** Asynchronous finish function for Fapi_Encrypt
208 *
209 * This function should be called after a previous Fapi_Encrypt_Async.
210 *
211 * @param[in,out] context The FAPI_CONTEXT
212 * @param[out] cipherText The encoded ciphertext
213 * @param[out] cipherTextSize The size of the encoded cipher text.
214 *
215 * @retval TSS2_RC_SUCCESS: if the function call was a success.
216 * @retval TSS2_FAPI_RC_BAD_REFERENCE: if context or ciphertext is NULL.
217 * @retval TSS2_FAPI_RC_BAD_CONTEXT: if context corruption is detected.
218 * @retval TSS2_FAPI_RC_BAD_SEQUENCE: if the context has an asynchronous
219 * operation already pending.
220 * @retval TSS2_FAPI_RC_IO_ERROR: if the data cannot be saved.
221 * @retval TSS2_FAPI_RC_MEMORY: if the FAPI cannot allocate enough memory for
222 * internal operations or return parameters.
223 * @retval TSS2_FAPI_RC_TRY_AGAIN: if the asynchronous operation is not yet
224 * complete. Call this function again later.
225 * @retval TSS2_FAPI_RC_NOT_IMPLEMENTED if the encryption algorithm is not available.
226 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
227 * the function.
228 * @retval TSS2_FAPI_RC_PATH_NOT_FOUND if a FAPI object path was not found
229 * during authorization.
230 * @retval TSS2_FAPI_RC_KEY_NOT_FOUND if a key was not found.
231 * @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
232 * @retval TSS2_FAPI_RC_AUTHORIZATION_UNKNOWN if a required authorization callback
233 * is not set.
234 * @retval TSS2_FAPI_RC_AUTHORIZATION_FAILED if the authorization attempt fails.
235 * @retval TSS2_FAPI_RC_POLICY_UNKNOWN if policy search for a certain policy digest
236 * was not successful.
237 * @retval TSS2_ESYS_RC_* possible error codes of ESAPI.
238 */
239 TSS2_RC
Fapi_Encrypt_Finish(FAPI_CONTEXT * context,uint8_t ** cipherText,size_t * cipherTextSize)240 Fapi_Encrypt_Finish(
241 FAPI_CONTEXT *context,
242 uint8_t **cipherText,
243 size_t *cipherTextSize)
244 {
245 LOG_TRACE("called for context:%p", context);
246
247 TSS2_RC r;
248
249 /* Check for NULL parameters */
250 check_not_null(context);
251 check_not_null(cipherText);
252
253 /* Helpful alias pointers */
254 IFAPI_Data_EncryptDecrypt * command = &context->cmd.Data_EncryptDecrypt;
255 IFAPI_OBJECT *encKeyObject;
256 TPM2B_PUBLIC_KEY_RSA *tpmCipherText = NULL;
257
258 switch (context->state) {
259 statecase(context->state, DATA_ENCRYPT_WAIT_FOR_PROFILE);
260 /* Retrieve the profile for the provided key in order to get the
261 encryption scheme below. */
262 r = ifapi_profiles_get(&context->profiles, command->keyPath,
263 &command->profile);
264 return_try_again(r);
265 goto_if_error_reset_state(r, " FAPI create session", error_cleanup);
266
267 /* Initialize a session used for authorization and parameter encryption. */
268 r = ifapi_get_sessions_async(context,
269 IFAPI_SESSION_GENEK | IFAPI_SESSION1,
270 TPMA_SESSION_ENCRYPT | TPMA_SESSION_DECRYPT, 0);
271 goto_if_error_reset_state(r, "Create sessions", error_cleanup);
272
273 fallthrough;
274
275 statecase(context->state, DATA_ENCRYPT_WAIT_FOR_SESSION);
276 r = ifapi_get_sessions_finish(context, &context->profiles.default_profile,
277 context->profiles.default_profile.nameAlg);
278 return_try_again(r);
279 goto_if_error(r, "Get session.", error_cleanup);
280
281 /* Load the reference key by loading all of its parents starting from the SRK. */
282 r = ifapi_load_keys_async(context, command->keyPath);
283 goto_if_error(r, "Load keys.", error_cleanup);
284
285 fallthrough;
286
287 statecase(context->state, DATA_ENCRYPT_WAIT_FOR_KEY);
288 r = ifapi_load_keys_finish(context, IFAPI_FLUSH_PARENT,
289 &command->key_handle,
290 &command->key_object);
291 return_try_again(r);
292 goto_if_error_reset_state(r, " Load key.", error_cleanup);
293
294 encKeyObject = command->key_object;
295
296 if (encKeyObject->misc.key.public.publicArea.type == TPM2_ALG_RSA) {
297 TPM2B_DATA null_data = { .size = 0, .buffer = {} };
298 TPM2B_PUBLIC_KEY_RSA *rsa_message = (TPM2B_PUBLIC_KEY_RSA *)&context->aux_data;
299 rsa_message->size = context->cmd.Data_EncryptDecrypt.in_dataSize;
300 size_t key_size =
301 encKeyObject->misc.key.public.publicArea.parameters.rsaDetail.keyBits / 8;
302 if (rsa_message->size <= key_size) {
303 memcpy(&rsa_message->buffer[0], context->cmd.Data_EncryptDecrypt.in_data,
304 context->cmd.Data_EncryptDecrypt.in_dataSize);
305
306 /* Received plain text will be encrypted */
307 r = Esys_TRSess_SetAttributes(context->esys, context->session1,
308 TPMA_SESSION_CONTINUESESSION | TPMA_SESSION_DECRYPT,
309 0xff);
310 goto_if_error_reset_state(r, "Set session attributes.", error_cleanup);
311
312 r = Esys_RSA_Encrypt_Async(context->esys,
313 context->cmd.Data_EncryptDecrypt.key_handle,
314 context->session1, ESYS_TR_NONE, ESYS_TR_NONE,
315 rsa_message,
316 &command->profile->rsa_decrypt_scheme,
317 &null_data);
318 goto_if_error(r, "Error esys rsa encrypt", error_cleanup);
319
320 context-> state = DATA_ENCRYPT_WAIT_FOR_RSA_ENCRYPTION;
321 } else {
322 goto_error_reset_state(r, TSS2_FAPI_RC_NOT_IMPLEMENTED,
323 "Size to big for RSA encryption.", error_cleanup);
324 }
325 } else {
326 goto_error(r, TSS2_FAPI_RC_NOT_IMPLEMENTED,
327 "Unsupported algorithm (%" PRIu16 ")",
328 error_cleanup, encKeyObject->misc.key.public.publicArea.type);
329 }
330 fallthrough;
331
332 statecase(context->state, DATA_ENCRYPT_WAIT_FOR_RSA_ENCRYPTION);
333 r = Esys_RSA_Encrypt_Finish(context->esys, &tpmCipherText);
334 return_try_again(r);
335 goto_if_error_reset_state(r, "RSA encryption.", error_cleanup);
336
337 /* Return cipherTextSize if requested by the caller. */
338 if (cipherTextSize)
339 *cipherTextSize = tpmCipherText->size;
340
341 /* Duplicate the outputs for handling off to the caller. */
342 *cipherText = malloc(tpmCipherText->size);
343 goto_if_null2(*cipherText, "Out of memory", r, TSS2_FAPI_RC_MEMORY,
344 error_cleanup);
345
346 memcpy(*cipherText, &tpmCipherText->buffer[0], tpmCipherText->size);
347 SAFE_FREE(tpmCipherText);
348
349 /* Flush the key from the TPM. */
350 r = Esys_FlushContext_Async(context->esys,
351 command->key_handle);
352 goto_if_error(r, "Error: FlushContext", error_cleanup);
353 fallthrough;
354
355 statecase(context->state, DATA_ENCRYPT_WAIT_FOR_FLUSH);
356 r = Esys_FlushContext_Finish(context->esys);
357 return_try_again(r);
358
359 goto_if_error(r, "Error: FlushContext", error_cleanup);
360 command->key_handle = ESYS_TR_NONE;
361 fallthrough;
362
363 statecase(context->state, DATA_ENCRYPT_CLEAN)
364 /* Cleanup the sessions. */
365 r = ifapi_cleanup_session(context);
366 try_again_or_error_goto(r, "Cleanup", error_cleanup);
367
368 break;
369
370 statecasedefault(context->state);
371 }
372
373 context->state = _FAPI_STATE_INIT;
374
375 error_cleanup:
376 /* Cleanup any intermediate results and state stored in the context. */
377 if (command->key_handle != ESYS_TR_NONE)
378 Esys_FlushContext(context->esys, command->key_handle);
379 ifapi_cleanup_ifapi_object(&context->loadKey.auth_object);
380 ifapi_cleanup_ifapi_object(context->loadKey.key_object);
381 ifapi_cleanup_ifapi_object(&context->createPrimary.pkey_object);
382 ifapi_cleanup_ifapi_object(command->key_object);
383 SAFE_FREE(tpmCipherText);
384 SAFE_FREE(command->keyPath);
385 SAFE_FREE(command->in_data);
386 SAFE_FREE(command->out_data);
387 ifapi_session_clean(context);
388 LOG_TRACE("finished");
389 return r;
390 }
391