From 8249e6afadb1f694622d5fbdec0f0cf13ae7b9ea Mon Sep 17 00:00:00 2001 From: Andreas Steffen Date: Mon, 8 Nov 2021 09:02:40 +0100 Subject: [PATCH] libtpmtss: Establish session with TPM 2.0 Using the trusted RSA or ECC Endorsement Key of the TPM 2.0 a secure session is established via RSA public key encryption or an ephemeral ECDH key exchange, respectively. The session allows HMAC-based authenticated communication with the TPM 2.0 and the exchanged parameters can be encrypted where necessary to guarantee confidentiality. --- conf/plugins/tpm.opt | 7 +- src/libtpmtss/Makefile.am | 3 +- src/libtpmtss/tpm_tss_tss2_session.c | 829 +++++++++++++++++++++++++++ src/libtpmtss/tpm_tss_tss2_session.h | 69 +++ src/libtpmtss/tpm_tss_tss2_v2.c | 269 ++++++--- 5 files changed, 1084 insertions(+), 93 deletions(-) create mode 100644 src/libtpmtss/tpm_tss_tss2_session.c create mode 100644 src/libtpmtss/tpm_tss_tss2_session.h diff --git a/conf/plugins/tpm.opt b/conf/plugins/tpm.opt index 06c88861ea..cb1ab36e42 100644 --- a/conf/plugins/tpm.opt +++ b/conf/plugins/tpm.opt @@ -1,5 +1,6 @@ charon.plugins.tpm.use_rng = no - Whether the TPM should be used as RNG. + Whether the TPM should be used as RNG. For security reasons enable only if + an authenticated session can be set up (see _ek_handle_ option). charon.plugins.tpm.fips_186_4 = no Is the TPM 2.0 FIPS-186-4 compliant, forcing e.g. the use of the default @@ -14,3 +15,7 @@ charon.plugins.tpm.tcti.name = device|tabrmd charon.plugins.tpm.tcti.opts = /dev/tpmrm0| Options for the TPM 2.0 TCTI library. Defaults are _/dev/tpmrm0_ if the TCTI library name is _device_ and no options otherwise. + +charon.plugins.tpm.ek_handle = + Handle of the RSA or ECC Endorsement Key (EK) to be used to set up an + authenticated session with a TPM 2.0 (e.g. 0x81010001). diff --git a/src/libtpmtss/Makefile.am b/src/libtpmtss/Makefile.am index d192fc1267..a9ce63036c 100644 --- a/src/libtpmtss/Makefile.am +++ b/src/libtpmtss/Makefile.am @@ -25,7 +25,8 @@ libtpmtss_la_SOURCES = \ tpm_tss_quote_info.h tpm_tss_quote_info.c \ tpm_tss_trousers.h tpm_tss_trousers.c \ tpm_tss_tss2.h tpm_tss_tss2_v1.c tpm_tss_tss2_v2.c \ - tpm_tss_tss2_names.h tpm_tss_tss2_names_v1.c tpm_tss_tss2_names_v2.c + tpm_tss_tss2_names.h tpm_tss_tss2_names_v1.c tpm_tss_tss2_names_v2.c \ + tpm_tss_tss2_session.h tpm_tss_tss2_session.c if MONOLITHIC SUBDIRS = diff --git a/src/libtpmtss/tpm_tss_tss2_session.c b/src/libtpmtss/tpm_tss_tss2_session.c new file mode 100644 index 0000000000..05e5b3b20a --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_session.c @@ -0,0 +1,829 @@ +/* + * Copyright (C) 2021 Andreas Steffen, strongSec GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifdef TSS_TSS2_V2 + +#include "tpm_tss_tss2_session.h" + +#define LABEL "TPM 2.0 - " + +typedef struct private_tpm_tss_tss2_session_t private_tpm_tss_tss2_session_t; + +/** + * Private data of an tpm_tss_tss2_session_t object. + */ +struct private_tpm_tss_tss2_session_t { + + /** + * Public tpm_tss_tss2_session_t interface. + */ + tpm_tss_tss2_session_t public; + + /** + * Session handle for protected communication with TPM 2.0 + */ + uint32_t session_handle; + + /** + * Session key for protected communication with TPM 2.0 + */ + chunk_t session_key; + + /** + * Hash algorithm to be used for protected communication with TPM 2.0 + */ + TPM2_ALG_ID hash_alg; + + /** + * nonceCaller used for protected communication with TPM 2.0 + */ + TPM2B_NONCE nonceCaller; + + /** + * nonceTPM used for protected communication with TPM 2.0 + */ + TPM2B_NONCE nonceTPM; + + /** + * AES-CFB encryption of protected communication with TPM 2.0 + */ + crypter_t *crypter; + + /** + * SYS context + */ + TSS2_SYS_CONTEXT *sys_context; + +}; + +/** + * Two functions shared with tpm_tss_tss2_v2.c + */ + +hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg); + +size_t hash_len_from_tpm_alg_id(TPM2_ALG_ID alg); + + +/** + * Convert TPM2_ALG_ID to PRF algorithm + */ +pseudo_random_function_t prf_alg_from_tpm_alg_id(TPM2_ALG_ID alg) +{ + switch (alg) + { + case TPM2_ALG_SHA1: + return PRF_HMAC_SHA1; + case TPM2_ALG_SHA256: + return PRF_HMAC_SHA2_256; + case TPM2_ALG_SHA384: + return PRF_HMAC_SHA2_384; + case TPM2_ALG_SHA512: + return PRF_HMAC_SHA2_512; + default: + return PRF_UNDEFINED; + } +} + +static bool generate_nonce(size_t size, TPM2B_NONCE *nonce) +{ + nonce_gen_t *nonce_gen; + bool success; + + nonce_gen = lib->crypto->create_nonce_gen(lib->crypto); + if (!nonce_gen) + { + DBG1(DBG_PTS, "no nonce generator available"); + return FALSE; + } + nonce->size = size; + success = nonce_gen->get_nonce(nonce_gen, nonce->size, nonce->buffer); + nonce_gen->destroy(nonce_gen); + + if (!success) + { + DBG1(DBG_PTS, "generation of nonce failed"); + return FALSE; + } + + return TRUE; +} + +METHOD(tpm_tss_tss2_session_t, set_cmd_auths, bool, + private_tpm_tss_tss2_session_t *this) +{ + size_t hash_len, param_size, cp_size; + const uint8_t *param_buffer, *cp_buffer; + uint8_t cc_buffer[4]; + hash_algorithm_t hash_algorithm; + hasher_t *hasher; + pseudo_random_function_t prf_alg; + prf_t *prf; + chunk_t data, cp_hash, cp_hmac, nonce_caller, nonce_tpm, session_attributes; + bool success; + uint32_t rval; + + TSS2L_SYS_AUTH_COMMAND cmd; + TPM2B_DIGEST cpHash; + + cmd.count = 1; + cmd.auths[0].sessionHandle = this->session_handle; + cmd.auths[0].sessionAttributes = TPMA_SESSION_CONTINUESESSION | + TPMA_SESSION_ENCRYPT; + session_attributes = chunk_create(&cmd.auths[0].sessionAttributes, 1); + + hash_len = hash_len_from_tpm_alg_id(this->hash_alg); + + if (!generate_nonce(hash_len, &this->nonceCaller)) + { + return FALSE; + } + cmd.auths[0].nonce.size = this->nonceCaller.size; + memcpy(cmd.auths[0].nonce.buffer, this->nonceCaller.buffer, + this->nonceCaller.size); + + rval = Tss2_Sys_GetEncryptParam(this->sys_context, ¶m_size, + ¶m_buffer); + if (rval == TSS2_SYS_RC_NO_ENCRYPT_PARAM) + { + DBG2(DBG_PTS, LABEL "parameter encryption not possible"); + return FALSE; + } + + rval = Tss2_Sys_GetCommandCode(this->sys_context, cc_buffer); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetCommandCode failed: 0x%06x", rval); + return FALSE; + } + + rval = Tss2_Sys_GetCpBuffer(this->sys_context, &cp_size, &cp_buffer); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_GetCpBuffer failed: 0x%06x", rval); + return FALSE; + } + + /* compute cpHash */ + hash_algorithm = hash_alg_from_tpm_alg_id(this->hash_alg); + hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); + if (!hasher) + { + DBG1(DBG_PTS, "hasher could not be created"); + return FALSE; + } + + data = chunk_alloc(4 + cp_size); + memcpy(data.ptr, cc_buffer, 4); + memcpy(data.ptr + 4, cp_buffer, cp_size); + + success = hasher->get_hash(hasher, data, cpHash.buffer); + cpHash.size = hasher->get_hash_size(hasher); + hasher->destroy(hasher); + chunk_free(&data); + + if (!success) + { + DBG1(DBG_PTS, "computation of cpHash failed"); + return FALSE; + } + cp_hash = chunk_create(cpHash.buffer, cpHash.size); + + /* compute cp HMAC */ + prf_alg = prf_alg_from_tpm_alg_id(this->hash_alg); + prf = lib->crypto->create_prf(lib->crypto, prf_alg); + if (!prf) + { + DBG1(DBG_PTS, "could not create PRF"); + return FALSE; + } + if (!prf->set_key(prf, this->session_key)) + { + DBG1(DBG_PTS, "could not set PRF key"); + prf->destroy(prf); + return FALSE; + } + + nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); + nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); + + success = prf->get_bytes(prf, cp_hash, NULL) && + prf->get_bytes(prf, nonce_caller, NULL) && + prf->get_bytes(prf, nonce_tpm, NULL) && + prf->get_bytes(prf, session_attributes, cmd.auths[0].hmac.buffer); + cmd.auths[0].hmac.size = prf->get_block_size(prf); + prf->destroy(prf); + + if (!success) + { + DBG1(DBG_PTS, "cpHmac computation failed"); + return FALSE; + } + cp_hmac = chunk_create(cmd.auths[0].hmac.buffer, cmd.auths[0].hmac.size); + DBG2(DBG_PTS, LABEL "cpHmac: %B", &cp_hmac); + + rval = Tss2_Sys_SetCmdAuths(this->sys_context, &cmd); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_SetCmdAuths failed: 0x%06x", rval); + return FALSE; + } + + return TRUE; +} + +/** + * Key Derivation Function using Counter Mode as defined by NIST SP800-108 + * - the label is expected to be NUL terminated + */ +static bool kdf_a(TPMI_ALG_HASH hash_alg, chunk_t key, chunk_t label, + chunk_t context_u, chunk_t context_v, uint32_t bytes, + chunk_t *key_mat) +{ + pseudo_random_function_t prf_alg; + chunk_t count_chunk, bits_chunk; + uint32_t iterations, counter, count, bits; + uint8_t *pos; + size_t hlen; + prf_t *prf; + + bits = htonl(8 * bytes); + bits_chunk = chunk_create((uint8_t*)&bits, sizeof(bits)); + + prf_alg = prf_alg_from_tpm_alg_id(hash_alg); + prf = lib->crypto->create_prf(lib->crypto, prf_alg); + if (!prf) + { + DBG1(DBG_PTS, "could not create PRF"); + return FALSE; + } + if (!prf->set_key(prf, key)) + { + DBG1(DBG_PTS, "could not set PRF key"); + prf->destroy(prf); + return FALSE; + } + + hlen = prf->get_block_size(prf); + iterations = (bytes + hlen - 1) / hlen; + *key_mat = chunk_alloc(iterations * hlen); + pos = key_mat->ptr; + + for (counter = 1; counter <= iterations; counter++) + { + count = htonl(counter); + count_chunk = chunk_create((uint8_t*)&count, sizeof(count)); + + if (!prf->get_bytes(prf, count_chunk, NULL) || + !prf->get_bytes(prf, label, NULL) || + !prf->get_bytes(prf, context_u, NULL) || + !prf->get_bytes(prf, context_v, NULL) || + !prf->get_bytes(prf, bits_chunk, pos)) + { + DBG1(DBG_PTS, "KDFa computation failed"); + chunk_free(key_mat); + prf->destroy(prf); + return FALSE; + } + pos += hlen; + } + prf->destroy(prf); + + return TRUE; +} + +METHOD(tpm_tss_tss2_session_t, get_rsp_auths, bool, + private_tpm_tss_tss2_session_t *this) +{ + size_t param_size, rp_size, key_len, iv_len; + const uint8_t *param_buffer, *rp_buffer; + uint8_t rc_buffer[4] = { 0 }; + uint8_t cc_buffer[4]; + hash_algorithm_t hash_algorithm; + hasher_t *hasher; + pseudo_random_function_t prf_alg; + prf_t *prf; + chunk_t kdf_label = chunk_from_chars('C','F','B', 0x00); + chunk_t data, rp_hash, rp_hmac, nonce_caller, nonce_tpm, session_attributes; + chunk_t key_mat, aes_key, aes_iv; + bool success; + uint32_t rval; + + TSS2L_SYS_AUTH_RESPONSE rsp; + TPM2B_DIGEST rpHash, rpHmac; + + rval = Tss2_Sys_GetRspAuths(this->sys_context, &rsp); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetRspAuths failed: 0x%06x", rval); + return FALSE; + } + + /* update nonceTPM */ + memcpy(this->nonceTPM.buffer, rsp.auths[0].nonce.buffer, + rsp.auths[0].nonce.size); + this->nonceTPM.size = rsp.auths[0].nonce.size; + + rval = Tss2_Sys_GetRpBuffer(this->sys_context, &rp_size, &rp_buffer); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetRpBuffer failed: 0x%06x", rval); + return FALSE; + } + + rval = Tss2_Sys_GetCommandCode(this->sys_context, cc_buffer); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetCommandCode failed: 0x%06x", rval); + return FALSE; + } + + /* compute rpHash */ + hash_algorithm = hash_alg_from_tpm_alg_id(this->hash_alg); + hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); + if (!hasher) + { + DBG1(DBG_PTS, "hasher could not be created"); + return FALSE; + } + + data = chunk_alloc(4 + 4 + rp_size); + memcpy(data.ptr, rc_buffer, 4); + memcpy(data.ptr + 4, cc_buffer, 4); + memcpy(data.ptr + 8, rp_buffer, rp_size); + + success = hasher->get_hash(hasher, data, rpHash.buffer); + rpHash.size = hasher->get_hash_size(hasher); + hasher->destroy(hasher); + chunk_free(&data); + + if (!success) + { + DBG1(DBG_PTS, "computation of rpHash failed"); + return FALSE; + } + rp_hash = chunk_create(rpHash.buffer, rpHash.size); + + /* compute rpHmac */ + prf_alg = prf_alg_from_tpm_alg_id(this->hash_alg); + prf = lib->crypto->create_prf(lib->crypto, prf_alg); + if (!prf) + { + DBG1(DBG_PTS, "could not create PRF"); + return FALSE; + } + if (!prf->set_key(prf, this->session_key)) + { + DBG1(DBG_PTS, "could not set PRF key"); + prf->destroy(prf); + return FALSE; + } + + nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); + nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); + session_attributes = chunk_create(&rsp.auths[0].sessionAttributes, 1); + + success = prf->get_bytes(prf, rp_hash, NULL) && + prf->get_bytes(prf, nonce_tpm, NULL) && + prf->get_bytes(prf, nonce_caller, NULL) && + prf->get_bytes(prf, session_attributes, rpHmac.buffer); + rpHmac.size = prf->get_block_size(prf); + prf->destroy(prf); + + if (!success) + { + DBG1(DBG_PTS, "computation of rpHmac failed"); + return FALSE; + } + rp_hmac = chunk_create(rpHmac.buffer, rpHmac.size); + DBG2(DBG_PTS, LABEL "rpHMAC: %B", &rp_hmac); + + /* verify rpHmac */ + if (!memeq(rsp.auths[0].hmac.buffer, rpHmac.buffer, rpHmac.size)) + { + DBG1(DBG_PTS, LABEL "invalid HMAC received for session 0x%08x", + this->session_handle); + return FALSE; + } + + /* decrypt parameter */ + rval = Tss2_Sys_GetEncryptParam(this->sys_context, ¶m_size, + ¶m_buffer); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetEncryptParam failed: 0x%06x", rval); + return FALSE; + } + + key_len = this->crypter->get_key_size(this->crypter); + iv_len = this->crypter->get_iv_size(this->crypter); + + /* derive decryption key using KDFa */ + if (!kdf_a(this->hash_alg, this->session_key, kdf_label, nonce_tpm, + nonce_caller, key_len + iv_len , &key_mat)) + { + return FALSE; + } + aes_key = chunk_create(key_mat.ptr, key_len); + aes_iv = chunk_create(key_mat.ptr + key_len, iv_len); + + if (!this->crypter->set_key(this->crypter, aes_key)) + { + chunk_clear(&key_mat); + return FALSE; + } + + /* copy ciphertext */ + data = chunk_alloc(param_size); + memcpy(data.ptr, param_buffer, param_size); + + /* decrypt ciphertext */ + success = this->crypter->decrypt(this->crypter, data, aes_iv, NULL); + chunk_clear(&key_mat); + if (!success) + { + chunk_free(&data); + return FALSE; + } + DBG4(DBG_PTS, LABEL "plaintext: %B", &data); + + /* copy back plaintext */ + rval = Tss2_Sys_SetEncryptParam(this->sys_context, data.len, data.ptr); + chunk_clear(&data); + + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_SetEncryptParam failed: 0x%06x", rval); + return FALSE; + } + + return TRUE; +} + + +METHOD(tpm_tss_tss2_session_t, destroy, void, + private_tpm_tss_tss2_session_t *this) +{ + if (this->session_handle) + { + uint32_t rval; + + /* flush session context */ + rval = Tss2_Sys_FlushContext(this->sys_context, this->session_handle); + if (rval != TPM2_RC_SUCCESS) + { + DBG2(DBG_PTS, LABEL "Tss2_Sys_FlushContext failed: 0x%06x", rval); + } + chunk_clear(&this->session_key); + } + DESTROY_IF(this->crypter); + free(this); +} + +static chunk_t secret_label = chunk_from_chars('S','E','C','R','E','T', 0x00); + +static bool rsa_salt(TPM2B_PUBLIC *public, TPMI_ALG_HASH hash_alg, + chunk_t *secret, TPM2B_ENCRYPTED_SECRET *encryptedSalt) +{ + encryption_scheme_t encryption_scheme; + public_key_t *pubkey = NULL; + nonce_gen_t *nonce_gen; + chunk_t encrypted_salt = chunk_empty; + chunk_t rsa_modulus; + chunk_t rsa_exponent = chunk_from_chars(0x01, 0x00, 0x01); + uint32_t exponent; + size_t hash_len; + bool success; + + TPM2B_PUBLIC_KEY_RSA *rsa; + + switch (hash_alg) + { + case TPM2_ALG_SHA1: + encryption_scheme = ENCRYPT_RSA_OAEP_SHA1; + break; + case TPM2_ALG_SHA256: + encryption_scheme = ENCRYPT_RSA_OAEP_SHA256; + break; + case TPM2_ALG_SHA384: + encryption_scheme = ENCRYPT_RSA_OAEP_SHA384; + break; + case TPM2_ALG_SHA512: + encryption_scheme = ENCRYPT_RSA_OAEP_SHA512; + break; + default: + DBG1(DBG_PTS, LABEL "unsupported key hash algorithm"); + return FALSE; + } + + hash_len = hash_len_from_tpm_alg_id(hash_alg); + + /* create a salt nonce to be used as a shared secret */ + nonce_gen = lib->crypto->create_nonce_gen(lib->crypto); + if (!nonce_gen) + { + DBG1(DBG_PTS, "no nonce generator available"); + return FALSE; + } + success = nonce_gen->allocate_nonce(nonce_gen, hash_len, secret); + nonce_gen->destroy(nonce_gen); + if (!success) + { + DBG1(DBG_PTS, "generation of salt nonce failed"); + return FALSE; + } + + /* get RSA public key */ + rsa = &public->publicArea.unique.rsa; + rsa_modulus = chunk_create(rsa->buffer, rsa->size); + exponent = htonl(public->publicArea.parameters.rsaDetail.exponent); + if (exponent) + { + rsa_exponent = chunk_from_thing(exponent); + } + pubkey = lib->creds->create(lib->creds, CRED_PUBLIC_KEY, KEY_RSA, + BUILD_RSA_MODULUS, rsa_modulus, BUILD_RSA_PUB_EXP, + rsa_exponent, BUILD_END); + if (!pubkey) + { + DBG1(DBG_PTS, "retrieval of EK public key failed"); + chunk_clear(secret); + return FALSE; + } + + /* use RSA public key encryption to encrypt secret salt nonce */ + success = pubkey->encrypt(pubkey, encryption_scheme, &secret_label, + *secret, &encrypted_salt); + pubkey->destroy(pubkey); + if (!success) + { + DBG1(DBG_PTS, "encryption of salt failed"); + chunk_clear(secret); + return FALSE; + } + + /* copy encryptedSalt to output parameter */ + encryptedSalt->size = encrypted_salt.len; + memcpy(encryptedSalt->secret, encrypted_salt.ptr, encrypted_salt.len); + free(encrypted_salt.ptr); + + return TRUE; +} + + +/** + * Key Derivation Function used to derive an ecc-based secret + * - the label is expected to be NUL terminated + */ +static bool kdf_e(TPMI_ALG_HASH hash_alg, chunk_t z, chunk_t label, + chunk_t context_u, chunk_t context_v, uint32_t bytes, + chunk_t *key_mat) +{ + hash_algorithm_t hash_algorithm; + chunk_t count_chunk; + uint32_t iterations, counter, count; + uint8_t *pos; + size_t hlen; + hasher_t *hasher; + + hash_algorithm = hash_alg_from_tpm_alg_id(hash_alg); + hasher = lib->crypto->create_hasher(lib->crypto, hash_algorithm); + if (!hasher) + { + DBG1(DBG_PTS, "could not create hasher"); + return FALSE; + } + + hlen = hasher->get_hash_size(hasher); + iterations = (bytes + hlen - 1) / hlen; + *key_mat = chunk_alloc(iterations * hlen); + pos = key_mat->ptr; + + for (counter = 1; counter <= iterations; counter++) + { + count = htonl(counter); + count_chunk = chunk_create((uint8_t*)&count, sizeof(count)); + + if (!hasher->get_hash(hasher, count_chunk, NULL) || + !hasher->get_hash(hasher, z, NULL) || + !hasher->get_hash(hasher, label, NULL) || + !hasher->get_hash(hasher, context_u, NULL) || + !hasher->get_hash(hasher, context_v, pos)) + { + DBG1(DBG_PTS, "KDFe computation failed"); + chunk_free(key_mat); + hasher->destroy(hasher); + return FALSE; + } + pos += hlen; + } + hasher->destroy(hasher); + + return TRUE; +} + +static bool ecc_salt(TPM2B_PUBLIC *public, TPMI_ALG_HASH hash_alg, + chunk_t *secret, TPM2B_ENCRYPTED_SECRET *encryptedSalt) +{ + diffie_hellman_group_t ec_group; + diffie_hellman_t *dh; + chunk_t ecdh_pubkey = chunk_empty, ecdh_pubkey_x, ecdh_pubkey_y; + chunk_t ecc_pubkey = chunk_empty, ecc_pubkey_x, ecc_pubkey_y; + chunk_t z = chunk_empty; + uint16_t len; + uint8_t *pos; + size_t hash_len; + bool success = FALSE; + + switch (public->publicArea.parameters.eccDetail.curveID) + { + case TPM2_ECC_NIST_P256: + ec_group = ECP_256_BIT; + break; + case TPM2_ECC_NIST_P384: + ec_group = ECP_384_BIT; + break; + case TPM2_ECC_NIST_P521: + ec_group = ECP_521_BIT; + break; + default: + DBG1(DBG_PTS, "type of ECC EK key not supported"); + return FALSE; + } + + /* Generate ECDH key pair */ + dh = lib->crypto->create_dh(lib->crypto, ec_group); + if (!dh) + { + DBG1(DBG_PTS, "DH group could not be created"); + return FALSE; + } + if (!dh->get_my_public_value(dh, &ecdh_pubkey)) + { + DBG1(DBG_PTS, "DH public key could not be generated"); + dh->destroy(dh); + return FALSE; + } + ecdh_pubkey_x = chunk_create(ecdh_pubkey.ptr, ecdh_pubkey.len / 2); + ecdh_pubkey_y = chunk_create(ecdh_pubkey.ptr + ecdh_pubkey_x.len, + ecdh_pubkey_x.len); + + /* get ECC public key */ + ecc_pubkey_x = chunk_create(public->publicArea.unique.ecc.x.buffer, + public->publicArea.unique.ecc.x.size); + ecc_pubkey_y = chunk_create(public->publicArea.unique.ecc.y.buffer, + public->publicArea.unique.ecc.y.size); + ecc_pubkey = chunk_cat("cc", ecc_pubkey_x, ecc_pubkey_y); + + /* compute point multiplication of ecc_pubkey with ecdh_privkey */ + if (!dh->set_other_public_value(dh, ecc_pubkey)) + { + DBG1(DBG_PTS, "ECC public could not be set"); + goto error; + } + if (!dh->get_shared_secret(dh, &z)) + { + DBG1(DBG_PTS, "could not create shared secret"); + goto error; + } + + hash_len = hash_len_from_tpm_alg_id(hash_alg); + + /* derive secret using KDFe */ + if (!kdf_e(hash_alg, z, secret_label, ecdh_pubkey_x, ecc_pubkey_x, + hash_len, secret)) + { + goto error; + } + + /* copy ECDH pubkey to encrypted salt parameter */ + len = htons(ecdh_pubkey_x.len); + encryptedSalt->size = 2 * sizeof(len) + ecdh_pubkey.len; + pos = encryptedSalt->secret; + memcpy(pos, (uint8_t*)&len, sizeof(len)); + pos += sizeof(len); + memcpy(pos, ecdh_pubkey_x.ptr, ecdh_pubkey_x.len); + pos += ecdh_pubkey_x.len; + memcpy(pos, (uint8_t*)&len, sizeof(len)); + pos += sizeof(len); + memcpy(pos, ecdh_pubkey_y.ptr, ecdh_pubkey_y.len); + + success = TRUE; + +error: + dh->destroy(dh); + chunk_free(&ecdh_pubkey); + chunk_free(&ecc_pubkey); + chunk_clear(&z); + + return success; +} + +/** + * See header + */ +tpm_tss_tss2_session_t* tpm_tss_tss2_session_create(uint32_t ek_handle, + TPM2B_PUBLIC *public, TSS2_SYS_CONTEXT *sys_context) +{ + private_tpm_tss_tss2_session_t *this; + chunk_t secret = chunk_empty; + chunk_t kdf_label = chunk_from_chars('A','T','H', 0x00); + chunk_t nonce_caller, nonce_tpm; + size_t hash_len; + uint32_t rval; + + TPM2B_ENCRYPTED_SECRET encryptedSalt; + TPM2_SE sessionType = TPM2_SE_HMAC; + TPMT_SYM_DEF symmetric = { .algorithm = TPM2_ALG_AES, + .mode.aes = TPM2_ALG_CFB, .keyBits.aes = 128 }; + + INIT(this, + .public = { + .set_cmd_auths = _set_cmd_auths, + .get_rsp_auths = _get_rsp_auths, + .destroy = _destroy, + }, + .sys_context = sys_context, + .hash_alg = public->publicArea.nameAlg, + ); + + hash_len = hash_len_from_tpm_alg_id(this->hash_alg); + + this->crypter = lib->crypto->create_crypter(lib->crypto, ENCR_AES_CFB, + symmetric.keyBits.aes / 8); + if (!this->crypter) + { + DBG1(DBG_PTS, "could not create %N crypter", encryption_algorithm_names, + ENCR_AES_CFB); + goto error; + } + + if (!generate_nonce(hash_len, &this->nonceCaller)) + { + goto error; + } + + /* determine endorsement key type */ + switch (public->publicArea.type) + { + case TPM2_ALG_RSA: + DBG1(DBG_PTS, LABEL "RSA EK handle: 0x%08x", ek_handle); + if (!rsa_salt(public, this->hash_alg, &secret, &encryptedSalt)) + { + goto error; + } + break; + case TPM2_ALG_ECC: + DBG1(DBG_PTS, LABEL "ECC EK handle: 0x%08x", ek_handle); + if (!ecc_salt(public, this->hash_alg, &secret, &encryptedSalt)) + { + goto error; + } + break; + default: + DBG1(DBG_PTS, LABEL "unsupported ek key type"); + goto error; + } + + rval = Tss2_Sys_StartAuthSession(this->sys_context, ek_handle, TPM2_RH_NULL, + NULL, &this->nonceCaller, &encryptedSalt, sessionType, &symmetric, + this->hash_alg, &this->session_handle, &this->nonceTPM, NULL); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_StartAuthSession failed: 0x%06x", rval); + goto error; + } + DBG2(DBG_PTS, LABEL "session handle: 0x%08x", this->session_handle); + + nonce_tpm = chunk_create(this->nonceTPM.buffer, this->nonceTPM.size); + nonce_caller = chunk_create(this->nonceCaller.buffer, this->nonceCaller.size); + + /* derive sessionKey using KDFa */ + if (!kdf_a(this->hash_alg, secret, kdf_label, nonce_tpm, nonce_caller, + hash_len, &this->session_key)) + { + goto error; + } + chunk_clear(&secret); + DBG4(DBG_PTS, LABEL "session key: %B", &this->session_key); + + return &this->public; + + error: + chunk_clear(&secret); + destroy(this); + return NULL; +} + +#endif /* TSS_TSS2_V2 */ diff --git a/src/libtpmtss/tpm_tss_tss2_session.h b/src/libtpmtss/tpm_tss_tss2_session.h new file mode 100644 index 0000000000..479ebf6891 --- /dev/null +++ b/src/libtpmtss/tpm_tss_tss2_session.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2021 Andreas Steffen, strongSec GmbH + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +/** + * @defgroup tpm_tss_tss2_session tpm_tss_tss2_session + * @{ @ingroup libtpmtss + */ + +#ifndef TPM_TSS_TSS2_SESSION_H_ +#define TPM_TSS_TSS2_SESSION_H_ + +#ifdef TSS_TSS2_V2 + +#include + +#include + +typedef struct tpm_tss_tss2_session_t tpm_tss_tss2_session_t; + +/** + * public interface of TPM 2.0 TSS session object + */ +struct tpm_tss_tss2_session_t { + + /** + * Set TPM 2.0 TSS Command Authentications + * + * @return TRUE if successful + */ + bool (*set_cmd_auths)(tpm_tss_tss2_session_t *this); + + /** + * Get TPM 2.0 TSS Response Authentications + * + * @return TRUE if successful + */ + bool (*get_rsp_auths)(tpm_tss_tss2_session_t *this); + + /** + * Destroy the TPM 2.0 TSS session object + */ + void (*destroy)(tpm_tss_tss2_session_t *this); + +}; + +/** + * Create a tpm_tss_tss2_session instance. + * + * @param ek_handle endorsement key handle + * @param public public information on endorsement key + * @param sys_context TSS2 system context + */ +tpm_tss_tss2_session_t* tpm_tss_tss2_session_create(uint32_t ek_handle, + TPM2B_PUBLIC *public, TSS2_SYS_CONTEXT *sys_context); + +#endif /* TSS_TSS2_V2 */ + +#endif /** TPM_TSS_TSS2_SESSION_H_ @}*/ diff --git a/src/libtpmtss/tpm_tss_tss2_v2.c b/src/libtpmtss/tpm_tss_tss2_v2.c index 7357fdeaf8..915bd31b29 100644 --- a/src/libtpmtss/tpm_tss_tss2_v2.c +++ b/src/libtpmtss/tpm_tss_tss2_v2.c @@ -3,6 +3,8 @@ * Copyright (C) 2018-2020 Andreas Steffen * HSR Hochschule fuer Technik Rapperswil * + * Copyright (C) 2021 Andreas Steffen, strongSec GmbH + * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your @@ -19,20 +21,20 @@ #ifdef TSS_TSS2_V2 +#include "tpm_tss_tss2_session.h" + #include #include #include #include #include -#include - #include #include #include #include -#define LABEL "TPM 2.0 -" +#define LABEL "TPM 2.0 - " #define PLATFORM_PCR 24 #define MAX_PCR_BANKS 4 @@ -94,6 +96,11 @@ struct private_tpm_tss_tss2_t { */ bool old_event_digest_format; + /** + * TSS2 session used for protected communication with TPM 2.0 + */ + tpm_tss_tss2_session_t *session; + /** * Mutex controlling access to the TPM 2.0 context */ @@ -144,7 +151,7 @@ static TPM2_ALG_ID hash_alg_to_tpm_alg_id(hash_algorithm_t alg) /** * Convert TPM2_ALG_ID to hash algorithm */ -static hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg) +hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg) { switch (alg) { @@ -167,6 +174,31 @@ static hash_algorithm_t hash_alg_from_tpm_alg_id(TPM2_ALG_ID alg) } } +/** + * Return hash length of TPM2_ALG_ID algorithm + */ +size_t hash_len_from_tpm_alg_id(TPM2_ALG_ID alg) +{ + switch (alg) + { + case TPM2_ALG_SHA1: + return TPM2_SHA1_DIGEST_SIZE; + case TPM2_ALG_SHA256: + case TPM2_ALG_SHA3_256: + return TPM2_SHA256_DIGEST_SIZE; + case TPM2_ALG_SHA384: + case TPM2_ALG_SHA3_384: + return TPM2_SHA384_DIGEST_SIZE; + case TPM2_ALG_SHA512: + case TPM2_ALG_SHA3_512: + return TPM2_SHA512_DIGEST_SIZE; + case TPM2_ALG_SM3_256: + return TPM2_SM3_256_DIGEST_SIZE; + default: + return 0; + } +} + /** * Check if an algorithm given by its TPM2_ALG_ID is supported by the TPM */ @@ -234,8 +266,8 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_TPM_PROPERTIES: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "GetCapability failed for TPM2_CAP_TPM_PROPERTIES: 0x%06x", + rval); return FALSE; } memset(manufacturer, '\0', sizeof(manufacturer)); @@ -280,7 +312,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) this->fips_186_4 = lib->settings->get_bool(lib->settings, "%s.plugins.tpm.fips_186_4", FALSE, lib->ns); } - DBG2(DBG_PTS, "%s manufacturer: %s (%s) rev: %05.2f %u %s", LABEL, + DBG2(DBG_PTS, LABEL "manufacturer: %s (%s) rev: %05.2f %u %s", manufacturer, vendor_string, (float)revision/100, year, fips_140_2 ? "FIPS 140-2" : (this->fips_186_4 ? "FIPS 186-4" : "")); @@ -313,8 +345,8 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_ALGS: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "GetCapability failed for TPM2_CAP_ALGS: 0x%06x", + rval); return FALSE; } @@ -335,7 +367,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) pos += written; len -= written; } - DBG2(DBG_PTS, "%s algorithms:%s", LABEL, buf); + DBG2(DBG_PTS, LABEL "algorithms:%s", buf); /* get supported ECC curves */ this->mutex->lock(this->mutex); @@ -344,8 +376,8 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_ECC_CURVES: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "GetCapability failed for TPM2_CAP_ECC_CURVES: 0x%06x", + rval); return FALSE; } @@ -365,7 +397,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) pos += written; len -= written; } - DBG2(DBG_PTS, "%s ECC curves:%s", LABEL, buf); + DBG2(DBG_PTS, LABEL "ECC curves:%s", buf); /* get assigned PCR banks */ this->mutex->lock(this->mutex); @@ -374,8 +406,8 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s GetCapability failed for TPM2_CAP_PCRS: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "GetCapability failed for TPM2_CAP_PCRS: 0x%06x", + rval); return FALSE; } @@ -399,7 +431,7 @@ static bool get_algs_capability(private_tpm_tss_tss2_t *this) pos += written; len -= written; } - DBG2(DBG_PTS, "%s PCR banks:%s", LABEL, buf); + DBG2(DBG_PTS, LABEL "PCR banks:%s", buf); return TRUE; } @@ -421,7 +453,7 @@ static bool initialize_tcti_context(private_tpm_tss_tss2_t *this) rval = tcti_init(NULL, &tcti_context_size, tcti_opts); if (rval != TSS2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s tcti init setup failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "tcti init setup failed: 0x%06x", rval); return FALSE; } @@ -433,7 +465,7 @@ static bool initialize_tcti_context(private_tpm_tss_tss2_t *this) rval = tcti_init(this->tcti_context, &tcti_context_size, tcti_opts); if (rval != TSS2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s tcti init allocation failed: 0x%06x", LABEL,rval); + DBG1(DBG_PTS, LABEL "tcti init allocation failed: 0x%06x", rval); return FALSE; } return TRUE; @@ -465,8 +497,7 @@ static bool initialize_sys_context(private_tpm_tss_tss2_t *this) this->tcti_context, &abi_version); if (rval != TSS2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s could not get sys_context: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "could not get sys_context: 0x%06x", rval); return FALSE; } @@ -523,8 +554,8 @@ bool read_public(private_tpm_tss_tss2_t *this, TPMI_DH_OBJECT handle, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s could not read public key from handle 0x%08x: 0x%06x", - LABEL, handle, rval); + DBG1(DBG_PTS, LABEL "could not read public key from handle 0x%08x: 0x%06x", + handle, rval); return FALSE; } return TRUE; @@ -577,8 +608,8 @@ METHOD(tpm_tss_t, get_public, chunk_t, NULL, &aik_pubkey, CRED_PART_RSA_MODULUS, aik_modulus, CRED_PART_RSA_PUB_EXP, aik_exponent, CRED_PART_END)) { - DBG1(DBG_PTS, "%s subjectPublicKeyInfo encoding of public key " - "failed", LABEL); + DBG1(DBG_PTS, LABEL "subjectPublicKeyInfo encoding of public key " + "failed"); return chunk_empty; } break; @@ -618,7 +649,7 @@ METHOD(tpm_tss_t, get_public, chunk_t, break; } default: - DBG1(DBG_PTS, "%s unsupported key type", LABEL); + DBG1(DBG_PTS, LABEL "unsupported key type"); return chunk_empty; } DBG1(DBG_PTS, "signature algorithm is %N with %N hash", @@ -706,7 +737,7 @@ METHOD(tpm_tss_t, supported_signature_schemes, enumerator_t*, break; } default: - DBG1(DBG_PTS, "%s unsupported key type", LABEL); + DBG1(DBG_PTS, LABEL "unsupported key type"); return enumerator_create_empty(); } return enumerator_create_single(signature_params_clone(&supported_scheme), @@ -743,8 +774,8 @@ static bool init_pcr_selection(private_tpm_tss_tss2_t *this, uint32_t pcrs, /* check if there is an assigned PCR bank for this hash algorithm */ if (!has_pcr_bank(this, alg)) { - DBG1(DBG_PTS, "%s %N hash algorithm not supported by any PCR bank", - LABEL, hash_algorithm_short_names, alg); + DBG1(DBG_PTS, LABEL "%N hash algorithm not supported by any PCR bank", + hash_algorithm_short_names, alg); return FALSE; } @@ -781,8 +812,8 @@ METHOD(tpm_tss_t, read_pcr, bool, if (pcr_num >= PLATFORM_PCR) { - DBG1(DBG_PTS, "%s maximum number of supported PCR is %d", - LABEL, PLATFORM_PCR); + DBG1(DBG_PTS, LABEL "maximum number of supported PCR is %d", + PLATFORM_PCR); return FALSE; } @@ -801,8 +832,7 @@ METHOD(tpm_tss_t, read_pcr, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s PCR bank could not be read: 0x%60x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "PCR bank could not be read: 0x%60x", rval); return FALSE; } pcr_value_ptr = (uint8_t *)pcr_values.digests[0].buffer; @@ -827,8 +857,8 @@ METHOD(tpm_tss_t, extend_pcr, bool, /* check if there is an assigned PCR bank for this hash algorithm */ if (!has_pcr_bank(this, alg)) { - DBG1(DBG_PTS, "%s %N hash algorithm not supported by any PCR bank", - LABEL, hash_algorithm_short_names, alg); + DBG1(DBG_PTS, LABEL "%N hash algorithm not supported by any PCR bank", + hash_algorithm_short_names, alg); return FALSE; } @@ -883,8 +913,8 @@ METHOD(tpm_tss_t, extend_pcr, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS, "%s PCR %02u could not be extended: 0x%06x", - LABEL, pcr_num, rval); + DBG1(DBG_PTS, LABEL "PCR %02u could not be extended: 0x%06x", + pcr_num, rval); return FALSE; } @@ -935,7 +965,7 @@ METHOD(tpm_tss_t, quote, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_Quote failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_Quote failed: 0x%06x", rval); return FALSE; } quoted_chunk = chunk_create(quoted.attestationData, quoted.size); @@ -948,7 +978,7 @@ METHOD(tpm_tss_t, quote, bool, !reader->read_data (reader, 10, &pcr_select) || !reader->read_data16(reader, &pcr_digest)) { - DBG1(DBG_PTS, "%s parsing of quoted struct failed", LABEL); + DBG1(DBG_PTS, LABEL "parsing of quoted struct failed"); reader->destroy(reader); return FALSE; } @@ -987,8 +1017,8 @@ METHOD(tpm_tss_t, quote, bool, hash_alg = sig.signature.ecdsa.hash; break; default: - DBG1(DBG_PTS, "%s unsupported %N signature algorithm", - LABEL, tpm_alg_id_names, sig.sigAlg); + DBG1(DBG_PTS, LABEL "unsupported %N signature algorithm", + tpm_alg_id_names, sig.sigAlg); return FALSE; } @@ -1053,8 +1083,8 @@ METHOD(tpm_tss_t, sign, bool, alg_id = hash_alg_to_tpm_alg_id(hash_alg); if (!is_supported_alg(this, alg_id)) { - DBG1(DBG_PTS, "%s %N hash algorithm not supported by TPM", - LABEL, hash_algorithm_short_names, hash_alg); + DBG1(DBG_PTS, LABEL "%N hash algorithm not supported by TPM", + hash_algorithm_short_names, hash_alg); return FALSE; } @@ -1085,8 +1115,8 @@ METHOD(tpm_tss_t, sign, bool, } else { - DBG1(DBG_PTS, "%s signature scheme %N not supported by TPM key", - LABEL, signature_scheme_names, scheme); + DBG1(DBG_PTS, LABEL "signature scheme %N not supported by TPM key", + signature_scheme_names, scheme); return FALSE; } @@ -1101,7 +1131,7 @@ METHOD(tpm_tss_t, sign, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_Hash failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS,LABEL "Tss2_Sys_Hash failed: 0x%06x", rval); return FALSE; } } @@ -1116,8 +1146,8 @@ METHOD(tpm_tss_t, sign, bool, alg_id, &sequence_handle, 0); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_HashSequenceStart failed: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_HashSequenceStart failed: 0x%06x", + rval); this->mutex->unlock(this->mutex); return FALSE; } @@ -1133,8 +1163,8 @@ METHOD(tpm_tss_t, sign, bool, &auth_cmd, &buffer, 0); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_SequenceUpdate failed: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_SequenceUpdate failed: 0x%06x", + rval); this->mutex->unlock(this->mutex); return FALSE; } @@ -1147,8 +1177,8 @@ METHOD(tpm_tss_t, sign, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_SequenceComplete failed: 0x%06x", - LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_SequenceComplete failed: 0x%06x", + rval); return FALSE; } } @@ -1159,7 +1189,7 @@ METHOD(tpm_tss_t, sign, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_Sign failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_Sign failed: 0x%06x", rval); return FALSE; } @@ -1206,34 +1236,107 @@ METHOD(tpm_tss_t, sign, bool, sig.signature.ecdsa.signatureS.size))); break; default: - DBG1(DBG_PTS, "%s unsupported %N signature scheme", - LABEL, signature_scheme_names, scheme); + DBG1(DBG_PTS, LABEL "unsupported %N signature scheme", + signature_scheme_names, scheme); return FALSE; } return TRUE; } +/** + * Check if an authenticated session with the TPM 2.0 can be started + * The handle of the RSA Endorsement Key (EK) is required + */ +static void try_session_start(private_tpm_tss_tss2_t *this) +{ + uint32_t ek_handle = 0; + chunk_t handle_chunk; + char *handle_str; + + TPM2B_PUBLIC public = { 0, }; + + /* get Endorsement Key (EK) handle from settings */ + handle_str = lib->settings->get_str(lib->settings, + "%s.plugins.tpm.ek_handle", NULL, lib->ns); + if (handle_str) + { + handle_chunk = chunk_from_hex(chunk_from_str(handle_str), + (char *)&ek_handle); + ek_handle = (handle_chunk.len == 4) ? htonl(ek_handle) : 0; + + /* establish protected auth session if ek_handle is set */ + if (ek_handle && read_public(this, ek_handle, &public)) + { + this->mutex->lock(this->mutex); + this->session = tpm_tss_tss2_session_create(ek_handle, &public, + this->sys_context); + this->mutex->unlock(this->mutex); + } + } +} + METHOD(tpm_tss_t, get_random, bool, private_tpm_tss_tss2_t *this, size_t bytes, uint8_t *buffer) { - size_t len, random_len= sizeof(TPM2B_DIGEST)-2; + size_t len, random_len = sizeof(TPM2B_DIGEST)-2; TPM2B_DIGEST random = { random_len, }; uint8_t *pos = buffer; uint32_t rval; + if (!this->session) + { + try_session_start(this); + } + while (bytes > 0) { - len = min(bytes, random_len); + bool success = FALSE; + len = min(bytes, random_len); this->mutex->lock(this->mutex); - rval = Tss2_Sys_GetRandom(this->sys_context, NULL, len, &random, NULL); - this->mutex->unlock(this->mutex); + + rval = Tss2_Sys_GetRandom_Prepare(this->sys_context, len); if (rval != TSS2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_GetRandom failed: 0x%06x", LABEL, rval); - return FALSE; + DBG1(DBG_PTS, "%s Tss2_Sys_GetRandom_Prepare failed: 0x%06x", + LABEL, rval); + goto error; } + + if (this->session && !this->session->set_cmd_auths(this->session)) + { + goto error; + } + + rval = Tss2_Sys_Execute(this->sys_context); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_Execute failed: 0x%06x", rval); + goto error; + } + + if (this->session && !this->session->get_rsp_auths(this->session)) + { + goto error; + } + + rval = Tss2_Sys_GetRandom_Complete(this->sys_context, &random); + if (rval != TSS2_RC_SUCCESS) + { + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetRandom_Complete failed: 0x%06x", + rval); + goto error; + } + success = TRUE; + +error: + this->mutex->unlock(this->mutex); + if (!success) + { + return FALSE; + } + memcpy(pos, random.buffer, random.size); pos += random.size; bytes -= random.size; @@ -1265,8 +1368,8 @@ METHOD(tpm_tss_t, get_data, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_GetCapability failed for " - "TPM2_CAP_TPM_PROPERTIES: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_GetCapability failed for " + "TPM2_CAP_TPM_PROPERTIES: 0x%06x", rval); return FALSE; } max_data_size = min(cap_data.data.tpmProperties.tpmProperty[0].value, @@ -1279,7 +1382,7 @@ METHOD(tpm_tss_t, get_data, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_NV_ReadPublic failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_NV_ReadPublic failed: 0x%06x", rval); return FALSE; } nv_size = nv_public.nvPublic.dataSize; @@ -1304,7 +1407,7 @@ METHOD(tpm_tss_t, get_data, bool, this->mutex->unlock(this->mutex); if (rval != TPM2_RC_SUCCESS) { - DBG1(DBG_PTS,"%s Tss2_Sys_NV_Read failed: 0x%06x", LABEL, rval); + DBG1(DBG_PTS, LABEL "Tss2_Sys_NV_Read failed: 0x%06x", rval); chunk_free(data); return FALSE; } @@ -1352,26 +1455,9 @@ METHOD(tpm_tss_t, get_event_digest, bool, { return FALSE; } - hash_alg = hash_alg_from_tpm_alg_id(alg_id); + hash_alg = hash_alg_from_tpm_alg_id(alg_id); + digest_len = hash_len_from_tpm_alg_id(alg_id); - switch (hash_alg) - { - case HASH_SHA1: - digest_len = HASH_SIZE_SHA1; - break; - case HASH_SHA256: - digest_len = HASH_SIZE_SHA256; - break; - case HASH_SHA384: - digest_len = HASH_SIZE_SHA384; - break; - case HASH_SHA512: - digest_len = HASH_SIZE_SHA512; - break; - default: - DBG2(DBG_PTS, "alg_id: 0x%04x", alg_id); - return FALSE; - } if (hash_alg == alg) { *digest = chunk_alloc(digest_len); @@ -1397,6 +1483,7 @@ METHOD(tpm_tss_t, get_event_digest, bool, METHOD(tpm_tss_t, destroy, void, private_tpm_tss_tss2_t *this) { + DESTROY_IF(this->session); finalize_context(this); this->mutex->destroy(this->mutex); free(this->version_info.ptr); @@ -1443,6 +1530,7 @@ tpm_tss_t *tpm_tss_tss2_create() destroy(this); return NULL; } + return &this->public; } @@ -1467,8 +1555,8 @@ bool tpm_tss_tss2_init(void) { i = 1; } - DBG2(DBG_PTS, "%s \"%s\" in-kernel resource manager is %spresent", - LABEL, tcti_options[0], i ? "not " : ""); + DBG2(DBG_PTS, LABEL "\"%s\" in-kernel resource manager is %spresent", + tcti_options[0], i ? "not " : ""); /* select a dynamic TCTI library (device, tabrmd or mssim) */ tcti_name = lib->settings->get_str(lib->settings, @@ -1485,8 +1573,7 @@ bool tpm_tss_tss2_init(void) } if (!match) { - DBG1(DBG_PTS, "%s \"%s\" is not a valid TCTI library name", - LABEL, tcti_lib); + DBG1(DBG_PTS, LABEL "\"%s\" is not a valid TCTI library name", tcti_lib); return FALSE; } @@ -1497,20 +1584,20 @@ bool tpm_tss_tss2_init(void) tcti_handle = dlopen(tcti_lib, RTLD_LAZY); if (!tcti_handle) { - DBG1(DBG_PTS, "%s could not load \"%s\"", LABEL, tcti_lib); + DBG1(DBG_PTS, LABEL "could not load \"%s\"", tcti_lib); return FALSE; } infofn = (TSS2_TCTI_INFO_FUNC)dlsym(tcti_handle, TSS2_TCTI_INFO_SYMBOL); if (!infofn) { - DBG1(DBG_PTS, "%s symbol \"%s\" not found in \"%s\"", LABEL, - TSS2_TCTI_INFO_SYMBOL, tcti_lib); + DBG1(DBG_PTS, LABEL "symbol \"%s\" not found in \"%s\"", + TSS2_TCTI_INFO_SYMBOL, tcti_lib); tpm_tss_tss2_deinit(); return FALSE; } - DBG2(DBG_PTS, "%s \"%s\" successfully loaded", LABEL, tcti_lib); + DBG2(DBG_PTS, LABEL "\"%s\" successfully loaded", tcti_lib); info = infofn(); tcti_init = info->init; -- 2.47.2