/* SPDX-License-Identifier: BSD-2-Clause */ /******************************************************************************* * Copyright 2018-2019, Fraunhofer SIT sponsored by Infineon Technologies AG * All rights reserved. ******************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include "fapi_certificates.h" #include "fapi_util.h" #include "util/aux_util.h" #include "fapi_crypto.h" #define LOGMODULE fapi #include "util/log.h" /** 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); } 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: 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_GFp(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); } } } 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 * @retval TSS2_FAPI_RC_BAD_VALUE if an invalid value was passed into * the function. */ 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)) { int rc = X509_STORE_CTX_get_error(ctx); LOG_ERROR("%s", X509_verify_cert_error_string(rc)); 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)) { int rc = X509_STORE_CTX_get_error(ctx); LOG_ERROR("%s", X509_verify_cert_error_string(rc)); 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; if (!(hashSize = ifapi_hash_get_digest_size(hashAlg))) { 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; }