/* SPDX-License-Identifier: BSD-2-Clause */ /******************************************************************************* * Copyright 2017-2018, Fraunhofer SIT sponsored by Infineon Technologies AG * All rights reserved. ******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "tss2_esys.h" #include "esys_crypto.h" #include "esys_crypto_ossl.h" #include "esys_iutil.h" #include "esys_mu.h" #define LOGMODULE esys_crypto #include "util/log.h" #include "util/aux_util.h" static int iesys_bn2binpad(const BIGNUM *bn, unsigned char *bin, int bin_length) { int len_bn = BN_num_bytes(bn); int offset = bin_length - len_bn; memset(bin,0,offset); BN_bn2bin(bn, bin + offset); return 1; } /** Context to hold temporary values for iesys_crypto */ typedef struct _IESYS_CRYPTO_CONTEXT { enum { IESYS_CRYPTOSSL_TYPE_HASH = 1, IESYS_CRYPTOSSL_TYPE_HMAC, } type; /**< The type of context to hold; hash or hmac */ union { struct { EVP_MD_CTX *ossl_context; const EVP_MD *ossl_hash_alg; size_t hash_len; } hash; /**< the state variables for a hash context */ struct { EVP_MD_CTX *ossl_context; const EVP_MD *ossl_hash_alg; size_t hmac_len; } hmac; /**< the state variables for an hmac context */ }; } IESYS_CRYPTOSSL_CONTEXT; const EVP_MD * get_ossl_hash_md(TPM2_ALG_ID hashAlg) { switch (hashAlg) { case TPM2_ALG_SHA1: return EVP_sha1(); break; case TPM2_ALG_SHA256: return EVP_sha256(); break; case TPM2_ALG_SHA384: return EVP_sha384(); break; case TPM2_ALG_SHA512: return EVP_sha512(); break; default: return NULL; } } /** 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_cryptossl_hash_start(IESYS_CRYPTO_CONTEXT_BLOB ** context, TPM2_ALG_ID hashAlg) { TSS2_RC r = TSS2_RC_SUCCESS; LOG_TRACE("call: context=%p hashAlg=%"PRIu16, context, hashAlg); return_if_null(context, "Context is NULL", TSS2_ESYS_RC_BAD_REFERENCE); return_if_null(context, "Null-Pointer passed for context", TSS2_ESYS_RC_BAD_REFERENCE); IESYS_CRYPTOSSL_CONTEXT *mycontext; mycontext = calloc(1, sizeof(IESYS_CRYPTOSSL_CONTEXT)); return_if_null(mycontext, "Out of Memory", TSS2_ESYS_RC_MEMORY); mycontext->type = IESYS_CRYPTOSSL_TYPE_HASH; if (!(mycontext->hash.ossl_hash_alg = get_ossl_hash_md(hashAlg))) { goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED, "Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg); } if (iesys_crypto_hash_get_digest_size(hashAlg, &mycontext->hash.hash_len)) { goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED, "Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg); } if (!(mycontext->hash.ossl_context = EVP_MD_CTX_create())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Error EVP_MD_CTX_create", cleanup); } if (1 != EVP_DigestInit_ex(mycontext->hash.ossl_context, mycontext->hash.ossl_hash_alg, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Errror EVP_DigestInit_ex", cleanup); } *context = (IESYS_CRYPTO_CONTEXT_BLOB *) mycontext; return TSS2_RC_SUCCESS; cleanup: if (mycontext->hash.ossl_context) EVP_MD_CTX_destroy(mycontext->hash.ossl_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_cryptossl_hash_update(IESYS_CRYPTO_CONTEXT_BLOB * context, const uint8_t * buffer, size_t size) { LOG_TRACE("called for context %p, buffer %p and size %zd", context, buffer, size); if (context == NULL || buffer == NULL) { LOG_ERROR("Null-Pointer passed"); return TSS2_ESYS_RC_BAD_REFERENCE; } IESYS_CRYPTOSSL_CONTEXT *mycontext = (IESYS_CRYPTOSSL_CONTEXT *) context; if (mycontext->type != IESYS_CRYPTOSSL_TYPE_HASH) { LOG_ERROR("bad context"); return TSS2_ESYS_RC_BAD_REFERENCE; } LOGBLOB_TRACE(buffer, size, "Updating hash with"); if (1 != EVP_DigestUpdate(mycontext->hash.ossl_context, buffer, size)) { return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "OSSL 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_cryptossl_hash_update2b(IESYS_CRYPTO_CONTEXT_BLOB * context, TPM2B * b) { LOG_TRACE("called for context-pointer %p and 2b-pointer %p", context, b); if (context == NULL || b == NULL) { LOG_ERROR("Null-Pointer passed"); return TSS2_ESYS_RC_BAD_REFERENCE; } TSS2_RC ret = iesys_cryptossl_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_cryptossl_hash_finish(IESYS_CRYPTO_CONTEXT_BLOB ** context, uint8_t * buffer, size_t * size) { unsigned int digest_size = 0; LOG_TRACE("called for context-pointer %p, buffer %p and size-pointer %p", context, buffer, size); if (context == NULL || *context == NULL || buffer == NULL || size == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed"); } IESYS_CRYPTOSSL_CONTEXT *mycontext = * context; if (mycontext->type != IESYS_CRYPTOSSL_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 (1 != EVP_DigestFinal_ex(mycontext->hash.ossl_context, buffer, &digest_size)) { return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "Ossl error."); } if (digest_size != mycontext->hash.hash_len) { return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "Invalid size computed by EVP_DigestFinal_ex"); } LOGBLOB_TRACE(buffer, mycontext->hash.hash_len, "read hash result"); *size = mycontext->hash.hash_len; EVP_MD_CTX_destroy(mycontext->hash.ossl_context); free(mycontext); *context = NULL; return TSS2_RC_SUCCESS; } /** 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_cryptossl_hash_abort(IESYS_CRYPTO_CONTEXT_BLOB ** context) { LOG_TRACE("called for context-pointer %p", context); if (context == NULL || *context == NULL) { LOG_DEBUG("Null-Pointer passed"); return; } IESYS_CRYPTOSSL_CONTEXT *mycontext = (IESYS_CRYPTOSSL_CONTEXT *) * context; if (mycontext->type != IESYS_CRYPTOSSL_TYPE_HASH) { LOG_DEBUG("bad context"); return; } EVP_MD_CTX_destroy(mycontext->hash.ossl_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_cryptossl_hmac_start(IESYS_CRYPTO_CONTEXT_BLOB ** context, TPM2_ALG_ID hashAlg, const uint8_t * key, size_t size) { TSS2_RC r = TSS2_RC_SUCCESS; EVP_PKEY *hkey = NULL; LOG_TRACE("called for context-pointer %p and hmacAlg %d", context, hashAlg); LOGBLOB_TRACE(key, size, "Starting hmac with"); if (context == NULL || key == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed in for context"); } IESYS_CRYPTOSSL_CONTEXT *mycontext = calloc(1, sizeof(IESYS_CRYPTOSSL_CONTEXT)); return_if_null(mycontext, "Out of Memory", TSS2_ESYS_RC_MEMORY); if (!(mycontext->hmac.ossl_hash_alg = get_ossl_hash_md(hashAlg))) { goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED, "Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg); } if (iesys_crypto_hash_get_digest_size(hashAlg, &mycontext->hmac.hmac_len)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Unsupported hash algorithm (%"PRIu16")", cleanup, hashAlg); } if (!(mycontext->hmac.ossl_context = EVP_MD_CTX_create())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Error EVP_MD_CTX_create", cleanup); } if (!(hkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, NULL, key, size))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "EVP_PKEY_new_mac_key", cleanup); } if(1 != EVP_DigestSignInit(mycontext->hmac.ossl_context, NULL, mycontext->hmac.ossl_hash_alg, NULL, hkey)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "DigestSignInit", cleanup); } mycontext->type = IESYS_CRYPTOSSL_TYPE_HMAC; *context = (IESYS_CRYPTO_CONTEXT_BLOB *) mycontext; EVP_PKEY_free(hkey); return TSS2_RC_SUCCESS; cleanup: if (mycontext->hmac.ossl_context) EVP_MD_CTX_destroy(mycontext->hmac.ossl_context); if(hkey) EVP_PKEY_free(hkey); 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_cryptossl_hmac_update(IESYS_CRYPTO_CONTEXT_BLOB * context, const uint8_t * buffer, size_t size) { LOG_TRACE("called for context %p, buffer %p and size %zd", context, buffer, size); if (context == NULL || buffer == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed"); } IESYS_CRYPTOSSL_CONTEXT *mycontext = (IESYS_CRYPTOSSL_CONTEXT *) context; if (mycontext->type != IESYS_CRYPTOSSL_TYPE_HMAC) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "bad context"); } LOGBLOB_TRACE(buffer, size, "Updating hmac with"); /* Call update with the message */ if(1 != EVP_DigestSignUpdate(mycontext->hmac.ossl_context, buffer, size)) { return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "OSSL 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_cryptossl_hmac_update2b(IESYS_CRYPTO_CONTEXT_BLOB * context, TPM2B * b) { LOG_TRACE("called for context-pointer %p and 2b-pointer %p", context, b); if (context == NULL || b == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed"); } TSS2_RC ret = iesys_cryptossl_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_cryptossl_hmac_finish(IESYS_CRYPTO_CONTEXT_BLOB ** context, uint8_t * buffer, size_t * size) { TSS2_RC r = TSS2_RC_SUCCESS; LOG_TRACE("called for context-pointer %p, buffer %p and size-pointer %p", context, buffer, size); if (context == NULL || *context == NULL || buffer == NULL || size == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Null-Pointer passed"); } IESYS_CRYPTOSSL_CONTEXT *mycontext = (IESYS_CRYPTOSSL_CONTEXT *) * context; if (mycontext->type != IESYS_CRYPTOSSL_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 (1 != EVP_DigestSignFinal(mycontext->hmac.ossl_context, buffer, size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "DigestSignFinal", cleanup); } LOGBLOB_TRACE(buffer, *size, "read hmac result"); cleanup: EVP_MD_CTX_destroy(mycontext->hmac.ossl_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_cryptossl_hmac_finish2b(IESYS_CRYPTO_CONTEXT_BLOB ** context, TPM2B * hmac) { LOG_TRACE("called for context-pointer %p and 2b-pointer %p", context, 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_cryptossl_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_cryptossl_hmac_abort(IESYS_CRYPTO_CONTEXT_BLOB ** context) { LOG_TRACE("called for context-pointer %p", context); if (context == NULL || *context == NULL) { LOG_DEBUG("Null-Pointer passed"); return; } if (*context != NULL) { IESYS_CRYPTOSSL_CONTEXT *mycontext = (IESYS_CRYPTOSSL_CONTEXT *) * context; if (mycontext->type != IESYS_CRYPTOSSL_TYPE_HMAC) { LOG_DEBUG("bad context"); return; } EVP_MD_CTX_destroy(mycontext->hmac.ossl_context); free(mycontext); *context = NULL; } } /** 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_cryptossl_random2b(TPM2B_NONCE * nonce, size_t num_bytes) { const RAND_METHOD *rand_save = RAND_get_rand_method(); if (num_bytes == 0) { nonce->size = sizeof(TPMU_HA); } else { nonce->size = num_bytes; } #if OPENSSL_VERSION_NUMBER >= 0x10100000L RAND_set_rand_method(RAND_OpenSSL()); #else RAND_set_rand_method(RAND_SSLeay()); #endif if (1 != RAND_bytes(&nonce->buffer[0], nonce->size)) { RAND_set_rand_method(rand_save); return_error(TSS2_ESYS_RC_GENERAL_FAILURE, "Failure in random number generator."); } RAND_set_rand_method(rand_save); 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_cryptossl_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) { const RAND_METHOD *rand_save = RAND_get_rand_method(); #if OPENSSL_VERSION_NUMBER >= 0x10100000L RAND_set_rand_method(RAND_OpenSSL()); #else RAND_set_rand_method(RAND_SSLeay()); #endif TSS2_RC r = TSS2_RC_SUCCESS; const EVP_MD * hashAlg = NULL; RSA * rsa_key = NULL; EVP_PKEY *evp_rsa_key = NULL; EVP_PKEY_CTX *ctx = NULL; BIGNUM* bne = NULL; int padding; char *label_copy = NULL; if (!(hashAlg = get_ossl_hash_md(pub_tpm_key->publicArea.nameAlg))) { LOG_ERROR("Unsupported hash algorithm (%"PRIu16")", pub_tpm_key->publicArea.nameAlg); RAND_set_rand_method(rand_save); return TSS2_ESYS_RC_NOT_IMPLEMENTED; } if (!(bne = BN_new())) { goto_error(r, TSS2_ESYS_RC_MEMORY, "Could not allocate Big Number", cleanup); } switch (pub_tpm_key->publicArea.parameters.rsaDetail.scheme.scheme) { case TPM2_ALG_NULL: padding = RSA_NO_PADDING; break; case TPM2_ALG_RSAES: padding = RSA_PKCS1_PADDING; break; case TPM2_ALG_OAEP: padding = RSA_PKCS1_OAEP_PADDING; break; default: goto_error(r, TSS2_ESYS_RC_BAD_VALUE, "Illegal RSA scheme", cleanup); } UINT32 exp; if (pub_tpm_key->publicArea.parameters.rsaDetail.exponent == 0) exp = 65537; else exp = pub_tpm_key->publicArea.parameters.rsaDetail.exponent; if (1 != BN_set_word(bne, exp)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set exponent.", cleanup); } if (!(rsa_key = RSA_new())) { goto_error(r, TSS2_ESYS_RC_MEMORY, "Could not allocate RSA key", cleanup); } if (1 != RSA_generate_key_ex(rsa_key, pub_tpm_key->publicArea.parameters.rsaDetail.keyBits, bne, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not generate RSA key", cleanup); } if (!(evp_rsa_key = EVP_PKEY_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not create evp key.", cleanup); } #if OPENSSL_VERSION_NUMBER < 0x10100000L if (!BN_bin2bn(pub_tpm_key->publicArea.unique.rsa.buffer, pub_tpm_key->publicArea.unique.rsa.size, rsa_key->n)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not create rsa n.", cleanup); } #else BIGNUM *n = NULL; if (!(n = BN_bin2bn(pub_tpm_key->publicArea.unique.rsa.buffer, pub_tpm_key->publicArea.unique.rsa.size, NULL))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not create rsa n.", cleanup); } if (1 != RSA_set0_key(rsa_key, n, NULL, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set rsa n.", cleanup); } #endif if (1 != EVP_PKEY_set1_RSA(evp_rsa_key, rsa_key)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set rsa key.", cleanup); } if (!(ctx = EVP_PKEY_CTX_new(evp_rsa_key, NULL))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not create evp context.", cleanup); } if (1 != EVP_PKEY_encrypt_init(ctx)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not init encrypt context.", cleanup); } if (1 != EVP_PKEY_CTX_set_rsa_padding(ctx, padding)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set RSA passing.", cleanup); } label_copy = OPENSSL_strdup(label); if (!label_copy) { goto_error(r, TSS2_ESYS_RC_MEMORY, "Could not duplicate OAEP label", cleanup); } if (1 != EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, label_copy, strlen(label_copy)+1)) { OPENSSL_free(label_copy); goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set RSA label.", cleanup); } if (1 != EVP_PKEY_CTX_set_rsa_oaep_md(ctx, hashAlg)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not set hash algorithm.", cleanup); } /* Determine out size */ if (1 != EVP_PKEY_encrypt(ctx, NULL, out_size, in_buffer, in_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not determine ciper size.", cleanup); } if ((size_t)*out_size > max_out_size) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Encrypted data too big", cleanup); } /* Encrypt data */ if (1 != EVP_PKEY_encrypt(ctx, out_buffer, out_size, in_buffer, in_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Could not encrypt data.", cleanup); } r = TSS2_RC_SUCCESS; cleanup: OSSL_FREE(ctx, EVP_PKEY_CTX); OSSL_FREE(evp_rsa_key, EVP_PKEY); OSSL_FREE(rsa_key, RSA); OSSL_FREE(bne, BN); RAND_set_rand_method(rand_save); return r; } /** Computation of OSSL ec public point from TPM public point. * * @param[in] group The definition of the used ec curve. * @param[in] key The TPM public key. * @param[out] The TPM's public point in OSSL format. * @retval TSS2_RC_SUCCESS on success. * @retval TSS2_ESYS_RC_GENERAL_FAILURE The internal crypto engine failed. */ static TSS2_RC tpm_pub_to_ossl_pub(EC_GROUP *group, TPM2B_PUBLIC *key, EC_POINT **tpm_pub_key) { TSS2_RC r = TSS2_RC_SUCCESS; BIGNUM *bn_x = NULL; BIGNUM *bn_y = NULL; /* Create the big numbers for the coordinates of the point */ if (!(bn_x = BN_bin2bn(&key->publicArea.unique.ecc.x.buffer[0], key->publicArea.unique.ecc.x.size, NULL))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create big num from byte buffer.", cleanup); } if (!(bn_y = BN_bin2bn(&key->publicArea.unique.ecc.y.buffer[0], key->publicArea.unique.ecc.y.size, NULL))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create big num from byte buffer.", cleanup); } /* Create the ec point with the affine coordinates of the TPM point */ if (!(*tpm_pub_key = EC_POINT_new(group))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create point.", cleanup); } if (1 != EC_POINT_set_affine_coordinates_GFp(group, *tpm_pub_key, bn_x, bn_y, NULL)) { OSSL_FREE(*tpm_pub_key, EC_POINT); goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Set affine coordinates", cleanup); } if (1 != EC_POINT_is_on_curve(group, *tpm_pub_key, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "The TPM point is not on the curve", cleanup); } cleanup: OSSL_FREE(bn_x, BN); OSSL_FREE(bn_y, BN); 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 keyare 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_cryptossl_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; EC_GROUP *group = NULL; /* Group defines the used curve */ EC_KEY *eph_ec_key = NULL; /* Ephemeral ec key of application */ const EC_POINT *eph_pub_key = NULL; /* Public part of ephemeral key */ EC_POINT *tpm_pub_key = NULL; /* Public part of TPM key */ EC_POINT *mul_eph_tpm = NULL; BIGNUM *bn_x = NULL; BIGNUM *bn_y = NULL; size_t key_size; int curveId; size_t offset; /* Set ossl constant for curve type and create group for curve */ switch (key->publicArea.parameters.eccDetail.curveID) { case TPM2_ECC_NIST_P192: curveId = NID_X9_62_prime192v1; key_size = 24; break; case TPM2_ECC_NIST_P224: curveId = NID_secp224r1; key_size = 28; break; case TPM2_ECC_NIST_P256: curveId = NID_X9_62_prime256v1; key_size = 32; break; case TPM2_ECC_NIST_P384: curveId = NID_secp384r1; key_size = 48; break; case TPM2_ECC_NIST_P521: curveId = NID_secp521r1; key_size = 66; break; default: return_error(TSS2_ESYS_RC_NOT_IMPLEMENTED, "ECC curve not implemented."); } if (!(group = EC_GROUP_new_by_curve_name(curveId))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create group for curve", cleanup); } /* Create ephemeral key */ if (!(eph_ec_key = EC_KEY_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create ec key", cleanup); } if (1 != EC_KEY_set_group(eph_ec_key , group)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Set group", cleanup); } if (1 != EC_KEY_generate_key(eph_ec_key)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Generate ec key", cleanup); } if (!(eph_pub_key = EC_KEY_get0_public_key(eph_ec_key))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Get public key", cleanup); } if (1 != EC_POINT_is_on_curve(group, eph_pub_key, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Ephemeral public key is on curve",cleanup); } /* Write affine coordinates of ephemeral pub key to TPM point Q */ if (!(bn_x = BN_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create bignum", cleanup); } if (!(bn_y = BN_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create bignum", cleanup); } if (1 != EC_POINT_get_affine_coordinates_GFp(group, eph_pub_key, bn_x, bn_y, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Get affine x coordinate", cleanup); } if (1 != iesys_bn2binpad(bn_x, &Q->x.buffer[0], key_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Write big num byte buffer", cleanup); } if (1 != iesys_bn2binpad(bn_y, &Q->y.buffer[0], key_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Write big num byte buffer", cleanup); } Q->x.size = key_size; Q->y.size = key_size; /* Create an OSSL EC point from the TPM public point */ r = tpm_pub_to_ossl_pub(group, key, &tpm_pub_key); goto_if_error(r, "Convert TPM pub point to ossl pub point", cleanup); /* Multiply the ephemeral private key with TPM public key */ const BIGNUM * eph_priv_key = EC_KEY_get0_private_key(eph_ec_key); if (!(mul_eph_tpm = EC_POINT_new(group))) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Create point.", cleanup); } if (1 != EC_POINT_mul(group, mul_eph_tpm, NULL, tpm_pub_key, eph_priv_key, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "ec point multiplication", cleanup); } /* Write the x-part of the affine coordinate to Z */ if (1 != EC_POINT_get_affine_coordinates_GFp(group, mul_eph_tpm, bn_x, bn_y, NULL)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Get affine x coordinate", cleanup); } if (1 != iesys_bn2binpad(bn_x, &Z->buffer[0], key_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Write big num byte buffer", cleanup); } Z->size = key_size; /* Write the public ephemeral key in TPM format to out buffer */ offset = 0; r = Tss2_MU_TPMS_ECC_POINT_Marshal(Q, &out_buffer[0], max_out_size, &offset); goto_if_error(r, "Error marshaling", cleanup); *out_size = offset; cleanup: OSSL_FREE(mul_eph_tpm, EC_POINT); OSSL_FREE(tpm_pub_key, EC_POINT); OSSL_FREE(group,EC_GROUP); OSSL_FREE(eph_ec_key, EC_KEY); /* Note: free of eph_pub_key already done by free of eph_ec_key */ OSSL_FREE(bn_x, BN); OSSL_FREE(bn_y, BN); 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_cryptossl_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) { TSS2_RC r = TSS2_RC_SUCCESS; const EVP_CIPHER *cipher_alg = NULL; EVP_CIPHER_CTX *ctx = NULL; int cipher_len; if (key == NULL || buffer == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Bad reference"); } LOGBLOB_TRACE(buffer, buffer_size, "IESYS AES input"); /* Parameter blk_len needed for other crypto libraries */ (void)blk_len; if (key_bits == 128 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_128_cfb(); else if (key_bits == 192 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_192_cfb(); else if (key_bits == 256 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_256_cfb(); else { goto_error(r, TSS2_ESYS_RC_BAD_VALUE, "AES algorithm not implemented or illegal mode (CFB expected).", cleanup); } if (tpm_sym_alg != TPM2_ALG_AES) { goto_error(r, TSS2_ESYS_RC_BAD_VALUE, "AES encrypt called with wrong algorithm.", cleanup); } /* Create and initialize the context */ if(!(ctx = EVP_CIPHER_CTX_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Initialize cipher context", cleanup); } if (1 != EVP_EncryptInit_ex(ctx, cipher_alg, NULL, key, iv)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Initialize cipher operation", cleanup); } if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Set key and iv", cleanup); } /* Perform the encryption */ if (1 != EVP_EncryptUpdate(ctx, buffer, &cipher_len, buffer, buffer_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Encrypt update", cleanup); } if (1 != EVP_EncryptFinal_ex(ctx, buffer, &cipher_len)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Encrypt final", cleanup); } LOGBLOB_TRACE(buffer, buffer_size, "IESYS AES output"); cleanup: OSSL_FREE(ctx,EVP_CIPHER_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_cryptossl_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) { TSS2_RC r = TSS2_RC_SUCCESS; const EVP_CIPHER *cipher_alg = NULL; EVP_CIPHER_CTX *ctx = NULL; int cipher_len = 0; /* Parameter blk_len needed for other crypto libraries */ (void)blk_len; if (key == NULL || buffer == NULL) { return_error(TSS2_ESYS_RC_BAD_REFERENCE, "Bad reference"); } if (tpm_sym_alg != TPM2_ALG_AES) { goto_error(r, TSS2_ESYS_RC_BAD_VALUE, "AES encrypt called with wrong algorithm.", cleanup); } if (key_bits == 128 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_128_cfb(); else if (key_bits == 192 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_192_cfb(); else if (key_bits == 256 && tpm_mode == TPM2_ALG_CFB) cipher_alg = EVP_aes_256_cfb(); else { goto_error(r, TSS2_ESYS_RC_NOT_IMPLEMENTED, "AES algorithm not implemented.", cleanup); } /* Create and initialize the context */ if(!(ctx = EVP_CIPHER_CTX_new())) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Initialize cipher context", cleanup); } LOGBLOB_TRACE(buffer, buffer_size, "IESYS AES input"); if (1 != EVP_DecryptInit_ex(ctx, cipher_alg, NULL, key, iv)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Initialize cipher operation", cleanup); } if (1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Set key and iv", cleanup); } /* Perform the decryption */ if (1 != EVP_DecryptUpdate(ctx, buffer, &cipher_len, buffer, buffer_size)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Encrypt update", cleanup); } if (1 != EVP_DecryptFinal_ex(ctx, buffer, &cipher_len)) { goto_error(r, TSS2_ESYS_RC_GENERAL_FAILURE, "Encrypt final", cleanup); } LOGBLOB_TRACE(buffer, buffer_size, "IESYS AES output"); cleanup: OSSL_FREE(ctx,EVP_CIPHER_CTX); return r; } /** Initialize OpenSSL crypto backend. * * Initialize OpenSSL internal tables. * * @retval TSS2_RC_SUCCESS always returned because OpenSSL_add_all_algorithms * does not deliver * a return code. */ TSS2_RC iesys_cryptossl_init() { ENGINE_load_builtin_engines(); OpenSSL_add_all_algorithms(); return TSS2_RC_SUCCESS; }