• 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 "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