blob: aa4bdb5dbdebadfe0eeb111b41d317131ebef35c [file] [log] [blame]
/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
* Copyright: 2020, Andreas Dröscher
* All rights reserved.
******************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mbedtls/md.h>
#include <mbedtls/rsa.h>
#include <mbedtls/ecdh.h>
#include <mbedtls/aes.h>
#include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h>
#include "esys_iutil.h"
#include "esys_mu.h"
#define LOGMODULE esys_crypto
#include "util/log.h"
#include "util/aux_util.h"
/** Context to hold temporary values for iesys_crypto */
typedef struct _IESYS_CRYPTO_CONTEXT {
enum {
IESYS_CRYPTMBED_TYPE_HASH = 1,
IESYS_CRYPTMBED_TYPE_HMAC,
} type; /**< The type of context to hold; hash or hmac */
union {
struct {
mbedtls_md_context_t mbed_context;
size_t hash_len;
} hash; /**< the state variables for a hash context */
struct {
mbedtls_md_context_t mbed_context;
size_t hmac_len;
} hmac; /**< the state variables for an hmac context */
};
} IESYS_CRYPTMBED_CONTEXT;
/** Provide the context for the computation of a hash digest.
*
* The context will be created and initialized according to the hash function.
* @param[out] context The created context (callee-allocated).
* @param[in] hashAlg The hash algorithm for the creation of the context.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_VALUE or TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_ESYS_RC_MEMORY Memory cannot be allocated.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_hash_start(IESYS_CRYPTO_CONTEXT_BLOB ** context,
TPM2_ALG_ID hashAlg)
{
TSS2_RC r = TSS2_RC_SUCCESS;
const mbedtls_md_info_t* md_info = NULL;
if (context == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE,
"Null-Pointer passed in for context");
}
IESYS_CRYPTMBED_CONTEXT *mycontext = calloc(1, sizeof(IESYS_CRYPTMBED_CONTEXT));
return_if_null(mycontext, "Out of Memory", TSS2_ESYS_RC_MEMORY);
mbedtls_md_init(&mycontext->hash.mbed_context);
switch(hashAlg) {
case TPM2_ALG_SHA1:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
break;
case TPM2_ALG_SHA256:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
break;
case TPM2_ALG_SHA384:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
break;
}
if (md_info == NULL) {
goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED,
"Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg);
}
mycontext->hash.hash_len = mbedtls_md_get_size(md_info);
if (mbedtls_md_setup(&mycontext->hash.mbed_context, md_info, true) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"MBED HASH setup", cleanup);
}
if (mbedtls_md_starts(&mycontext->hash.mbed_context) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"MBED HASH start", cleanup);
}
mycontext->type = IESYS_CRYPTMBED_TYPE_HASH;
*context = (IESYS_CRYPTO_CONTEXT_BLOB *) mycontext;
return TSS2_RC_SUCCESS;
cleanup:
mbedtls_md_free(&mycontext->hash.mbed_context);
SAFE_FREE(mycontext);
return r;
}
/** Update the digest value of a digest object from a byte buffer.
*
* The context of a digest object will be updated according to the hash
* algorithm of the context. <
* @param[in,out] context The context of the digest object which will be updated.
* @param[in] buffer The data for the update.
* @param[in] size The size of the data buffer.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
*/
TSS2_RC
iesys_cryptmbed_hash_update(IESYS_CRYPTO_CONTEXT_BLOB * context,
const uint8_t * buffer, size_t size)
{
if (context == NULL || buffer == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
IESYS_CRYPTMBED_CONTEXT *mycontext = (IESYS_CRYPTMBED_CONTEXT *) context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HASH) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "bad context");
}
if (mbedtls_md_update(&mycontext->hash.mbed_context, buffer, size) != 0) {
return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "MBED HASH update");
}
return TSS2_RC_SUCCESS;
}
/** Update the digest value of a digest object from a TPM2B object.
*
* The context of a digest object will be updated according to the hash
* algorithm of the context.
* @param[in,out] context The context of the digest object which will be updated.
* @param[in] b The TPM2B object for the update.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
*/
TSS2_RC
iesys_cryptmbed_hash_update2b(IESYS_CRYPTO_CONTEXT_BLOB * context, TPM2B * b)
{
if (context == NULL || b == NULL) {
return TSS2_ESYS_RC_BAD_REFERENCE;
}
TSS2_RC ret = iesys_cryptmbed_hash_update(context, &b->buffer[0], b->size);
return ret;
}
/** Get the digest value of a digest object and close the context.
*
* The digest value will written to a passed buffer and the resources of the
* digest object are released.
* @param[in,out] context The context of the digest object to be released
* @param[out] buffer The buffer for the digest value (caller-allocated).
* @param[out] size The size of the digest.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_hash_finish(IESYS_CRYPTO_CONTEXT_BLOB ** context,
uint8_t * buffer, size_t * size)
{
TSS2_RC r = TSS2_RC_SUCCESS;
if (context == NULL || *context == NULL || buffer == NULL || size == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
IESYS_CRYPTMBED_CONTEXT *mycontext =
(IESYS_CRYPTMBED_CONTEXT *) * context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HASH) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "bad context");
}
if (*size < mycontext->hash.hash_len) {
return_error(TSS2_ESYS_RC_BAD_SIZE, "Buffer too small");
}
if (mbedtls_md_finish(&mycontext->hmac.mbed_context, buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "MBED HASH finish", cleanup);
}
*size = mycontext->hash.hash_len;
cleanup:
mbedtls_md_free(&mycontext->hash.mbed_context);
SAFE_FREE(mycontext);
*context = NULL;
return r;
}
/** Release the resources of a digest object.
*
* The assigned resources will be released and the context will be set to NULL.
* @param[in,out] context The context of the digest object.
*/
void
iesys_cryptmbed_hash_abort(IESYS_CRYPTO_CONTEXT_BLOB ** context)
{
if (context == NULL || *context == NULL) {
return;
}
IESYS_CRYPTMBED_CONTEXT *mycontext =
(IESYS_CRYPTMBED_CONTEXT *) * context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HASH) {
return;
}
mbedtls_md_free(&mycontext->hash.mbed_context);
free(mycontext);
*context = NULL;
}
/* HMAC */
/** Provide the context an HMAC digest object from a byte buffer key.
*
* The context will be created and initialized according to the hash function
* and the used HMAC key.
* @param[out] context The created context (callee-allocated).
* @param[in] hmacAlg The hash algorithm for the HMAC computation.
* @param[in] key The byte buffer of the HMAC key.
* @param[in] size The size of the HMAC key.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_ESYS_RC_MEMORY Memory cannot be allocated.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_hmac_start(IESYS_CRYPTO_CONTEXT_BLOB ** context,
TPM2_ALG_ID hashAlg,
const uint8_t * key, size_t size)
{
TSS2_RC r = TSS2_RC_SUCCESS;
const mbedtls_md_info_t* md_info = NULL;
if (context == NULL || key == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE,
"Null-Pointer passed in for context");
}
IESYS_CRYPTMBED_CONTEXT *mycontext = calloc(1, sizeof(IESYS_CRYPTMBED_CONTEXT));
return_if_null(mycontext, "Out of Memory", TSS2_ESYS_RC_MEMORY);
mbedtls_md_init(&mycontext->hash.mbed_context);
switch(hashAlg) {
case TPM2_ALG_SHA1:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA1);
break;
case TPM2_ALG_SHA256:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256);
break;
case TPM2_ALG_SHA384:
md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384);
break;
}
if (md_info == NULL) {
goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED,
"Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg);
}
mycontext->hmac.hmac_len = mbedtls_md_get_size(md_info);
if (mbedtls_md_setup(&mycontext->hash.mbed_context, md_info, true) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"MBED HMAC setup", cleanup);
}
if (mbedtls_md_hmac_starts(&mycontext->hash.mbed_context, key, size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"MBED HMAC start", cleanup);
}
mycontext->type = IESYS_CRYPTMBED_TYPE_HMAC;
*context = (IESYS_CRYPTO_CONTEXT_BLOB *) mycontext;
return TSS2_RC_SUCCESS;
cleanup:
mbedtls_md_free(&mycontext->hmac.mbed_context);
SAFE_FREE(mycontext);
return r;
}
/** Update and HMAC digest value from a byte buffer.
*
* The context of a digest object will be updated according to the hash
* algorithm and the key of the context.
* @param[in,out] context The context of the digest object which will be updated.
* @param[in] buffer The data for the update.
* @param[in] size The size of the data buffer.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
*/
TSS2_RC
iesys_cryptmbed_hmac_update(IESYS_CRYPTO_CONTEXT_BLOB * context,
const uint8_t * buffer, size_t size)
{
if (context == NULL || buffer == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
IESYS_CRYPTMBED_CONTEXT *mycontext = (IESYS_CRYPTMBED_CONTEXT *) context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HMAC) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "bad context");
}
if (mbedtls_md_hmac_update(&mycontext->hmac.mbed_context, buffer, size) != 0) {
return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "MBED HMAC update");
}
return TSS2_RC_SUCCESS;
}
/** Update and HMAC digest value from a TPM2B object.
*
* The context of a digest object will be updated according to the hash
* algorithm and the key of the context.
* @param[in,out] context The context of the digest object which will be updated.
* @param[in] b The TPM2B object for the update.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
*/
TSS2_RC
iesys_cryptmbed_hmac_update2b(IESYS_CRYPTO_CONTEXT_BLOB * context, TPM2B * b)
{
if (context == NULL || b == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
TSS2_RC ret = iesys_cryptmbed_hmac_update(context, &b->buffer[0], b->size);
return ret;
}
/** Write the HMAC digest value to a byte buffer and close the context.
*
* The digest value will written to a passed buffer and the resources of the
* HMAC object are released.
* @param[in,out] context The context of the HMAC object.
* @param[out] buffer The buffer for the digest value (caller-allocated).
* @param[out] size The size of the digest.
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_ESYS_RC_BAD_SIZE If the size passed is lower than the HMAC length.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_hmac_finish(IESYS_CRYPTO_CONTEXT_BLOB ** context,
uint8_t * buffer, size_t * size)
{
TSS2_RC r = TSS2_RC_SUCCESS;
if (context == NULL || *context == NULL || buffer == NULL || size == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
IESYS_CRYPTMBED_CONTEXT *mycontext =
(IESYS_CRYPTMBED_CONTEXT *) * context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HMAC) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "bad context");
}
if (*size < mycontext->hmac.hmac_len) {
return_error(TSS2_ESYS_RC_BAD_SIZE, "Buffer too small");
}
if (mbedtls_md_hmac_finish(&mycontext->hmac.mbed_context, buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "MBED HMAC finish", cleanup);
}
*size = mycontext->hmac.hmac_len;
cleanup:
mbedtls_md_free(&mycontext->hmac.mbed_context);
SAFE_FREE(mycontext);
*context = NULL;
return r;
}
/** Write the HMAC digest value to a TPM2B object and close the context.
*
* The digest value will written to a passed TPM2B object and the resources of
* the HMAC object are released.
* @param[in,out] context The context of the HMAC object.
* @param[out] hmac The buffer for the digest value (caller-allocated).
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_ESYS_RC_BAD_SIZE if the size passed is lower than the HMAC length.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_hmac_finish2b(IESYS_CRYPTO_CONTEXT_BLOB ** context, TPM2B * hmac)
{
if (context == NULL || *context == NULL || hmac == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed");
}
size_t s = hmac->size;
TSS2_RC ret = iesys_cryptmbed_hmac_finish(context, &hmac->buffer[0], &s);
hmac->size = s;
return ret;
}
/** Release the resources of an HAMC object.
*
* The assigned resources will be released and the context will be set to NULL.
* @param[in,out] context The context of the HMAC object.
*/
void
iesys_cryptmbed_hmac_abort(IESYS_CRYPTO_CONTEXT_BLOB ** context)
{
if (context == NULL || *context == NULL) {
return;
}
IESYS_CRYPTMBED_CONTEXT *mycontext =
(IESYS_CRYPTMBED_CONTEXT *) * context;
if (mycontext->type != IESYS_CRYPTMBED_TYPE_HMAC) {
return;
}
mbedtls_md_free(&mycontext->hmac.mbed_context);
free(mycontext);
*context = NULL;
}
/** Wrapper for mbedtls random number generator
*
* @param[in] context Optional unused parameter.
* @param[out] buffer Buffer to write randomness to.
* @param[om] buf_size Number of bytes to write to buffer.
*/
static int get_random(void* context, unsigned char * buffer, size_t buf_size) {
(void)context;
int r = 0; //success in terms of mbedtls
mbedtls_entropy_context entropy_context;
mbedtls_entropy_init(&entropy_context);
mbedtls_ctr_drbg_context drbg_context;
mbedtls_ctr_drbg_init(&drbg_context);
if (mbedtls_ctr_drbg_seed(&drbg_context,
mbedtls_entropy_func,
&entropy_context,
NULL,
0) != 0) {
goto_error(r, MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED,
"Could not seed random number generator.", cleanup);
}
if (mbedtls_ctr_drbg_random(&drbg_context,
&buffer[0],
buf_size) != 0) {
goto_error(r, MBEDTLS_ERR_CTR_DRBG_REQUEST_TOO_BIG,
"Failure in random number generator.", cleanup);
}
cleanup:
mbedtls_ctr_drbg_free(&drbg_context);
mbedtls_entropy_free(&entropy_context);
return r;
}
/** Compute random TPM2B data.
*
* The random data will be generated and written to a passed TPM2B structure.
* @param[out] nonce The TPM2B structure for the random data (caller-allocated).
* @param[in] num_bytes The number of bytes to be generated.
* @retval TSS2_RC_SUCCESS on success.
*
* NOTE: the TPM should not be used to obtain the random data
*/
TSS2_RC
iesys_cryptmbed_random2b(TPM2B_NONCE * nonce, size_t num_bytes)
{
if (num_bytes == 0) {
nonce->size = sizeof(TPMU_HA);
} else {
nonce->size = num_bytes;
}
if (get_random(NULL, &nonce->buffer[0], nonce->size) != 0) {
return_error(TSS2_ESYS_RC_GENERAL_FAILURE,
"Hash algorithm not supported");
}
return TSS2_RC_SUCCESS;
}
/** Encryption of a buffer using a public (RSA) key.
*
* Encrypting a buffer using a public key is used for example during
* Esys_StartAuthSession in order to encrypt the salt value.
* @param[in] key The key to be used for encryption.
* @param[in] in_size The size of the buffer to be encrypted.
* @param[in] in_buffer The data buffer to be encrypted.
* @param[in] max_out_size The maximum size for the output encrypted buffer.
* @param[out] out_buffer The encrypted buffer.
* @param[out] out_size The size of the encrypted output.
* @param[in] label The label used in the encryption scheme.
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_ESYS_RC_BAD_VALUE The algorithm of key is not implemented.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE The internal crypto engine failed.
*/
TSS2_RC
iesys_cryptmbed_pk_encrypt(TPM2B_PUBLIC * pub_tpm_key,
size_t in_size,
BYTE * in_buffer,
size_t max_out_size,
BYTE * out_buffer,
size_t * out_size, const char *label)
{
TSS2_RC r = TSS2_RC_SUCCESS;
mbedtls_rsa_context rsa_context;
switch(pub_tpm_key->publicArea.nameAlg) {
case TPM2_ALG_SHA1:
mbedtls_rsa_init(&rsa_context, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA1);
break;
case TPM2_ALG_SHA256:
mbedtls_rsa_init(&rsa_context, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
break;
case TPM2_ALG_SHA384:
mbedtls_rsa_init(&rsa_context, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA384);
break;
case TPM2_ALG_SHA512:
mbedtls_rsa_init(&rsa_context, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA512);
break;
default:
return_error(TSS2_ESYS_RC_NOT_IMPLEMENTED,
"Hash algorithm not supported");
}
if (pub_tpm_key->publicArea.unique.rsa.size == 0) {
goto_error(r, TSS2_ESYS_RC_BAD_VALUE,
"Public key size may not be 0", cleanup);
}
UINT8 exp[sizeof(UINT32)] = { 0x00, 0x01, 0x00, 0x01 }; //big-endian 65537
UINT32 exp_as_int = pub_tpm_key->publicArea.parameters.rsaDetail.exponent;
if (exp_as_int != 0) {
exp[0] = (exp_as_int >> 24) & 0xff;
exp[1] = (exp_as_int >> 16) & 0xff;
exp[2] = (exp_as_int >> 8) & 0xff;
exp[3] = (exp_as_int >> 0) & 0xff;
}
if (mbedtls_rsa_import_raw(&rsa_context,
pub_tpm_key->publicArea.unique.rsa.buffer,
pub_tpm_key->publicArea.unique.rsa.size,
NULL /* p */,
0,
NULL /* q */,
0,
NULL /* d */,
0,
&exp[0],
sizeof(UINT32)) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Could not import public key", cleanup);
}
if (mbedtls_rsa_complete(&rsa_context) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Could not complete key import", cleanup);
}
if (mbedtls_rsa_get_len(&rsa_context) > max_out_size) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Encrypted data too big", cleanup);
}
switch (pub_tpm_key->publicArea.parameters.rsaDetail.scheme.scheme) {
case TPM2_ALG_RSAES:
if (mbedtls_rsa_pkcs1_encrypt(&rsa_context,
get_random,
NULL,
MBEDTLS_RSA_PUBLIC,
in_size,
in_buffer,
out_buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Could not encrypt data.", cleanup);
}
break;
case TPM2_ALG_OAEP:
if (mbedtls_rsa_rsaes_oaep_encrypt(&rsa_context,
get_random,
NULL,
MBEDTLS_RSA_PUBLIC,
(const UINT8*)label,
strlen(label) + 1,
in_size,
in_buffer,
out_buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Could not encrypt data.", cleanup);
}
break;
default:
goto_error(r, TSS2_ESYS_RC_BAD_VALUE,
"Illegal RSA scheme", cleanup);
break;
}
*out_size = mbedtls_rsa_get_len(&rsa_context);
r = TSS2_RC_SUCCESS;
cleanup:
mbedtls_rsa_free(&rsa_context);
return r;
}
/** Computation of ephemeral ECC key and shared secret Z.
*
* According to the description in TPM spec part 1 C 6.1 a shared secret
* between application and TPM is computed (ECDH). An ephemeral ECC key and a
* TPM key are used for the ECDH key exchange.
* @param[in] key The key to be used for ECDH key exchange.
* @param[in] max_out_size the max size for the output of the public key of the
* computed ephemeral key.
* @param[out] Z The computed shared secret.
* @param[out] Q The public part of the ephemeral key in TPM format.
* @param[out] out_buffer The public part of the ephemeral key will be marshaled
* to this buffer.
* @param[out] out_size The size of the marshaled output.
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_ESYS_RC_BAD_VALUE The algorithm of key is not implemented.
* @retval TSS2_ESYS_RC_GENERAL_FAILURE The internal crypto engine failed.
*/
TSS2_RC
iesys_cryptmbed_get_ecdh_point(TPM2B_PUBLIC *key,
size_t max_out_size,
TPM2B_ECC_PARAMETER *Z,
TPMS_ECC_POINT *Q,
BYTE * out_buffer,
size_t * out_size)
{
TSS2_RC r = TSS2_RC_SUCCESS;
mbedtls_ecdh_context ecdh_context;
mbedtls_ecdh_init(&ecdh_context);
/* Load named curve */
switch (key->publicArea.parameters.eccDetail.curveID) {
case TPM2_ECC_NIST_P192:
if(mbedtls_ecp_group_load(&ecdh_context.grp, MBEDTLS_ECP_DP_SECP192R1) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECP group load failed", cleanup);
}
break;
case TPM2_ECC_NIST_P224:
if(mbedtls_ecp_group_load(&ecdh_context.grp, MBEDTLS_ECP_DP_SECP224R1) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECP group load failed", cleanup);
}
break;
case TPM2_ECC_NIST_P256:
if(mbedtls_ecp_group_load(&ecdh_context.grp, MBEDTLS_ECP_DP_SECP256R1) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECP group load failed", cleanup);
}
break;
case TPM2_ECC_NIST_P384:
if(mbedtls_ecp_group_load(&ecdh_context.grp, MBEDTLS_ECP_DP_SECP384R1) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECP group load failed", cleanup);
}
break;
case TPM2_ECC_NIST_P521:
if(mbedtls_ecp_group_load(&ecdh_context.grp, MBEDTLS_ECP_DP_SECP521R1) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECP group load failed", cleanup);
}
break;
default:
return_error(TSS2_ESYS_RC_NOT_IMPLEMENTED,
"ECC curve not implemented.");
}
/* Generate ephemeral key */
if (mbedtls_ecdh_gen_public(&ecdh_context.grp,
&ecdh_context.d,
&ecdh_context.Q,
get_random,
NULL) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECDH gen failed", cleanup);
}
/* Write affine coordinates of ephemeral pub key to TPM point Q
*
* Note
* mpi_write_bin prepends numbers with 0 if they are smaller as
* TPM2_MAX_ECC_KEY_BYTES. This is for big-endian mathematicaly
* correct but breaks the interface if we want to set bignum.size
* to anything but TPM2_MAX_ECC_KEY_BYTES. The easiest fix is
* to shrink out_size to mpi_size.
*/
Q->x.size = mbedtls_mpi_size(&ecdh_context.Q.X);
if (Q->x.size > TPM2_MAX_ECC_KEY_BYTES) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Q.x does not fit into byte buffer", cleanup);
}
if (mbedtls_mpi_write_binary(&ecdh_context.Q.X,
&Q->x.buffer[0],
Q->x.size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Q.x to byte buffer failed", cleanup);
}
Q->y.size = mbedtls_mpi_size(&ecdh_context.Q.Y);
if (Q->y.size > TPM2_MAX_ECC_KEY_BYTES) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Q.y does not fit into byte buffer", cleanup);
}
if (mbedtls_mpi_write_binary(&ecdh_context.Q.Y,
&Q->y.buffer[0],
Q->y.size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Q.y to byte buffer failed", cleanup);
}
/* Initialise Qp.Z (Qp would be zero, or "at infinity", if Z == 0) */
if (mbedtls_mpi_lset(&ecdh_context.Qp.Z, 1) != 0)
{
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Init of Qp.z to 1 failed", cleanup);
}
/* Read ephemeral pub key from TPM to Qp */
if (mbedtls_mpi_read_binary(&ecdh_context.Qp.X,
&key->publicArea.unique.ecc.x.buffer[0],
key->publicArea.unique.ecc.x.size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Read of Qp.x from byte buffer failed", cleanup);
}
if (mbedtls_mpi_read_binary(&ecdh_context.Qp.Y,
&key->publicArea.unique.ecc.y.buffer[0],
key->publicArea.unique.ecc.y.size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Read of Qp.y from byte buffer failed", cleanup);
}
/* Validate TPM's pub key */
if (mbedtls_ecp_check_pubkey(&ecdh_context.grp,
&ecdh_context.Qp) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Point Qp is invalid", cleanup);
}
/* Calculate shared secret */
if (mbedtls_ecdh_compute_shared(&ecdh_context.grp,
&ecdh_context.z,
&ecdh_context.Qp,
&ecdh_context.d,
get_random,
NULL) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"ECDH compute shared failed", cleanup);
}
/* Write shared secret to TPM ECC param Z */
Z->size = mbedtls_mpi_size(&ecdh_context.z);
if (Z->size > TPM2_MAX_ECC_KEY_BYTES) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Q.y does not fit into byte buffer", cleanup);
}
if (mbedtls_mpi_write_binary(&ecdh_context.z,
&Z->buffer[0],
Z->size) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Write Z to byte buffer failed", cleanup);
}
/* Write the public ephemeral key in TPM format to out buffer */
size_t offset = 0;
r = Tss2_MU_TPMS_ECC_POINT_Marshal(Q, &out_buffer[0], max_out_size, &offset);
goto_if_error(r, "Error marshaling Q", cleanup);
*out_size = offset;
cleanup:
mbedtls_ecdh_free(&ecdh_context);
return r;
}
/** Encrypt data with AES.
*
* @param[in] key key used for AES.
* @param[in] tpm_sym_alg AES type in TSS2 notation (must be TPM2_ALG_AES).
* @param[in] key_bits Key size in bits.
* @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB).
* For parameter encryption only CFB can be used.
* @param[in] blk_len Length Block length of AES.
* @param[in,out] buffer Data to be encrypted. The encrypted date will be stored
* in this buffer.
* @param[in] buffer_size size of data to be encrypted.
* @param[in] iv The initialization vector. The size is equal to blk_len.
* @retval TSS2_RC_SUCCESS on success, or TSS2_ESYS_RC_BAD_VALUE and
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters,
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_sym_aes_encrypt(uint8_t * key,
TPM2_ALG_ID tpm_sym_alg,
TPMI_AES_KEY_BITS key_bits,
TPM2_ALG_ID tpm_mode,
size_t blk_len,
uint8_t * buffer,
size_t buffer_size,
uint8_t * iv)
{
/* Parameter blk_len needed for other crypto libraries */
(void)blk_len;
TSS2_RC r = TSS2_RC_SUCCESS;
mbedtls_aes_context aes_ctx;
if (key == NULL || buffer == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Bad reference");
}
mbedtls_aes_init(&aes_ctx);
if (tpm_sym_alg != TPM2_ALG_AES || tpm_mode != TPM2_ALG_CFB) {
goto_error(r, TSS2_ESYS_RC_BAD_VALUE,
"AES encrypt called with wrong algorithm.", cleanup);
}
if (mbedtls_aes_setkey_enc(&aes_ctx, key, key_bits) != 0) {
goto_error(r, TSS2_ESYS_RC_BAD_VALUE,
"Key size not not implemented.", cleanup);
}
size_t iv_off = 0;
if (mbedtls_aes_crypt_cfb128(&aes_ctx, MBEDTLS_AES_ENCRYPT, buffer_size,
&iv_off, iv, buffer, buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Enncrypt", cleanup);
}
cleanup:
mbedtls_aes_free(&aes_ctx);
return r;
}
/** Decrypt data with AES.
*
* @param[in] key key used for AES.
* @param[in] tpm_sym_alg AES type in TSS2 notation (must be TPM2_ALG_AES).
* @param[in] key_bits Key size in bits.
* @param[in] tpm_mode Block cipher mode of opertion in TSS2 notation (CFB).
* For parameter encryption only CFB can be used.
* @param[in] blk_len Length Block length of AES.
* @param[in,out] buffer Data to be decrypted. The decrypted date will be stored
* in this buffer.
* @param[in] buffer_size size of data to be encrypted.
* @param[in] iv The initialization vector. The size is equal to blk_len.
* @retval TSS2_RC_SUCCESS on success, or TSS2_ESYS_RC_BAD_VALUE and
* @retval TSS2_ESYS_RC_BAD_REFERENCE for invalid parameters,
* @retval TSS2_ESYS_RC_GENERAL_FAILURE for errors of the crypto library.
*/
TSS2_RC
iesys_cryptmbed_sym_aes_decrypt(uint8_t * key,
TPM2_ALG_ID tpm_sym_alg,
TPMI_AES_KEY_BITS key_bits,
TPM2_ALG_ID tpm_mode,
size_t blk_len,
uint8_t * buffer,
size_t buffer_size,
uint8_t * iv)
{
/* Parameter blk_len needed for other crypto libraries */
(void)blk_len;
TSS2_RC r = TSS2_RC_SUCCESS;
mbedtls_aes_context aes_ctx;
if (key == NULL || buffer == NULL) {
return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Bad reference");
}
mbedtls_aes_init(&aes_ctx);
if (tpm_sym_alg != TPM2_ALG_AES || tpm_mode != TPM2_ALG_CFB) {
goto_error(r, TSS2_ESYS_RC_BAD_VALUE,
"AES encrypt called with wrong algorithm.", cleanup);
}
/* Note in mbedTLS Documentation:
* For CFB, you must set up the context with mbedtls_aes_setkey_enc(),
* regardless of whether you are performing an encryption or decryption
* operation, that is, regardless of the mode parameter. This is because
* CFB mode uses the same key schedule for encryption and decryption.
*/
if (mbedtls_aes_setkey_enc(&aes_ctx, key, key_bits) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Key size not not implemented.", cleanup);
}
size_t iv_off = 0;
if (mbedtls_aes_crypt_cfb128(&aes_ctx, MBEDTLS_AES_DECRYPT, buffer_size,
&iv_off, iv, buffer, buffer) != 0) {
goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE,
"Dencrypt", cleanup);
}
cleanup:
mbedtls_aes_free(&aes_ctx);
return r;
}