blob: ea68197a476990f893aace2f8ddf4dcb8883a7d3 [file] [log] [blame]
/* SPDX-License-Identifier: BSD-2-Clause */
/*******************************************************************************
* Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG
* All rights reserved.
******************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <openssl/evp.h>
#include <openssl/aes.h>
#include <openssl/rsa.h>
#include <openssl/engine.h>
#include <openssl/pem.h>
#include <openssl/x509v3.h>
#include <curl/curl.h>
#include <openssl/err.h>
#include "fapi_certificates.h"
#include "fapi_util.h"
#include "util/aux_util.h"
#include "fapi_crypto.h"
#define LOGMODULE fapi
#include "util/log.h"
#if OPENSSL_VERSION_NUMBER >= 0x10101000L
#define EC_POINT_set_affine_coordinates_tss(group, tpm_pub_key, bn_x, bn_y, dmy) \
EC_POINT_set_affine_coordinates(group, tpm_pub_key, bn_x, bn_y, dmy)
#define EC_POINT_get_affine_coordinates_tss(group, tpm_pub_key, bn_x, bn_y, dmy) \
EC_POINT_get_affine_coordinates(group, tpm_pub_key, bn_x, bn_y, dmy)
#else
#define EC_POINT_set_affine_coordinates_tss(group, tpm_pub_key, bn_x, bn_y, dmy) \
EC_POINT_set_affine_coordinates_GFp(group, tpm_pub_key, bn_x, bn_y, dmy)
#define EC_POINT_get_affine_coordinates_tss(group, tpm_pub_key, bn_x, bn_y, dmy) \
EC_POINT_get_affine_coordinates_GFp(group, tpm_pub_key, bn_x, bn_y, dmy)
#endif /* OPENSSL_VERSION_NUMBER >= 0x10101000L */
/** Context to hold temporary values for ifapi_crypto */
typedef struct _IFAPI_CRYPTO_CONTEXT {
/** The hash engine's context */
EVP_MD_CTX *osslContext;
/** The currently used hash algorithm */
const EVP_MD *osslHashAlgorithm;
/** The size of the hash's digest */
size_t hashSize;
} IFAPI_CRYPTO_CONTEXT;
/** A singleton crypto engine for hash operations */
static ENGINE *engine = NULL;
/**
* Returns the signature scheme that is currently used in the FAPI context.
*
* @param[in] profile The FAPI profile from which the signing scheme is
* retrieved
* @param[in] tpmPublic The public key for which the signing key is fetched
* from the FAPI
* @param[out] signatureScheme The currently used signature scheme
*
* @retval TSS2_RC_SUCCESS if the signature scheme was successfully fetched
* @retval TSS2_FAPI_RC_BAD_REFERENCE if one of the parameters is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if the key type is not TPM2_ALG_RSA or
* TPM2_ALG_ECC
*/
TPM2_RC
ifapi_get_profile_sig_scheme(
const IFAPI_PROFILE *profile,
const TPMT_PUBLIC *tpmPublic,
TPMT_SIG_SCHEME *signatureScheme)
{
/* Check for NULL parameters */
return_if_null(profile, "profile is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmPublic, "tpmPublic is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signatureScheme, "signatureScheme is NULL",
TSS2_FAPI_RC_BAD_REFERENCE);
/* Determine the appropriate signing scheme */
if (tpmPublic->type == TPM2_ALG_RSA) {
*signatureScheme = profile->rsa_signing_scheme;
return TSS2_RC_SUCCESS;
} else if (tpmPublic->type == TPM2_ALG_ECC) {
*signatureScheme = profile->ecc_signing_scheme;
return TSS2_RC_SUCCESS;
} else {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid key type.");
}
}
static const TPM2B_PUBLIC templateRsaSign = {
.size = 0,
.publicArea = {
.type = TPM2_ALG_RSA,
.nameAlg = TPM2_ALG_SHA1,
.objectAttributes = (TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_SIGN_ENCRYPT |
TPMA_OBJECT_RESTRICTED |
TPMA_OBJECT_SENSITIVEDATAORIGIN
),
.authPolicy = {
.size = 0,
.buffer = 0,
},
.parameters.rsaDetail = {
.symmetric = {
.algorithm = TPM2_ALG_NULL,
.keyBits.aes = 128,
.mode.aes = TPM2_ALG_CFB,
},
.scheme = {
.scheme = TPM2_ALG_RSAPSS,
.details.rsapss.hashAlg = TPM2_ALG_SHA1,
},
.keyBits = 2048,
.exponent = 65537,
},
.unique.rsa = {
.size = 0,
.buffer = {},
}
}
};
/**
* A FAPI template for ECC signing keys
*/
static const TPM2B_PUBLIC templateEccSign = {
.size = 0,
.publicArea = {
.type = TPM2_ALG_ECC,
.nameAlg = TPM2_ALG_SHA1,
.objectAttributes = (
TPMA_OBJECT_USERWITHAUTH |
TPMA_OBJECT_RESTRICTED |
TPMA_OBJECT_SIGN_ENCRYPT |
TPMA_OBJECT_SENSITIVEDATAORIGIN
),
.authPolicy = {
.size = 0,
},
.parameters.eccDetail = {
.symmetric = {
.algorithm = TPM2_ALG_NULL,
.keyBits.aes = 128,
.mode.aes = TPM2_ALG_ECB,
},
.scheme = {
.scheme = TPM2_ALG_ECDSA,
.details = { .ecdsa = { .hashAlg = TPM2_ALG_SHA256 }},
},
.curveID = TPM2_ECC_BN_P256,
.kdf = { .scheme = TPM2_ALG_NULL, .details = {} }
},
.unique.ecc = {
.x = { .size = 0, .buffer = {} },
.y = { .size = 0, .buffer = {} },
},
},
};
/**
* Initializes a FAPI key template for a given signature algorithm.
*
* @param[in] signatureAlgorithm The signature algorithm to use. Must be
* TPM2_ALG_RSA or TPM2_ALG_ECC
* @param[out] public The template to initialize
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if template is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if signatureAlgorithm is not TPM2_ALG_RSA or
* TPM2_ALG_ECC
*/
TSS2_RC
ifapi_initialize_sign_public(TPM2_ALG_ID signatureAlgorithm,
TPM2B_PUBLIC *public) {
/* Check for NULL parameters */
return_if_null(public, "public is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Initialize the template */
if (signatureAlgorithm == TPM2_ALG_RSA) {
/* RSA key template */
memcpy(public, &templateRsaSign, sizeof(TPM2B_PUBLIC));
} else if (signatureAlgorithm == TPM2_ALG_ECC) {
/* ECC key template */
memcpy(public, &templateEccSign, sizeof(TPM2B_PUBLIC));
} else {
/* Invalid key type */
LOG_ERROR("No suitable template found");
return TSS2_FAPI_RC_BAD_VALUE;
}
return TSS2_RC_SUCCESS;
}
/**
* Converts an openSSL BIGNUM into a binary byte buffer using.
*
* @param[in] bn The BIGNUM to convert
* @param[out] bin The binary buffer to which the bignum is converted
* @param[in] binSize The size of bin in bytes
*
* @retval 1 if the conversion was successful
* @retval 0 if one of the parameters is NULL
*/
static int
ifapi_bn2binpad(const BIGNUM *bn, unsigned char *bin, int binSize)
{
/* Check for NULL parameters */
return_if_null(bn, "bn is NULL", 0);
return_if_null(bin, "bin is NULL", 0);
/* Convert bn */
int bnSize = BN_num_bytes(bn);
int offset = binSize - bnSize;
memset(bin, 0, offset);
BN_bn2bin(bn, bin + offset);
return 1;
}
/**
* Returns the singleton hash engine for the use in ifapi_hash operations. If
* it does not yet exist, this function creates it.
*
* @retval A singleton hash engine
*/
static ENGINE *
get_engine()
{
/* If an engine is present, it is returned */
if (engine)
return engine;
/* Otherwise, engine is created and returned */
engine = ENGINE_by_id(NULL);
return engine;
}
/**
* Returns a suitable openSSL hash algorithm identifier for a given TSS hash
* algorithm identifier.
*
* @param[in] hashAlgorithm The TSS hash algorithm identifier
*
* @retval An openSSL hash algorithm identifier if one that is suitable to
* hashAlgorithm could be found
* @retval NULL if no suitable hash algorithm identifier could be found
*/
static const EVP_MD *
get_hash_md(TPM2_ALG_ID hashAlgorithm)
{
switch (hashAlgorithm) {
case TPM2_ALG_SHA1:
return EVP_sha1();
case TPM2_ALG_SHA256:
return EVP_sha256();
case TPM2_ALG_SHA384:
return EVP_sha384();
case TPM2_ALG_SHA512:
return EVP_sha512();
default:
return NULL;
}
}
/**
* Returns a suitable openSSL RSA signature scheme identifiver for a given TSS
* RSA signature scheme identifier.
*
* @param[in] signatureScheme The TSS RSA signature scheme identifier
*
* @retval RSA_PCKS1_PSS_PADDING if signatureScheme is TPM2_ALG_RSAPSS
* @retval RSA_PKCS1_PADDING if signatureScheme is TPM2_ALG_RSASSA
* @retval 0 otherwise
*/
static int
get_sig_scheme(TPM2_ALG_ID signatureScheme)
{
switch (signatureScheme) {
case TPM2_ALG_RSAPSS:
return RSA_PKCS1_PSS_PADDING;
case TPM2_ALG_RSASSA:
return RSA_PKCS1_PADDING;
default:
return 0;
}
}
/**
* Convert a TPM ECDSA signature into a DER formatted byte buffer. This can be
* used by TLS libraries.
*
* @param[in] tpmSignature The signature created by the TPM
* @param[out] signature A byte buffer that will hold the DER representation of
* the signature (callee allocated)
* @param[out] signatureSize The size of signature in bytes. May be NULL
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if tpmSignature is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
TSS2_RC
ifapi_tpm_ecc_sig_to_der(
const TPMT_SIGNATURE *tpmSignature,
uint8_t **signature,
size_t *signatureSize)
{
/* Check for NULL parameters */
return_if_null(tpmSignature, "tpmSignature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
ECDSA_SIG *ecdsaSignature = NULL;
BIGNUM *bns = NULL, *bnr = NULL;
int osslRC;
TSS2_RC r;
uint8_t *signatureWalking;
/* Initialize an OpenSSL ECDSA signature which servers as an intermediate
* between the TSS ECDSA signature and the DER byte buffer */
ecdsaSignature = ECDSA_SIG_new();
goto_if_null(ecdsaSignature, "Out of memory", TSS2_FAPI_RC_MEMORY,
cleanup);
bns = BN_bin2bn(&tpmSignature->signature.ecdsa.signatureS.buffer[0],
tpmSignature->signature.ecdsa.signatureS.size, NULL);
goto_if_null(bns, "Out of memory", TSS2_FAPI_RC_MEMORY, cleanup);
bnr = BN_bin2bn(&tpmSignature->signature.ecdsa.signatureR.buffer[0],
tpmSignature->signature.ecdsa.signatureR.size, NULL);
goto_if_null(bnr, "Out of memory", TSS2_FAPI_RC_MEMORY, cleanup);
#if OPENSSL_VERSION_NUMBER < 0x10100000
ecdsaSignature->s = bns;
ecdsaSignature->r = bnr;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
ECDSA_SIG_set0(ecdsaSignature, bnr, bns);
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
osslRC = i2d_ECDSA_SIG(ecdsaSignature, NULL);
if (osslRC == -1) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "OSSL error", cleanup);
}
/* Initialize the byte buffer for the DER representation */
*signature = malloc(osslRC);
signatureWalking = *signature;
goto_if_null(*signature, "Out of memory", TSS2_FAPI_RC_MEMORY, cleanup);
if (signatureSize != NULL) {
*signatureSize = osslRC;
}
/* Convert the OpenSSL ECDSA signature to the DER buffer */
osslRC = i2d_ECDSA_SIG(ecdsaSignature, &signatureWalking);
if (!osslRC) {
free(*signature);
if (signatureSize != NULL) {
*signatureSize = 0;
}
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "OSSL error", cleanup);
}
r = TSS2_RC_SUCCESS;
cleanup:
if (ecdsaSignature)
ECDSA_SIG_free(ecdsaSignature);
return r;
}
/**
* Converts a public RSA key created by the TPM into one that can be used by
* OpenSSL.
*
* @param[in] tpmPublicKey The public RSA key created by the TPM
* @param[out] evpPublicKey The converted public RSA key that can be used by
* OpenSSL
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if one of the parameters is NULL
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
*/
static TSS2_RC
ossl_rsa_pub_from_tpm(const TPM2B_PUBLIC *tpmPublicKey, EVP_PKEY *evpPublicKey)
{
/* Check for NULL parameters */
return_if_null(tpmPublicKey, "tpmPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(evpPublicKey, "evpPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Initialize the RSA parameters */
TSS2_RC r;
RSA *rsa = RSA_new();
BIGNUM *e = BN_new();
BIGNUM *d = BN_new();
BIGNUM *p = BN_new();
BIGNUM *q = BN_new();
BIGNUM *dmp1 = BN_new();
BIGNUM *dmq1 = BN_new();
BIGNUM *iqmp = BN_new();
BIGNUM *n = BN_bin2bn(tpmPublicKey->publicArea.unique.rsa.buffer,
tpmPublicKey->publicArea.unique.rsa.size, NULL);
if (!n || !e || !d || !p || !q || !dmp1 || !dmq1 || !iqmp || !rsa) {
goto_error(r, TSS2_FAPI_RC_MEMORY, "Out of memory", error_cleanup);
}
BN_set_word(d, 0);
BN_set_word(p, 0);
BN_set_word(q, 0);
BN_set_word(dmp1, 0);
BN_set_word(dmq1, 0);
BN_set_word(iqmp, 0);
uint32_t exp;
if (tpmPublicKey->publicArea.parameters.rsaDetail.exponent == 0)
exp = 65537;
else
exp = tpmPublicKey->publicArea.parameters.rsaDetail.exponent;
if (1 != BN_set_word(e, exp)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Could not set exponent.", error_cleanup);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000
rsa->e = e;
rsa->n = n;
rsa->d = d;
rsa->p = p;
rsa->q = q;
rsa->dmp1 = dmp1;
rsa->dmq1 = dmq1;
rsa->iqmp = iqmp;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
RSA_set0_key(rsa, n, e, d);
RSA_set0_factors(rsa, p, q);
RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
/* Assign the parameters to the key */
if (!EVP_PKEY_assign_RSA(evpPublicKey, rsa)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Assign rsa key",
error_cleanup);
}
return TSS2_RC_SUCCESS;
error_cleanup:
OSSL_FREE(rsa, RSA);
OSSL_FREE(e, BN);
OSSL_FREE(n, BN);
OSSL_FREE(d, BN);
OSSL_FREE(p, BN);
OSSL_FREE(q, BN);
OSSL_FREE(dmp1, BN);
OSSL_FREE(dmq1, BN);
OSSL_FREE(iqmp, BN);
return r;
}
/**
* Converts a public ECC key created by the TPM into one that can be used by
* OpenSSL.
*
* @param[in] tpmPublicKey The public ECC key created by the TPM
* @param[out] evpPublicKey The converted public ECC key that can be used by
* OpenSSL
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if one of the parameters is NULL
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
static TSS2_RC
ossl_ecc_pub_from_tpm(const TPM2B_PUBLIC *tpmPublicKey, EVP_PKEY *evpPublicKey)
{
/* Check for NULL parameters */
return_if_null(tpmPublicKey, "tpmPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(evpPublicKey, "evpPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r;
EC_GROUP *ecgroup = NULL;
int curveId;
BIGNUM *x = NULL, *y = NULL;
EC_KEY *ecKey = EC_KEY_new();
return_if_null(ecKey, "Out of memory.", TSS2_FAPI_RC_MEMORY);
/* Find the curve of the ECC key */
switch (tpmPublicKey->publicArea.parameters.eccDetail.curveID) {
case TPM2_ECC_NIST_P192:
curveId = NID_X9_62_prime192v1;
break;
case TPM2_ECC_NIST_P224:
curveId = NID_secp224r1;
break;
case TPM2_ECC_NIST_P256:
curveId = NID_X9_62_prime256v1;
break;
case TPM2_ECC_NIST_P384:
curveId = NID_secp384r1;
break;
case TPM2_ECC_NIST_P521:
curveId = NID_secp521r1;
break;
default:
return_error(TSS2_FAPI_RC_BAD_VALUE,
"ECC curve not implemented.");
}
/* Initialize the OpenSSL ECC key with its group */
ecgroup = EC_GROUP_new_by_curve_name(curveId);
goto_if_null(ecgroup, "new EC group.", TSS2_FAPI_RC_GENERAL_FAILURE,
error_cleanup);
if (!EC_KEY_set_group(ecKey, ecgroup)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "EC_KEY_set_group",
error_cleanup);
}
EC_GROUP_free(ecgroup);
/* Set the ECC parameters in the OpenSSL key */
x = BN_bin2bn(tpmPublicKey->publicArea.unique.ecc.x.buffer,
tpmPublicKey->publicArea.unique.ecc.x.size, NULL);
y = BN_bin2bn(tpmPublicKey->publicArea.unique.ecc.y.buffer,
tpmPublicKey->publicArea.unique.ecc.y.size, NULL);
if (!x || !y) {
goto_error(r, TSS2_FAPI_RC_MEMORY, "Out of memory", error_cleanup);
}
if (!EC_KEY_set_public_key_affine_coordinates(ecKey, x, y)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"EC_KEY_set_public_key_affine_coordinates", error_cleanup);
}
if (!EVP_PKEY_assign_EC_KEY(evpPublicKey, ecKey)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Assign ecc key",
error_cleanup);
}
/* Needed for older OSSL versions. */
EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
OSSL_FREE(y, BN);
OSSL_FREE(x, BN);
return TSS2_RC_SUCCESS;
error_cleanup:
OSSL_FREE(y, BN);
OSSL_FREE(x, BN);
OSSL_FREE(ecKey, EC_KEY);
return r;
}
/**
* Convert a TPM public key into a PEM formatted byte buffer. This can be
* used by TLS libraries.
*
* @param[in] tpmPublicKey The public key created by the TPM
* @param[out] pemKey A byte buffer that will hold the PEM representation of
* the public key (callee allocated)
* @param[out] pemKeySize The size of pemKey in bytes
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_BAD_REFERENCE if tpmPublicKey or pemKeySize are NULL
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
TSS2_RC
ifapi_pub_pem_key_from_tpm(
const TPM2B_PUBLIC *tpmPublicKey,
char **pemKey,
int *pemKeySize)
{
/* Check for NULL parameters */
return_if_null(tpmPublicKey, "tpmPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(pemKeySize, "pemKeySize is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
EVP_PKEY *evpPublicKey = NULL;
BIO *bio = NULL;
TSS2_RC r = TPM2_RC_SUCCESS;
evpPublicKey = EVP_PKEY_new();
goto_if_null2(evpPublicKey, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup);
/* Memory IO will be used for OSSL key conversion */
bio = BIO_new(BIO_s_mem());
goto_if_null2(evpPublicKey, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup);
if (tpmPublicKey->publicArea.type == TPM2_ALG_RSA) {
r = ossl_rsa_pub_from_tpm(tpmPublicKey, evpPublicKey);
} else if (tpmPublicKey->publicArea.type == TPM2_ALG_ECC)
r = ossl_ecc_pub_from_tpm(tpmPublicKey, evpPublicKey);
else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid alg id.", cleanup);
}
goto_if_error(r, "Get ossl public key.", cleanup);
if (!PEM_write_bio_PUBKEY(bio, evpPublicKey)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "PEM_write_bio_PUBKEY",
cleanup);
}
/* Determine the size of the data written */
*pemKeySize = BIO_get_mem_data(bio, pemKey);
*pemKey = malloc(*pemKeySize+1);
goto_if_null(*pemKey, "Out of memory.", TSS2_FAPI_RC_MEMORY,
cleanup);
memset(*pemKey, 0, *pemKeySize + 1);
/* Get the byte buffer written to the BIO object */
int readSize = BIO_read(bio, *pemKey, *pemKeySize);
if (readSize != *pemKeySize) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid BIO_read",
cleanup);
}
cleanup:
EVP_PKEY_free(evpPublicKey);
BIO_free(bio);
return r;
}
/** Converts an ECDSA signature from a DER encoded byte buffer into the
* TPM format. It can then be verified by the TPM.
*
* @param[in] signature A DER encoded byte buffer holding the signature
* @param[in] signatureSize The size of signature in bytes
* @param[in] keySize The size of the verification key
* @param[in] hashAlgorithm The TSS identifier of the hash algorithm to use
* @param[out] tpmSignature The signature in the TPM format
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_BAD_REFERENCE if signature or tpmSignature is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
*/
static TSS2_RC
ifapi_ecc_der_sig_to_tpm(
const unsigned char *signature,
size_t signatureSize,
int keySize,
TPMI_ALG_HASH hashAlgorithm,
TPMT_SIGNATURE *tpmSignature)
{
/* Check for NULL parameters */
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmSignature, "tpmSignature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Initialize the ECDSA signature components */
ECDSA_SIG *ecdsaSignature = NULL;
#if OPENSSL_VERSION_NUMBER < 0x10100000
BIGNUM *bnr;
BIGNUM *bns;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
const BIGNUM *bnr;
const BIGNUM *bns;
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
d2i_ECDSA_SIG(&ecdsaSignature, &signature, signatureSize);
return_if_null(ecdsaSignature, "Invalid DER signature",
TSS2_FAPI_RC_GENERAL_FAILURE);
#if OPENSSL_VERSION_NUMBER < 0x10100000
bns = ecdsaSignature->s;
bnr = ecdsaSignature->r;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
ECDSA_SIG_get0(ecdsaSignature, &bnr, &bns);
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
/* Writing them to the TPM format signature */
tpmSignature->signature.ecdsa.hash = hashAlgorithm;
tpmSignature->sigAlg = TPM2_ALG_ECDSA; /**< only ECDSA is used by FAPI */
ifapi_bn2binpad(bnr, &tpmSignature->signature.ecdsa.signatureR.buffer[0],
keySize);
tpmSignature->signature.ecdsa.signatureR.size = keySize;
ifapi_bn2binpad(bns, &tpmSignature->signature.ecdsa.signatureS.buffer[0],
keySize);
tpmSignature->signature.ecdsa.signatureS.size = keySize;
OSSL_FREE(ecdsaSignature, ECDSA_SIG);
//OSSL_FREE(bnr, BN);
//OSSL_FREE(bns, BN);
return TSS2_RC_SUCCESS;
}
/** Convert signature from DER to TPM format.
*
* The signature in DER format is converted to TPM format to
* enable verification by the TPM.
*
* @param[in] tpmPublic The public information of the signature key
* @param[in] signature A byte buffer holding the DER encoded signature
* @param[in] signatureSize The size of signature in bytes
* @param[in] hashAlgorithm The TSS identifier for the hash algorithm used
* to compute the digest
* @param[out] tpmSignature The signature in TPM format
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_BAD_REFERENCE if tpmPublic, signature or tpmSignature is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
TSS2_RC
ifapi_der_sig_to_tpm(
const TPMT_PUBLIC *tpmPublic,
const unsigned char *signature,
size_t signatureSize,
TPMI_ALG_HASH hashAlgorithm,
TPMT_SIGNATURE *tpmSignature)
{
/* Check for NULL parameters */
return_if_null(tpmPublic, "tpmPublic is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmSignature, "tpmSignature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Convert the signature */
if (tpmPublic->type == TPM2_ALG_RSA) {
if (tpmPublic->parameters.rsaDetail.scheme.scheme == TPM2_ALG_RSAPSS) {
tpmSignature->sigAlg = TPM2_ALG_RSAPSS;
tpmSignature->signature.rsapss.hash = hashAlgorithm;
tpmSignature->signature.rsapss.sig.size = signatureSize;
memcpy(&tpmSignature->signature.rsapss.sig.buffer[0], signature,
signatureSize);
} else if (tpmPublic->parameters.rsaDetail.scheme.scheme == TPM2_ALG_RSASSA) {
tpmSignature->sigAlg = TPM2_ALG_RSASSA;
tpmSignature->signature.rsassa.hash = hashAlgorithm;
tpmSignature->signature.rsassa.sig.size = signatureSize;
memcpy(&tpmSignature->signature.rsassa.sig.buffer[0], signature,
signatureSize);
} else {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid RSA scheme.");
}
} else if (tpmPublic->type == TPM2_ALG_ECC) {
return ifapi_ecc_der_sig_to_tpm(signature, signatureSize,
tpmPublic->unique.ecc.x.size, hashAlgorithm,
tpmSignature);
} else {
return_error(TSS2_FAPI_RC_BAD_VALUE, "Invalid key tpye.");
}
return TSS2_RC_SUCCESS;
}
/**
* Size of the table with the possible padding schemes
*/
#define N_PADDING 2
/**
* Table with possible padding schemes to guess the one appropriate for
* for RSA signature verification
*/
static const int rsaPadding[N_PADDING] = { RSA_PKCS1_PADDING, RSA_PKCS1_PSS_PADDING };
/**
* Verifies an RSA signature given as a binary byte buffer.
*
* @param[in] publicKey The public key with which the signature is to be
* verified
* @param[in] signature A byte buffer holding the signature to verify
* @param[in] signatureSize The size of signature in bytes
* @param[in] digest The digest of the signature to verify
* @param[in] digestSize The size of digest in bytes. Required to determine the
* hash algorithm
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if publicKey, signature or digest is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if no hash algorithm that matches digestSize
* could be found
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED if the signature could not
* be verified
*/
static TSS2_RC
rsa_verify_signature(
EVP_PKEY *publicKey,
const uint8_t *signature,
size_t signatureSize,
const uint8_t *digest,
size_t digestSize)
{
/* Check for NULL parameters */
return_if_null(publicKey, "publicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(digest, "digest is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r;
const EVP_MD *mdType;
EVP_PKEY_CTX *ctx = NULL;
/* The hash algorithm of the signature is determined by the digest length */
switch (digestSize) {
case TPM2_SHA1_DIGEST_SIZE:
mdType = EVP_sha1();
break;
case TPM2_SHA256_DIGEST_SIZE:
mdType = EVP_sha256();
break;
case TPM2_SHA384_DIGEST_SIZE:
mdType = EVP_sha384();
break;
case TPM2_SHA512_DIGEST_SIZE:
mdType = EVP_sha512();
break;
default:
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Invalid digest size", cleanup);
}
/* Try all possible padding schemes for verification */
for (int i = 0; i < N_PADDING; i++) {
ctx = EVP_PKEY_CTX_new(publicKey, NULL);
if (!ctx) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Get pkey context.",
cleanup);
}
if (EVP_PKEY_verify_init(ctx) <= 0) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Verify init.",
cleanup);
}
if (EVP_PKEY_CTX_set_rsa_padding(ctx, rsaPadding[i]) <= 0) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"EVP_PKEY_CTX_set_rsa_padding", cleanup);
}
if (EVP_PKEY_CTX_set_signature_md(ctx, mdType) <= 0) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Verify set signature md.", cleanup);
}
if (1 != EVP_PKEY_verify(ctx, signature, signatureSize, digest, digestSize)) {
/* padding scheme was not appropriate, next should be tried */
EVP_PKEY_CTX_free(ctx);
ctx = NULL;
} else {
/* Verification with selected padding scheme was successful */
r = TSS2_RC_SUCCESS;
goto cleanup;
}
}
/* Verification was not successful with one of the possible padding schemes */
r = TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED;
cleanup:
if (ctx)
EVP_PKEY_CTX_free(ctx);
return r;
}
/**
* Verifies an ECDSA signature given as a binary byte buffer.
*
* @param[in] publicKey The public key with which the signature is to be
* verified
* @param[in] signature A byte buffer holding the signature to verify
* @param[in] signatureSize The size of signature in bytes
* @param[in] digest The digest of the signature to verify
* @param[in] digestSize The size of digest in bytes. Required to determine the
* hash algorithm
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if publicKey, signature or digest is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if no hash algorithm that matches digestSize
* could be found
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED if the signature could not
* be verified
*/
static TSS2_RC
ecdsa_verify_signature(
EVP_PKEY *publicKey,
const uint8_t *signature,
size_t signatureSize,
const uint8_t *digest,
size_t digestSize)
{
/* Check for NULL parameters */
return_if_null(publicKey, "publicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(digest, "digest is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
EC_KEY *eccKey = NULL;
eccKey = EVP_PKEY_get1_EC_KEY(publicKey);
/* Try to verify the signature using ECDSA, note that param 0 is unused */
int rc = ECDSA_verify(0, digest, digestSize, signature, signatureSize, eccKey);
if (rc == 0) {
goto_error(r, TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED,
"ECDSA signature verification failed.", error_cleanup);
} else if (rc < 0) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"ECDSA signature verification failed.", error_cleanup);
}
error_cleanup:
OSSL_FREE(eccKey, EC_KEY);
return r;
}
/**
* Gets an object with the TPM-relevant public information of an OpenSSL
* RSA public key.
*
* @param[in,out] profile The crypto profile from which parameters are retrieved
* @param[in] publicKey The public key for which the public information is
* retrieved
* @param[out] tpmPublic The public information of publicKey
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if profile, publicKey or tpmPublic is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
static TSS2_RC
get_rsa_tpm2b_public_from_evp(
EVP_PKEY *publicKey,
TPM2B_PUBLIC *tpmPublic)
{
/* Check for NULL parameters */
return_if_null(publicKey, "publicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmPublic, "tpmPublic is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Extract the public information */
TSS2_RC r = TSS2_RC_SUCCESS;
RSA *rsaKey = EVP_PKEY_get1_RSA(publicKey);
return_if_null(rsaKey, "Out of memory.", TSS2_FAPI_RC_MEMORY);
const BIGNUM *e = NULL, *n = NULL;
int rsaKeySize = RSA_size(rsaKey);
#if OPENSSL_VERSION_NUMBER < 0x10100000
e = rsaKey->e;
n = rsaKey->n;
#else /* OPENSSL_VERSION_NUMBER < 0x10100000 */
RSA_get0_key(rsaKey, &n, &e, NULL);
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000 */
tpmPublic->publicArea.unique.rsa.size = rsaKeySize;
if (1 != ifapi_bn2binpad(n, &tpmPublic->publicArea.unique.rsa.buffer[0],
rsaKeySize)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Write big num byte buffer", cleanup);
}
tpmPublic->publicArea.parameters.rsaDetail.keyBits = rsaKeySize * 8;
tpmPublic->publicArea.parameters.rsaDetail.exponent = BN_get_word(e);
cleanup:
OSSL_FREE(rsaKey, RSA);
return r;
}
/**
* Gets an object with the TPM-relevant public information of an OpenSSL
* ECC public key.
*
* @param[in,out] profile The crypto profile to retrieve parameters from.
* @param[in] publicKey The public key for which the public information is
* retrieved
* @param[out] tpmPublic The public information of publicKey
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if profile, publicKey or tpmPublic is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
static TSS2_RC
get_ecc_tpm2b_public_from_evp(
EVP_PKEY *publicKey,
TPM2B_PUBLIC *tpmPublic)
{
/* Check for NULL parameters */
return_if_null(publicKey, "publicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmPublic, "tpmPublic is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Initialize variables that will contain the relevant information */
TSS2_RC r = TSS2_RC_SUCCESS;
EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(publicKey);
return_if_null(ecKey, "Out of memory.", TSS2_FAPI_RC_MEMORY);
const EC_GROUP *ecGroup;
const EC_POINT *publicPoint;
int curveId;
size_t ecKeySize;
BIGNUM *bnX = NULL;
BIGNUM *bnY = NULL;
TPMI_ECC_CURVE tpmCurveId;
if (!ecKey) {
return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "No ECC key!");
}
/* Retrieve the relevant information and write it to tpmPublic */
ecGroup = EC_KEY_get0_group(ecKey);
publicPoint = EC_KEY_get0_public_key(ecKey);
curveId = EC_GROUP_get_curve_name(ecGroup);
ecKeySize = EC_GROUP_get_degree(ecGroup) / 8;
tpmPublic->publicArea.unique.ecc.x.size = ecKeySize;
tpmPublic->publicArea.unique.ecc.y.size = ecKeySize;
if (!(bnX = BN_new())) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Create bignum", cleanup);
}
if (!(bnY = BN_new())) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Create bignum", cleanup);
}
if (1 != EC_POINT_get_affine_coordinates_tss(ecGroup, publicPoint,
bnX, bnY, NULL)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Get affine coordinates", cleanup);
}
if (1 != ifapi_bn2binpad(bnX, &tpmPublic->publicArea.unique.ecc.x.buffer[0],
ecKeySize)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Write big num byte buffer", cleanup);
}
if (1 != ifapi_bn2binpad(bnY, &tpmPublic->publicArea.unique.ecc.y.buffer[0],
ecKeySize)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Write big num byte buffer", cleanup);
}
switch (curveId) {
case NID_X9_62_prime192v1:
tpmCurveId = TPM2_ECC_NIST_P192;
break;
case NID_secp224r1:
tpmCurveId = TPM2_ECC_NIST_P224;
break;
case NID_X9_62_prime256v1:
tpmCurveId = TPM2_ECC_NIST_P256;
break;
case NID_secp384r1:
tpmCurveId = TPM2_ECC_NIST_P384;
break;
case NID_secp521r1:
tpmCurveId = TPM2_ECC_NIST_P521;
break;
default:
goto_error(r, TSS2_FAPI_RC_BAD_VALUE,
"Curve %x not implemented", cleanup, curveId);
}
tpmPublic->publicArea.parameters.eccDetail.curveID = tpmCurveId;
cleanup:
OSSL_FREE(ecKey, EC_KEY);
OSSL_FREE(bnX, BN);
OSSL_FREE(bnY, BN);
return r;
}
/**
* Converts a given PEM key into an EVP public key object.
*
* @param[in] pemKey A byte buffer holding the PEM key to convert
* @param[out] publicKey An EVP public key
*
* @retval TSS2_FAPI_RC_BAD_REFERENCE if any of the parameters is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_BAD_VALUE if the PEM key could not be decoded
*/
static TSS2_RC
ifapi_get_evp_from_pem(const char *pemKey, EVP_PKEY **publicKey) {
/* Check for NULL parameters */
return_if_null(pemKey, "pemKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(publicKey, "publicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
BIO *bufio = NULL;
/* Use BIO for conversion */
bufio = BIO_new_mem_buf((void *)pemKey, strlen(pemKey));
goto_if_null(bufio, "BIO buffer could not be allocated.",
TSS2_FAPI_RC_MEMORY, cleanup);
/* Convert the key */
*publicKey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
goto_if_null(*publicKey, "PEM format could not be decoded.",
TSS2_FAPI_RC_BAD_VALUE, cleanup);
cleanup:
BIO_free(bufio);
return r;
}
/**
* Returns the TPM algorithm identifier that matches to the signature algorithm
* of a given PEM key.
*
* @param[in] pemKey The public key from which the signature algorithm is retrieved
*
* @retval TPM2_ALG_RSA if pemKey holds an RSA key
* @retval TPM2_ALG_ECC if pemKey holds an ECC key
* @retval TPM2_ALG_ERROR if the signature algorithm could not be determined
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
TPM2_ALG_ID
ifapi_get_signature_algorithm_from_pem(const char *pemKey) {
/* Check for NULL parameters */
return_if_null(pemKey, "pemKey is NULL", TPM2_ALG_ERROR);
/* Get an EVP object for the key */
EVP_PKEY * publicKey = NULL;
TPM2_ALG_ID algorithmId = TPM2_ALG_ERROR;
TSS2_RC r = ifapi_get_evp_from_pem(pemKey, &publicKey);
if (r != TSS2_RC_SUCCESS || publicKey == NULL) {
LOG_ERROR("Could not get an EVP key from the PEM key");
algorithmId = TPM2_ALG_ERROR;
goto cleanup;
}
/* Determine the signature algorithm of the converted key */
if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_RSA) {
algorithmId = TPM2_ALG_RSA;
} else if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_EC) {
algorithmId = TPM2_ALG_ECC;
} else {
algorithmId = TPM2_ALG_ERROR;
}
cleanup:
OSSL_FREE(publicKey, EVP_PKEY);
return algorithmId;
}
/**
* Gets an object with the TPM-relevant public information of a PEM encoded
* public key. The information is gathered from the key itself and the currently
* used FAPI profile.
*
* @param[in] pemKey A byte buffer holding the PEM encoded public key for
* which the public information is retrieved
* @param[out] tpmPublic The public information of pemKey
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if profile, pemKey or tpmPublic is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
TSS2_RC
ifapi_get_tpm2b_public_from_pem(
const char *pemKey,
TPM2B_PUBLIC *tpmPublic)
{
/* Check for NULL parameters */
return_if_null(pemKey, "pemKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(tpmPublic, "public is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
EVP_PKEY *publicKey = NULL;
r = ifapi_get_evp_from_pem(pemKey, &publicKey);
goto_if_error(r, "Get EVP key from PEM", cleanup);
if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_RSA) {
tpmPublic->publicArea.type = TPM2_ALG_RSA;
r = get_rsa_tpm2b_public_from_evp(publicKey, tpmPublic);
goto_if_error(r, "Get public for RSA key.", cleanup);
} else if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_EC) {
tpmPublic->publicArea.type = TPM2_ALG_ECC;
r = get_ecc_tpm2b_public_from_evp(publicKey, tpmPublic);
goto_if_error(r, "Get public for ECC key.", cleanup);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong key_type", cleanup);
}
cleanup:
OSSL_FREE(publicKey, EVP_PKEY);
return r;
}
/**
* Verifies the signature created by a Quote command.
*
* @param[in] keyObject A FAPI key with which the signature is verified
* @param[in] signature A byte buffer holding the signature
* @param[in] signatureSize The size of signature in bytes
* @param[in] digest The digest of the signature
* @param[in] digestSize The size of digest in bytes
* @param[in] signatureScheme The signature scheme
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if keyObject, signature, digest
* or signatureScheme is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_BAD_VALUE if the PEM encoded key could not be decoded
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED if the verification of the
* signature fails
*/
TSS2_RC
ifapi_verify_signature_quote(
const IFAPI_OBJECT *keyObject,
const uint8_t *signature,
size_t signatureSize,
const uint8_t *digest,
size_t digestSize,
const TPMT_SIG_SCHEME *signatureScheme)
{
/* Check for NULL parameters */
return_if_null(keyObject, "keyObject is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(digest, "digest is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signatureScheme, "signatureScheme is NULL",
TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
char *public_pem_key = NULL;
int pem_size;
EVP_PKEY *publicKey = NULL;
BIO *bufio = NULL;
EVP_PKEY_CTX *pctx = NULL;
EVP_MD_CTX *mdctx = NULL;
/* Check whether or not the key is valid */
if (keyObject->objectType == IFAPI_KEY_OBJ) {
/* Compute public key */
r = ifapi_pub_pem_key_from_tpm(&keyObject->misc.key.public, &public_pem_key,
&pem_size);
goto_if_error(r, "Compute public PEM key.", error_cleanup);
} else if (keyObject->objectType == IFAPI_EXT_PUB_KEY_OBJ) {
public_pem_key = strdup(keyObject->misc.ext_pub_key.pem_ext_public);
check_oom(public_pem_key);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong object type",
error_cleanup);
}
/* Create an OpenSSL object for the key */
bufio = BIO_new_mem_buf((void *)public_pem_key,
strlen(public_pem_key));
goto_if_null(bufio, "BIO buffer could not be allocated.",
TSS2_FAPI_RC_MEMORY, error_cleanup);
publicKey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
goto_if_null(publicKey, "PEM format could not be decoded.",
TSS2_FAPI_RC_BAD_VALUE, error_cleanup);
/* Create the hash engine */
if (!(mdctx = EVP_MD_CTX_create())) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "EVP_MD_CTX_create",
error_cleanup);
}
const EVP_MD *hashAlgorithm = get_hash_md(signatureScheme->details.any.hashAlg);
if (!hashAlgorithm) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid hash alg.",
error_cleanup);
}
/* Verify the digest of the signature */
if (1 != EVP_DigestVerifyInit(mdctx, &pctx, hashAlgorithm, NULL, publicKey)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "EVP_DigestVerifyInit",
error_cleanup);
}
goto_if_null(pctx, "Out of memory", TSS2_FAPI_RC_MEMORY, error_cleanup);
if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_RSA) {
int padding = get_sig_scheme(signatureScheme->scheme);
if (!padding) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Invalid padding scheme.", error_cleanup);
}
if (1 != EVP_PKEY_CTX_set_rsa_padding(pctx, padding)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"EVP_PKEY_CTX_set_rsa_padding", error_cleanup);
}
}
if (1 != EVP_DigestVerifyUpdate(mdctx, digest, digestSize)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"EVP_DigestVerifyUpdate", error_cleanup);
}
if (1 != EVP_DigestVerifyFinal(mdctx, signature, signatureSize)) {
goto_error(r, TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED,
"EVP_DigestSignFinal", error_cleanup);
}
error_cleanup:
if (mdctx != NULL) {
EVP_MD_CTX_destroy(mdctx);
}
SAFE_FREE(public_pem_key);
EVP_PKEY_free(publicKey);
BIO_free(bufio);
return r;
}
/**
* Verifies a signature using a given FAPI public key.
*
* @param[in] keyObject The FAPI public key used for verification
* @param[in] signature The signature to verify
* @param[in] signatureSize The size of signature in bytes
* @param[in] digest The digest of the signature
* @param[in] digestSize The size of digest in bytes
*
* @retval TSS2_RC_SUCCESS In case of success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if keyObject, signature or digest is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if the type of the key is wrong
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_SIGNATURE_VERIFICATION_FAILED if the verification of the
* signature fails
*
*/
TSS2_RC
ifapi_verify_signature(
const IFAPI_OBJECT *keyObject,
const uint8_t *signature,
size_t signatureSize,
const uint8_t *digest,
size_t digestSize)
{
/* Check for NULL parameters */
return_if_null(keyObject, "keyObject is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(signature, "signature is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(digest, "digest is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
char *public_pem_key = NULL;
int pem_size;
EVP_PKEY *publicKey = NULL;
BIO *bufio = NULL;
/* Check whether or not the key is valid */
if (keyObject->objectType == IFAPI_KEY_OBJ) {
/* Compute public key */
r = ifapi_pub_pem_key_from_tpm(&keyObject->misc.key.public, &public_pem_key,
&pem_size);
goto_if_error(r, "Compute public PEM key.", error_cleanup);
} else if (keyObject->objectType == IFAPI_EXT_PUB_KEY_OBJ) {
public_pem_key = strdup(keyObject->misc.ext_pub_key.pem_ext_public);
check_oom(public_pem_key);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong object type",
error_cleanup);
}
/* Convert the key to an OpenSSL object */
bufio = BIO_new_mem_buf((void *)public_pem_key,
strlen(public_pem_key));
goto_if_null(bufio, "Out of memory.", TSS2_FAPI_RC_MEMORY, error_cleanup);
publicKey = PEM_read_bio_PUBKEY(bufio, NULL, NULL, NULL);
goto_if_null(publicKey, "PEM format could not be decoded.",
TSS2_FAPI_RC_MEMORY, error_cleanup);
/* Call a suitable local function for the verification */
if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_RSA) {
r = rsa_verify_signature(publicKey, signature, signatureSize, digest,
digestSize);
goto_if_error(r, "Verify RSA signature.", error_cleanup);
} else if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_EC) {
r = ecdsa_verify_signature(publicKey, signature, signatureSize,
digest, digestSize);
goto_if_error(r, "Verify ECC signature", error_cleanup);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong key type",
error_cleanup);
}
error_cleanup:
SAFE_FREE(public_pem_key);
EVP_PKEY_free(publicKey);
if (bufio)
BIO_free(bufio);
return r;
}
/**
* Returns the digest size of a given hash algorithm.
*
* @param[in] hashAlgorithm The TSS identifier of the hash algorithm
*
* @return The size of the digest produced by the hash algorithm if
* hashAlgorithm is valid
* @retval 0 if hashAlgorithm is invalid
*/
size_t
ifapi_hash_get_digest_size(TPM2_ALG_ID hashAlgorithm)
{
switch (hashAlgorithm) {
case TPM2_ALG_SHA1:
return TPM2_SHA1_DIGEST_SIZE;
break;
case TPM2_ALG_SHA256:
return TPM2_SHA256_DIGEST_SIZE;
break;
case TPM2_ALG_SHA384:
return TPM2_SHA384_DIGEST_SIZE;
break;
case TPM2_ALG_SHA512:
return TPM2_SHA512_DIGEST_SIZE;
break;
case TPM2_ALG_SM3_256:
return TPM2_SM3_256_DIGEST_SIZE;
break;
default:
return 0;
}
}
/**
* Converts a TSS hash algorithm identifier into an OpenSSL hash algorithm
* identifier object.
*
* @param[in] hashAlgorithm The TSS hash algorithm identifier to convert
*
* @retval A suitable OpenSSL identifier object if one could be found
* @retval NULL if no suitable identifier object could be found
*/
static const EVP_MD *
get_ossl_hash_md(TPM2_ALG_ID hashAlgorithm)
{
switch (hashAlgorithm) {
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;
}
}
/**
* Starts the computation of a hash digest.
*
* @param[out] context The created hash context (callee-allocated).
* @param[in] hashAlgorithm The TSS hash identifier for the hash algorithm to use.
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_VALUE if hashAlgorithm is invalid
* @retval TSS2_FAPI_RC_BAD_REFERENCE if context is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory cannot be allocated
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
TSS2_RC
ifapi_crypto_hash_start(IFAPI_CRYPTO_CONTEXT_BLOB **context,
TPM2_ALG_ID hashAlgorithm)
{
/* Check for NULL parameters */
return_if_null(context, "context is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Initialize the hash context */
TSS2_RC r = TSS2_RC_SUCCESS;
LOG_DEBUG("call: context=%p hashAlg=%" PRIu16, context, hashAlgorithm);
IFAPI_CRYPTO_CONTEXT *mycontext = NULL;
mycontext = calloc(1, sizeof(IFAPI_CRYPTO_CONTEXT));
return_if_null(mycontext, "Out of memory", TSS2_FAPI_RC_MEMORY);
if (!(mycontext->osslHashAlgorithm = get_ossl_hash_md(hashAlgorithm))) {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE,
"Unsupported hash algorithm (%" PRIu16 ")", cleanup,
hashAlgorithm);
}
if (!(mycontext->hashSize = ifapi_hash_get_digest_size(hashAlgorithm))) {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE,
"Unsupported hash algorithm (%" PRIu16 ")", cleanup,
hashAlgorithm);
}
if (!(mycontext->osslContext = EVP_MD_CTX_create())) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Error EVP_MD_CTX_create",
cleanup);
}
if (1 != EVP_DigestInit_ex(mycontext->osslContext,
mycontext->osslHashAlgorithm, get_engine())) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Error EVP_DigestInit_ex",
cleanup);
}
*context = (IFAPI_CRYPTO_CONTEXT_BLOB *) mycontext;
return TSS2_RC_SUCCESS;
cleanup:
if (mycontext->osslContext)
EVP_MD_CTX_destroy(mycontext->osslContext);
SAFE_FREE(mycontext);
return r;
}
/**
* Updates the digest value of a hash object with data from a byte buffer.
*
* @param[in,out] context The hash context that will be updated
* @param[in] buffer The data for the update
* @param[in] size The size of data in bytes
*
* @retval TSS2_RC_SUCCESS on success.
* @retval TSS2_FAPI_RC_BAD_REFERENCE for invalid parameters.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
TSS2_RC
ifapi_crypto_hash_update(IFAPI_CRYPTO_CONTEXT_BLOB *context,
const uint8_t *buffer, size_t size)
{
/* Check for NULL parameters */
return_if_null(context, "context is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(buffer, "buffer is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
LOG_DEBUG("called for context %p, buffer %p and size %zd", context, buffer,
size);
/* Update the digest */
IFAPI_CRYPTO_CONTEXT *mycontext = (IFAPI_CRYPTO_CONTEXT *) context;
LOGBLOB_DEBUG(buffer, size, "Updating hash with");
if (1 != EVP_DigestUpdate(mycontext->osslContext, buffer, size)) {
return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "OSSL hash update");
}
return TSS2_RC_SUCCESS;
}
/**
* Gets the digest value from a hash context and closes it.
*
* @param[in,out] context The hash context that is released
* @param[out] digest The buffer for the digest value
* @param[out] digestSize The size of digest in bytes. Can be NULL
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if context or digest is NULL
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
TSS2_RC
ifapi_crypto_hash_finish(IFAPI_CRYPTO_CONTEXT_BLOB **context,
uint8_t *digest, size_t *digestSize)
{
/* Check for NULL parameters */
return_if_null(context, "context is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(digest, "digest is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
unsigned int computedDigestSize = 0;
LOG_TRACE("called for context-pointer %p, digest %p and size-pointer %p",
context, digest, digestSize);
/* Compute the digest */
IFAPI_CRYPTO_CONTEXT *mycontext = *context;
if (1 != EVP_DigestFinal_ex(mycontext->osslContext, digest,
&computedDigestSize)) {
return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "OSSL error.");
}
if (computedDigestSize != mycontext->hashSize) {
return_error(TSS2_FAPI_RC_GENERAL_FAILURE,
"Invalid size computed by EVP_DigestFinal_ex");
}
LOGBLOB_DEBUG(digest, mycontext->hashSize, "finish hash");
if (digestSize != NULL) {
*digestSize = mycontext->hashSize;
}
/* Finalize the hash context */
EVP_MD_CTX_destroy(mycontext->osslContext);
free(mycontext);
*context = NULL;
return TSS2_RC_SUCCESS;
}
/**
* Aborts a hash operation and finalizes the hash context. It will be set to
* NULL.
*
* @param[in,out] context The context of the digest object.
*/
void
ifapi_crypto_hash_abort(IFAPI_CRYPTO_CONTEXT_BLOB **context)
{
LOG_TRACE("called for context-pointer %p", context);
if (context == NULL || *context == NULL) {
LOG_DEBUG("Null-Pointer passed");
return;
}
IFAPI_CRYPTO_CONTEXT *mycontext = (IFAPI_CRYPTO_CONTEXT *) * context;
EVP_MD_CTX_destroy(mycontext->osslContext);
free(mycontext);
*context = NULL;
}
/**
* Get url to download crl from certificate.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_NO_CERT if an error did occur during certificate downloading.
*/
TSS2_RC
get_crl_from_cert(X509 *cert, X509_CRL **crl)
{
TSS2_RC r = TSS2_RC_SUCCESS;
unsigned char* url = NULL;
unsigned char *crl_buffer = NULL;
size_t crl_buffer_size;
int nid = NID_crl_distribution_points;
STACK_OF(DIST_POINT) * dist_points = (STACK_OF(DIST_POINT) *)X509_get_ext_d2i(cert, nid, NULL, NULL);
int curl_rc;
*crl = NULL;
for (int i = 0; i < sk_DIST_POINT_num(dist_points); i++)
{
DIST_POINT *dp = sk_DIST_POINT_value(dist_points, i);
DIST_POINT_NAME *distpoint = dp->distpoint;
if (distpoint->type==0)
{
for (int j = 0; j < sk_GENERAL_NAME_num(distpoint->name.fullname); j++)
{
GENERAL_NAME *gen_name = sk_GENERAL_NAME_value(distpoint->name.fullname, j);
ASN1_IA5STRING *asn1_str = gen_name->d.uniformResourceIdentifier;
SAFE_FREE(url);
url = (unsigned char *)strdup((char *)asn1_str->data);
goto_if_null2(url, "Out of memory", r, TSS2_FAPI_RC_MEMORY, cleanup);
}
}
}
/* No CRL dist point in the cert is legitimate */
if (url == NULL) {
goto cleanup;
}
curl_rc = ifapi_get_curl_buffer(url, &crl_buffer, &crl_buffer_size);
if (curl_rc != 0) {
goto_error(r, TSS2_FAPI_RC_NO_CERT, "Get crl.", cleanup);
}
OpenSSL_add_all_algorithms();
unsigned const char* tmp_ptr1 = crl_buffer;
unsigned const char** tmp_ptr2 = &tmp_ptr1;
if (!d2i_X509_CRL(crl, tmp_ptr2, crl_buffer_size)) {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Can't convert crl.", cleanup);
}
cleanup:
SAFE_FREE(crl_buffer);
CRL_DIST_POINTS_free(dist_points);
SAFE_FREE(url);
return r;
}
/**
* Converts a TPM certificate buffer to the PEM format.
*
* @param[in] certBuffer A byte buffer holding the certificate
* @param[in] certBufferSize The size of certBuffer in bytes
* @param[out] pemCert A byte buffer where the PEM-formatted certificate is
* stored
* @param[out] certAlgorithmId The key type of the certified key
* @param[out] tpmPublic The public key of the certificate in TPM format.
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if certBuffer or pemCert is NULL
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_RC_BAD_VALUE if the certificate is invalid
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
*/
TSS2_RC
ifapi_cert_to_pem(
const uint8_t *certBuffer,
size_t certBufferSize,
char **pemCert,
TPM2_ALG_ID *certAlgorithmId,
TPM2B_PUBLIC *tpmPublic)
{
/* Check for NULL parameters */
return_if_null(certBuffer, "certBuffer is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
return_if_null(pemCert, "pemCert is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
TSS2_RC r = TSS2_RC_SUCCESS;
X509 *cert = NULL;
BIO *bio = NULL;
EVP_PKEY *publicKey = NULL;
int pemCertSize;
if (!d2i_X509(&cert, (const unsigned char **)&certBuffer, certBufferSize)) {
LOGBLOB_ERROR(certBuffer, certBufferSize, "Bad certificate data");
return_error(TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid certificate.");
}
*pemCert = NULL;
/* Memory IO will be used for OSSL key conversion */
bio = BIO_new(BIO_s_mem());
return_if_null(bio, "Out of memory.", TSS2_FAPI_RC_MEMORY);
if (!PEM_write_bio_X509(bio, cert)) {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "PEM_write_bio_X509", cleanup);
}
/* Determine the size of the data written */
pemCertSize = BIO_get_mem_data(bio, pemCert);
*pemCert = malloc(pemCertSize + 1);
goto_if_null(pemCert, "Out of memory.", TSS2_FAPI_RC_MEMORY, cleanup);
/* Get the byte buffer written to the BIO object */
int readSize = BIO_read(bio, *pemCert, pemCertSize);
if (readSize != pemCertSize) {
SAFE_FREE(*pemCert);
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "Invalid BIO_read",
cleanup);
}
(*pemCert)[pemCertSize] = '\0';
publicKey = X509_get_pubkey(cert);
goto_if_null(publicKey, "No public key in certificate.",
TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_RSA) {
tpmPublic->publicArea.type = TPM2_ALG_RSA;
r = get_rsa_tpm2b_public_from_evp(publicKey, tpmPublic);
goto_if_error(r, "Get public for RSA key.", cleanup);
} else if (EVP_PKEY_type(EVP_PKEY_id(publicKey)) == EVP_PKEY_EC) {
tpmPublic->publicArea.type = TPM2_ALG_ECC;
r = get_ecc_tpm2b_public_from_evp(publicKey, tpmPublic);
goto_if_error(r, "Get public for ECC key.", cleanup);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong key_type", cleanup);
}
if (certAlgorithmId != NULL) {
switch (EVP_PKEY_id(publicKey)) {
case EVP_PKEY_RSA:
*certAlgorithmId = TPM2_ALG_RSA;
break;
case EVP_PKEY_EC:
*certAlgorithmId = TPM2_ALG_ECC;
break;
default:
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong certificate (key type).",
cleanup);
}
}
cleanup:
BIO_free(bio);
OSSL_FREE(cert, X509);
OSSL_FREE(publicKey, EVP_PKEY);
return r;
}
/**
* Returns a suitable hash algorithm for a given digest size.
*
* @param[in] size The size of the digest
* @param[out] hashAlgorithm A suitable hash algorithm for the digest size
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_REFERENCE if hashAlgorithm is NULL
* @retval TSS2_FAPI_RC_BAD_VALUE if the digest size is invalid
*/
TSS2_RC
ifapi_get_hash_alg_for_size(uint16_t size, TPMI_ALG_HASH *hashAlgorithm)
{
/* Check for NULL parameters */
return_if_null(hashAlgorithm, "hashAlgorithm is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
/* Determine the hash algorithm */
switch (size) {
case TPM2_SHA1_DIGEST_SIZE:
*hashAlgorithm = TPM2_ALG_SHA1;
return TSS2_RC_SUCCESS;
case TPM2_SHA256_DIGEST_SIZE:
*hashAlgorithm = TPM2_ALG_SHA256;
return TSS2_RC_SUCCESS;
case TPM2_SHA384_DIGEST_SIZE:
*hashAlgorithm = TPM2_ALG_SHA384;
return TSS2_RC_SUCCESS;
case TPM2_SHA512_DIGEST_SIZE:
*hashAlgorithm = TPM2_ALG_SHA512;
return TSS2_RC_SUCCESS;
default:
return TSS2_FAPI_RC_BAD_VALUE;
}
}
/** Convert PEM certificate to OSSL format.
*
* @param[in] pem_cert Certificate in PEM format.
* @retval X509 OSSL certificate object.
* @retval NULL If the conversion fails.
*/
static X509
*get_X509_from_pem(const char *pem_cert)
{
if (!pem_cert) {
return NULL;
}
BIO *bufio = NULL;
X509 *cert = NULL;
/* Use BIO for conversion */
size_t pem_length = strlen(pem_cert);
bufio = BIO_new_mem_buf((void *)pem_cert, pem_length);
if (!bufio)
return NULL;
/* Convert the certificate */
cert = PEM_read_bio_X509(bufio, NULL, NULL, NULL);
BIO_free(bufio);
return cert;
}
/** Get public information for key of a pem certificate.
*
* @param[in] pem_cert The pem certificate.
* @param[out] tpm_public The public information of the key in TPM format.
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_VALUE if the conversion fails.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if openssl errors occur.
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
*/
TSS2_RC
ifapi_get_public_from_pem_cert(const char* pem_cert, TPM2B_PUBLIC *tpm_public)
{
TSS2_RC r = TSS2_RC_SUCCESS;
X509 *cert = NULL;
EVP_PKEY *public_key = NULL;
cert = get_X509_from_pem(pem_cert);
return_if_null(cert, "Invalid certificate.", TSS2_FAPI_RC_BAD_VALUE);
public_key = X509_get_pubkey(cert);
goto_if_null(public_key, "No public key in certificate.",
TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
if (EVP_PKEY_type(EVP_PKEY_id(public_key)) == EVP_PKEY_RSA) {
tpm_public->publicArea.type = TPM2_ALG_RSA;
r = get_rsa_tpm2b_public_from_evp(public_key, tpm_public);
goto_if_error(r, "Get public for RSA key.", cleanup);
} else if (EVP_PKEY_type(EVP_PKEY_id(public_key)) == EVP_PKEY_EC) {
tpm_public->publicArea.type = TPM2_ALG_ECC;
r = get_ecc_tpm2b_public_from_evp(public_key, tpm_public);
goto_if_error(r, "Get public for ECC key.", cleanup);
} else {
goto_error(r, TSS2_FAPI_RC_BAD_VALUE, "Wrong key_type", cleanup);
}
cleanup:
OSSL_FREE(cert, X509);
OSSL_FREE(public_key, EVP_PKEY);
return r;
}
/** Convert buffer from DER format to X509 certificate.
*
* @param[in] cert_buffer Certificate in DER format.
* @aparm[in] cert_buffer_size Size of DER certificate.
* @retval X509 OSSL certificate object.
* @retval NULL If the conversion fails.
*/
static X509
*get_cert_from_buffer(unsigned char *cert_buffer, size_t cert_buffer_size)
{
unsigned char *buffer = cert_buffer;
X509 *cert = NULL;
unsigned const char* tmp_ptr1 = buffer;
unsigned const char** tmp_ptr2 = &tmp_ptr1;
if (!d2i_X509(&cert, tmp_ptr2, cert_buffer_size))
return NULL;
return cert;
}
/**
* Verify EK certificate read from TPM.
*
* @param[in] root_cert_pem The vendor root certificate.
* @param[in] intermed_cert_pem The vendor intermediate certificate.
* @param[in] ek_cert_pem The ek certificate from TPM.
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_BAD_VALUE if the verification was no successful.
* @retval TSS2_FAPI_RC_NO_CERT if an error did occur during certificate downloading.
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an internal error occurred.
* @retval TSS2_FAPI_RC_MEMORY if not enough memory can be allocated.
*/
TSS2_RC
ifapi_verify_ek_cert(
char* root_cert_pem,
char* intermed_cert_pem,
char* ek_cert_pem)
{
TSS2_RC r = TSS2_RC_SUCCESS;
X509 *root_cert = NULL;
X509 *intermed_cert = NULL;
X509 *ek_cert = NULL;
X509_STORE *store = NULL;
X509_STORE_CTX *ctx = NULL;
X509_CRL *crl_intermed = NULL;
X509_CRL *crl_ek = NULL;
int i;
size_t ui;
AUTHORITY_INFO_ACCESS *info = NULL;
ASN1_IA5STRING *uri = NULL;
unsigned char * url;
unsigned char *cert_buffer = NULL;
size_t cert_buffer_size;
int curl_rc;
ek_cert = get_X509_from_pem(ek_cert_pem);
goto_if_null2(ek_cert, "Failed to convert PEM certificate to DER.",
r, TSS2_FAPI_RC_BAD_VALUE, cleanup);
if (intermed_cert_pem) {
intermed_cert = get_X509_from_pem(intermed_cert_pem);
goto_if_null2(intermed_cert, "Failed to convert PEM certificate to DER.",
r, TSS2_FAPI_RC_BAD_VALUE, cleanup);
} else {
/* Get uri for ek intermediate certificate. */
OpenSSL_add_all_algorithms();
info = X509_get_ext_d2i(ek_cert, NID_info_access, NULL, NULL);
for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) {
ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i);
if (ad->location->type != GEN_URI) {
continue;
}
uri = ad->location->d.uniformResourceIdentifier;
url = uri->data;
curl_rc = ifapi_get_curl_buffer(url, &cert_buffer, &cert_buffer_size);
if (curl_rc != 0) {
goto_error(r, TSS2_FAPI_RC_NO_CERT, "Get certificate.", cleanup);
}
goto_if_null2(cert_buffer, "No certificate downloaded", r,
TSS2_FAPI_RC_NO_CERT, cleanup);
}
goto_if_null2(cert_buffer, "No certificate downloaded", r,
TSS2_FAPI_RC_NO_CERT, cleanup);
OpenSSL_add_all_algorithms();
intermed_cert = get_cert_from_buffer(cert_buffer, cert_buffer_size);
SAFE_FREE(cert_buffer);
goto_if_null2(intermed_cert, "Failed to create intermediate certificate.",
r, TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
/* Get Certificate revocation list for Intermediate certificate */
r = get_crl_from_cert(intermed_cert, &crl_intermed);
goto_if_error(r, "Get crl for intermediate certificate.", cleanup);
/* Get Certificate revocation list for EK certificate */
r = get_crl_from_cert(ek_cert, &crl_ek);
goto_if_error(r, "Get crl for ek certificate.", cleanup);
}
/* Prepare X509 certificate store */
store = X509_STORE_new();
goto_if_null2(store, "Failed to create X509 store.",
r, TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
/* Add Certificate revocation list for EK certificate if one exists. */
if (crl_ek) {
/* Set the flags of the store to use CRLs. */
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
if (1 != X509_STORE_add_crl(store, crl_ek)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to add intermediate crl.", cleanup);
}
}
/* Add Certificate revocation list for intermediate certificate if one exists. */
if (crl_intermed) {
/* Set the flags of the store to use CRLs. */
X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
if (1 != X509_STORE_add_crl(store, crl_intermed)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to add intermediate crl.", cleanup);
}
}
/* Add stored root certificates */
for (ui = 0; ui < sizeof(root_cert_list) / sizeof(char *); ui++) {
root_cert = get_X509_from_pem(root_cert_list[ui]);
goto_if_null2(root_cert, "Failed to convert PEM certificate to DER.",
r, TSS2_FAPI_RC_BAD_VALUE, cleanup);
if (1 != X509_STORE_add_cert(store, root_cert)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to add root certificate", cleanup);
}
OSSL_FREE(root_cert, X509);
}
/* Create root cert if passed as parameter */
if (root_cert_pem) {
root_cert = get_X509_from_pem(root_cert_pem);
goto_if_null2(root_cert, "Failed to convert PEM certificate to DER.",
r, TSS2_FAPI_RC_BAD_VALUE, cleanup);
if (1 != X509_STORE_add_cert(store, root_cert)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to add root certificate", cleanup);
}
OSSL_FREE(root_cert, X509);
}
/* Verify intermediate certificate */
ctx = X509_STORE_CTX_new();
goto_if_null2(ctx, "Failed to create X509 store context.",
r, TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
if (1 != X509_STORE_CTX_init(ctx, store, intermed_cert, NULL)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to initialize X509 context.", cleanup);
}
if (1 != X509_verify_cert(ctx)) {
LOG_ERROR("%s", X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)));
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to verify EK certificate", cleanup);
}
if (1 != X509_STORE_add_cert(store, intermed_cert)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to add intermediate certificate", cleanup);
}
X509_STORE_CTX_cleanup(ctx);
X509_STORE_CTX_free(ctx);
ctx = NULL;
ctx = X509_STORE_CTX_new();
goto_if_null2(ctx, "Failed to create X509 store context.",
r, TSS2_FAPI_RC_GENERAL_FAILURE, cleanup);
if (1 != X509_STORE_CTX_init(ctx, store, ek_cert, NULL)) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to initialize X509 context.", cleanup);
}
/* Verify the EK certificate. */
if (1 != X509_verify_cert(ctx)) {
LOG_ERROR("%s", X509_verify_cert_error_string(X509_STORE_CTX_get_error(ctx)));
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE,
"Failed to verify EK certificate", cleanup);
}
cleanup:
if (ctx) {
X509_STORE_CTX_cleanup(ctx);
X509_STORE_CTX_free(ctx);
}
if (store)
X509_STORE_free(store);
OSSL_FREE(root_cert, X509);
OSSL_FREE(intermed_cert, X509);
OSSL_FREE(ek_cert, X509);
OSSL_FREE(crl_intermed, X509_CRL);
OSSL_FREE(crl_ek, X509_CRL);
OSSL_FREE(info, AUTHORITY_INFO_ACCESS);
return r;
}
/** Compute the fingerprint of a TPM public key.
*
* @param[in] tpmPublicKey The public key created by the TPM
* @param[in] hashAlg The hash algorithm used for fingerprint computation.
* @param[out] fingerprint The fingerprint digest.
*
* @retval TSS2_RC_SUCCESS on success
* @retval TSS2_FAPI_RC_GENERAL_FAILURE if an error occurs in the crypto library
* @retval TSS2_FAPI_RC_MEMORY if memory could not be allocated
* @retval TSS2_FAPI_BAD_REFERENCE if tpmPublicKey or pemKeySize are NULL
* @retval TSS2_FAPI_RC_BAD_REFERENCE a invalid null pointer is passed.
* @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into
* the function.
*/
TSS2_RC
ifapi_get_tpm_key_fingerprint(
const TPM2B_PUBLIC *tpmPublicKey,
TPMI_ALG_HASH hashAlg,
TPM2B_DIGEST *fingerprint)
{
/* Check for NULL parameters */
return_if_null(tpmPublicKey, "tpmPublicKey is NULL", TSS2_FAPI_RC_BAD_REFERENCE);
EVP_PKEY *evpPublicKey = NULL;
TSS2_RC r = TPM2_RC_SUCCESS;
uint8_t *pubKeyDer = NULL;
int pubKeyDerSize;
IFAPI_CRYPTO_CONTEXT_BLOB *cryptoContext = NULL;
size_t hashSize;
size_t fingerPrintSize;
hashSize = ifapi_hash_get_digest_size(hashAlg);
if (!hashSize)
goto_error(r, TSS2_FAPI_RC_BAD_VALUE,
"Unsupported hash algorithm (%" PRIu16 ")", cleanup,
hashAlg);
evpPublicKey = EVP_PKEY_new();
goto_if_null2(evpPublicKey, "Out of memory.", r, TSS2_FAPI_RC_MEMORY, cleanup);
if (tpmPublicKey->publicArea.type == TPM2_ALG_RSA) {
r = ossl_rsa_pub_from_tpm(tpmPublicKey, evpPublicKey);
} else if (tpmPublicKey->publicArea.type == TPM2_ALG_ECC)
r = ossl_ecc_pub_from_tpm(tpmPublicKey, evpPublicKey);
else {
goto_error(r,TSS2_FAPI_RC_BAD_VALUE, "Invalid alg id.", cleanup);
}
goto_if_error(r, "Get ossl public key.", cleanup);
/* Convert the OpenSSL EVP pub key into DEF format */
pubKeyDerSize = i2d_PUBKEY(evpPublicKey, &pubKeyDer);
if (pubKeyDerSize == -1) {
goto_error(r, TSS2_FAPI_RC_GENERAL_FAILURE, "OSSL error", cleanup);
}
/* Compute the digest of the DER public key */
r = ifapi_crypto_hash_start(&cryptoContext, hashAlg);
goto_if_error(r, "crypto hash start", cleanup);
HASH_UPDATE_BUFFER(cryptoContext,
pubKeyDer, pubKeyDerSize, r, cleanup);
r = ifapi_crypto_hash_finish(&cryptoContext,
&fingerprint->buffer[0], &fingerPrintSize);
goto_if_error(r, "crypto hash finish", cleanup);
fingerprint->size = fingerPrintSize;
cleanup:
EVP_PKEY_free(evpPublicKey);
SAFE_FREE(pubKeyDer);
if (cryptoContext) {
ifapi_crypto_hash_abort(&cryptoContext);
}
return r;
}