From e32ec0d6d4c9a0c7b61071f04841d6760516d66b Mon Sep 17 00:00:00 2001 From: Shrikant Temburwar Date: Fri, 1 Mar 2024 11:31:53 +0530 Subject: [PATCH] Add support to store FDO Credentials in the TPM (#261) * Add support to store device credentials and device status inside TPM NV storage * Add support to store TPM private keys and device CSR inside TPM NV storage * Add execution logs in clear_tpm_nv.sh * Update TPM code as per specs * Update device key generation according to FDO TPM spec * Update tpm scripts * Add DCActive flag usecase as per FDO TPM spec * - Update DCActive value to bool - Add TPM2_NV_WriteLock/TPM2_NV_ReadLock support - Update readme for FDO TPM usage * Add command to lock the Device CSR Non-Volatile (NV) index for further writes --------- Signed-off-by: Shrikant Temburwar --- .gitignore | 8 + app/blob.c | 25 +- app/main.c | 19 +- cmake/blob_path.cmake | 26 +- cmake/cli_input.cmake | 29 + cmake/extension.cmake | 4 + crypto/common/fdo_crypto_common.c | 4 - crypto/common/fdo_hmac.c | 42 +- crypto/include/tpm20_Utils.h | 143 ++-- crypto/openssl/tpm20_ECDSA_sign_routines.c | 225 ++----- crypto/openssl/tpm20_Utils.c | 396 ++++------- docs/build_conf.md | 9 +- docs/tpm.md | 64 +- include/fdomodules.h | 10 +- lib/credentials_from_file.c | 347 +++++++++- lib/fdo.c | 13 +- lib/include/load_credentials.h | 6 + lib/m-string.c | 20 +- lib/prot/di/msg13.c | 7 + lib/prot/to2/msg70.c | 8 + storage/CMakeLists.txt | 6 + storage/include/storage_al.h | 11 + storage/include/tpm2_nv_storage.h | 85 +++ storage/linux/storage_if_linux.c | 278 ++++++++ storage/linux/tpm2_nv_storage.c | 747 +++++++++++++++++++++ utils/clear_tpm_nv.sh | 35 + utils/tpm_make_ready_ecdsa.sh | 71 +- 27 files changed, 1986 insertions(+), 652 deletions(-) create mode 100644 storage/include/tpm2_nv_storage.h create mode 100644 storage/linux/tpm2_nv_storage.c create mode 100644 utils/clear_tpm_nv.sh diff --git a/.gitignore b/.gitignore index e421fc56..7d7a6df6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,12 @@ *.so *.so.* *.a +*.blob +*.bin +*.pem +*.dat +*.ctx +*.key # Packages # ############ @@ -51,6 +57,7 @@ Thumbs.db ############### CMakeCache.txt CMakeFiles +Makefile CMakeScripts cmake_install.cmake install_manifest.txt @@ -92,6 +99,7 @@ buildNumber.properties .metadata *.iml *.ipr +.vscode # Visual Studio files # ####################### diff --git a/app/blob.c b/app/blob.c index 55b75acc..269c680d 100644 --- a/app/blob.c +++ b/app/blob.c @@ -36,6 +36,7 @@ #if defined(DEVICE_TPM20_ENABLED) #include "tpm20_Utils.h" #include "fdo_crypto.h" +#include "tpm2_nv_storage.h" #endif #if !defined(DEVICE_TPM20_ENABLED) @@ -58,7 +59,6 @@ static int32_t gen_rdm_bytestream(uint8_t *random_buffer, size_t num_bytes) } return 0; } -#endif int32_t configure_normal_blob(void) { @@ -71,19 +71,6 @@ int32_t configure_normal_blob(void) uint8_t *signed_normal_blob = NULL; size_t signed_normal_blob_size = 0; int32_t ret = -1; - -#if defined(DEVICE_TPM20_ENABLED) - if (0 == is_valid_tpm_data_protection_key_present()) { - if (0 != fdo_generate_storage_hmac_key()) { - LOG(LOG_ERROR, "Failed to generate TPM data protection" - " key.\n"); - goto err; - } - - LOG(LOG_DEBUG, - "TPM data protection key generated successfully.\n"); - } -#else uint8_t hmac_key[PLATFORM_HMAC_KEY_DEFAULT_LEN] = {0}; size_t key_size_stored = @@ -114,7 +101,6 @@ int32_t configure_normal_blob(void) LOG(LOG_ERROR, "Failed to read plain Normal blob!\n"); goto err; } -#endif raw_normal_blob_size = fdo_blob_size((char *)FDO_CRED_NORMAL, FDO_SDK_RAW_DATA); @@ -158,13 +144,6 @@ int32_t configure_normal_blob(void) "Malloc Failed for sealed Normal Blob buffer!\n"); goto err; } -#if defined(DEVICE_TPM20_ENABLED) - if (0 != fdo_compute_storage_hmac(raw_normal_blob, raw_normal_blob_size, - signed_normal_blob, - PLATFORM_HMAC_SIZE)) { - goto err; - } -#else #if defined(USE_MBEDTLS) if (0 != mbedtls_md_hmac(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), (const uint8_t *)hmac_key, @@ -178,7 +157,6 @@ int32_t configure_normal_blob(void) signed_normal_blob, NULL)) { goto err; } -#endif #endif // copy plain-text size signed_normal_blob[PLATFORM_HMAC_SIZE + 3] = raw_normal_blob_size >> 0; @@ -214,3 +192,4 @@ int32_t configure_normal_blob(void) } return ret; } +#endif \ No newline at end of file diff --git a/app/main.c b/app/main.c index 66d1c007..c1825e92 100644 --- a/app/main.c +++ b/app/main.c @@ -28,6 +28,10 @@ #include "cse_utils.h" #include "cse_tools.h" #endif +#if defined(DEVICE_TPM20_ENABLED) +#include "tpm20_Utils.h" +#include "fdo_crypto.h" +#endif #define STORAGE_NAMESPACE "storage" #define OWNERSHIP_TRANSFER_FILE "data/owner_transfer" @@ -218,19 +222,19 @@ static void print_device_status(void) status = fdo_sdk_get_status(); if (status == FDO_STATE_PRE_DI) { - LOG(LOG_DEBUG, "Device is ready for DI\n"); + LOG(LOG_INFO, "Device is ready for DI\n"); } if (status == FDO_STATE_PRE_TO1) { - LOG(LOG_DEBUG, "Device is ready for Ownership transfer\n"); + LOG(LOG_INFO, "Device is ready for Ownership transfer\n"); } if (status == FDO_STATE_IDLE) { - LOG(LOG_DEBUG, "Device Ownership transfer Done\n"); + LOG(LOG_INFO, "Device Ownership transfer Done\n"); } if (status == FDO_STATE_RESALE) { - LOG(LOG_DEBUG, "Device is ready for Ownership transfer\n"); + LOG(LOG_INFO, "Device is ready for Ownership transfer\n"); } if (status == FDO_STATE_ERROR) { - LOG(LOG_DEBUG, "Error in getting device status\n"); + LOG(LOG_ERROR, "Error in getting device status\n"); } } @@ -292,8 +296,9 @@ int app_main(bool is_resale) } #endif /* SECURE_ELEMENT */ -#if !defined(DEVICE_CSE_ENABLED) - LOG(LOG_DEBUG, "CSE not enabled, Normal Blob Modules loaded!\n"); +#if !defined(DEVICE_CSE_ENABLED) && !defined(DEVICE_TPM20_ENABLED) + LOG(LOG_DEBUG, + "CSE and TPM not enabled, Normal Blob Modules loaded!\n"); if (-1 == configure_normal_blob()) { LOG(LOG_ERROR, "Provisioning Normal blob for the 1st time failed!\n"); diff --git a/cmake/blob_path.cmake b/cmake/blob_path.cmake index ec008ae1..7d6d6b32 100644 --- a/cmake/blob_path.cmake +++ b/cmake/blob_path.cmake @@ -24,7 +24,7 @@ if(TARGET_OS MATCHES linux) -DDEVICE_CSE_ENABLED ) endif() - + if (${MTLS} MATCHES true) client_sdk_compile_definitions( -DSSL_CERT=\"${BLOB_PATH}/data/apiUser.pem\" @@ -35,16 +35,6 @@ if(TARGET_OS MATCHES linux) if (${DA} MATCHES tpm) client_sdk_compile_definitions( -DDEVICE_TPM20_ENABLED - -DTPM_DEVICE_CSR=\"${BLOB_PATH}/data/tpm_device_csr\" - -DTPM_ECDSA_DEVICE_KEY=\"${BLOB_PATH}/data/tpm_ecdsa_priv_pub_blob.key\" - -DTPM_INPUT_DATA_TEMP_FILE=\"${BLOB_PATH}/data/tpm_input_data_temp_file\" - -DTPM_OUTPUT_DATA_TEMP_FILE=\"${BLOB_PATH}/data/tpm_output_data_temp_file\" - -DTPM_HMAC_PUB_KEY=\"${BLOB_PATH}/data/tpm_hmac_pub.key\" - -DTPM_HMAC_PRIV_KEY=\"${BLOB_PATH}/data/tpm_hmac_priv.key\" - -DTPM_HMAC_REPLACEMENT_PUB_KEY=\"${BLOB_PATH}/data/tpm_hmac_replacement_pub.key\" - -DTPM_HMAC_REPLACEMENT_PRIV_KEY=\"${BLOB_PATH}/data/tpm_hmac_replacement_priv.key\" - -DTPM_HMAC_DATA_PUB_KEY=\"${BLOB_PATH}/data/tpm_hmac_data_pub.key\" - -DTPM_HMAC_DATA_PRIV_KEY=\"${BLOB_PATH}/data/tpm_hmac_data_priv.key\" ) endif() @@ -176,9 +166,11 @@ if(TARGET_OS MATCHES linux) # Configure if needed at a later point # configure_file(${BLOB_PATH}/data/Normal.blob NEWLINE_STYLE DOS) -file(WRITE ${BLOB_PATH}/data/platform_iv.bin "") -file(WRITE ${BLOB_PATH}/data/platform_hmac_key.bin "") -file(WRITE ${BLOB_PATH}/data/platform_aes_key.bin "") -file(WRITE ${BLOB_PATH}/data/Normal.blob "") -file(WRITE ${BLOB_PATH}/data/Secure.blob "") -file(WRITE ${BLOB_PATH}/data/raw.blob "") +if (NOT ${DA} MATCHES tpm) + file(WRITE ${BLOB_PATH}/data/platform_iv.bin "") + file(WRITE ${BLOB_PATH}/data/platform_hmac_key.bin "") + file(WRITE ${BLOB_PATH}/data/platform_aes_key.bin "") + file(WRITE ${BLOB_PATH}/data/Normal.blob "") + file(WRITE ${BLOB_PATH}/data/Secure.blob "") + file(WRITE ${BLOB_PATH}/data/raw.blob "") +endif() diff --git a/cmake/cli_input.cmake b/cmake/cli_input.cmake index 181e86e5..0a84923d 100644 --- a/cmake/cli_input.cmake +++ b/cmake/cli_input.cmake @@ -31,6 +31,7 @@ set (RESALE true) set (REUSE true) set (MTLS false) set (GET_DEV_SERIAL false) +set (LOCK_TPM true) #for CSE set (CSE_SHUTDOWN true) @@ -883,3 +884,31 @@ endif() set(CACHED_GET_DEV_SERIAL ${GET_DEV_SERIAL} CACHE STRING "Selected GET_DEV_SERIAL") message("Selected GET_DEV_SERIAL ${GET_DEV_SERIAL}") ########################################### +# FOR LOCK TPM +if (${DA} MATCHES tpm) + get_property(cached_lock_tpm_value CACHE LOCK_TPM PROPERTY VALUE) + + set(lock_tpm_cli_arg ${cached_lock_tpm_value}) + if(lock_tpm_cli_arg STREQUAL CACHED_LOCK_TPM) + unset(lock_tpm_cli_arg) + endif() + + set(lock_tpm_app_cmake_lists ${LOCK_TPM}) + if(cached_lock_tpm_value STREQUAL LOCK_TPM) + unset(lock_tpm_app_cmake_lists) + endif() + + if(DEFINED CACHED_LOCK_TPM) + if ((DEFINED lock_tpm_cli_arg) AND (NOT(CACHED_LOCK_TPM STREQUAL lock_tpm_cli_arg))) + message(WARNING "Need to do make pristine before cmake args can change.") + endif() + set(LOCK_TPM ${CACHED_LOCK_TPM}) + elseif(DEFINED lock_tpm_cli_arg) + set(LOCK_TPM ${lock_tpm_cli_arg}) + elseif(DEFINED lock_tpm_app_cmake_lists) + set(LOCK_TPM ${lock_tpm_app_cmake_lists}) + endif() + + set(CACHED_LOCK_TPM ${LOCK_TPM} CACHE STRING "Selected LOCK_TPM") + message("Selected LOCK_TPM ${LOCK_TPM}") +endif() \ No newline at end of file diff --git a/cmake/extension.cmake b/cmake/extension.cmake index 152715b5..cc5576ee 100644 --- a/cmake/extension.cmake +++ b/cmake/extension.cmake @@ -279,4 +279,8 @@ endif() if(${GET_DEV_SERIAL} STREQUAL true) client_sdk_compile_definitions(-DGET_DEV_SERIAL) endif() + +if(${LOCK_TPM} STREQUAL true) + client_sdk_compile_definitions(-DLOCK_TPM) +endif() ############################################################ diff --git a/crypto/common/fdo_crypto_common.c b/crypto/common/fdo_crypto_common.c index edab0c43..9f357db8 100644 --- a/crypto/common/fdo_crypto_common.c +++ b/crypto/common/fdo_crypto_common.c @@ -114,10 +114,6 @@ int32_t fdo_crypto_close(void) ret = crypto_close(); /* CLeanup of context structs */ cleanup_ctx(); -#if defined(DEVICE_TPM20_ENABLED) - /* clear the replacement hmac key objects */ - fdo_tpm_clear_replacement_hmac_key(); -#endif return ret; } diff --git a/crypto/common/fdo_hmac.c b/crypto/common/fdo_hmac.c index c347eb37..db82fa1c 100644 --- a/crypto/common/fdo_hmac.c +++ b/crypto/common/fdo_hmac.c @@ -14,6 +14,7 @@ #if defined(DEVICE_TPM20_ENABLED) #include "tpm20_Utils.h" +#include "tpm2_nv_storage.h" #endif #if defined(DEVICE_CSE_ENABLED) @@ -151,15 +152,14 @@ int32_t fdo_device_ov_hmac(uint8_t *OVHdr, size_t OVHdr_len, uint8_t *hmac, if (is_replacement_hmac) { #if defined(DEVICE_TPM20_ENABLED) return fdo_tpm_get_hmac(OVHdr, OVHdr_len, hmac, hmac_len, - TPM_HMAC_REPLACEMENT_PUB_KEY, - TPM_HMAC_REPLACEMENT_PRIV_KEY); + TPM_HMAC_KEY_PERSISTANT_HANDLE); #else keyset = get_replacement_OV_key(); #endif } else { #if defined(DEVICE_TPM20_ENABLED) return fdo_tpm_get_hmac(OVHdr, OVHdr_len, hmac, hmac_len, - TPM_HMAC_PUB_KEY, TPM_HMAC_PRIV_KEY); + TPM_HMAC_KEY_PERSISTANT_HANDLE); #else keyset = get_OV_key(); #endif @@ -225,8 +225,7 @@ int32_t fdo_generate_ov_hmac_key(void) int32_t ret = -1; #if defined(DEVICE_TPM20_ENABLED) - if (0 != - fdo_tpm_generate_hmac_key(TPM_HMAC_PUB_KEY, TPM_HMAC_PRIV_KEY)) { + if (0 != fdo_tpm_generate_hmac_key(TPM_HMAC_KEY_PERSISTANT_HANDLE)) { LOG(LOG_ERROR, "Failed to generate device HMAC key" " from TPM.\n"); return ret; @@ -270,8 +269,7 @@ int32_t fdo_generate_ov_replacement_hmac_key(void) int32_t ret = -1; #if defined(DEVICE_TPM20_ENABLED) - if (0 != fdo_tpm_generate_hmac_key(TPM_HMAC_REPLACEMENT_PUB_KEY, - TPM_HMAC_REPLACEMENT_PRIV_KEY)) { + if (0 != fdo_tpm_generate_hmac_key(TPM_HMAC_KEY_PERSISTANT_HANDLE)) { LOG(LOG_ERROR, "Failed to generate device replacement HMAC key" " from TPM.\n"); return ret; @@ -316,12 +314,6 @@ int32_t fdo_commit_ov_replacement_hmac_key(void) int32_t ret = -1; #if defined(DEVICE_TPM20_ENABLED) - if (0 != fdo_tpm_commit_replacement_hmac_key()) { - LOG(LOG_ERROR, "Failed to commit device replacement HMAC key" - " for TPM.\n"); - return ret; - } - ret = 0; #else fdo_byte_array_t **secret = get_replacement_OV_key(); @@ -365,17 +357,6 @@ int32_t fdo_compute_storage_hmac(const uint8_t *data, uint32_t data_length, goto error; } -#if defined(DEVICE_TPM20_ENABLED) - if (0 != fdo_tpm_get_hmac(data, data_length, computed_hmac, - computed_hmac_size, TPM_HMAC_DATA_PUB_KEY, - TPM_HMAC_DATA_PRIV_KEY)) { - LOG(LOG_ERROR, "TPM HMAC Computation failed!\n"); - goto error; - } - - LOG(LOG_DEBUG, "TPM HMAC computed successfully!\n"); - -#else uint8_t hmac_key[PLATFORM_HMAC_KEY_DEFAULT_LEN] = {0}; if (!get_platform_hmac_key(hmac_key, PLATFORM_HMAC_KEY_DEFAULT_LEN)) { @@ -403,7 +384,6 @@ int32_t fdo_compute_storage_hmac(const uint8_t *data, uint32_t data_length, #endif return ret; } -#endif /** * fdo_generate_storage_hmac_key function generates Storage HMAC key @@ -418,18 +398,6 @@ int32_t fdo_generate_storage_hmac_key(void) #if defined(TARGET_OS_OPTEE) return 0; - -#elif defined(DEVICE_TPM20_ENABLED) - if (0 != fdo_tpm_generate_hmac_key(TPM_HMAC_DATA_PUB_KEY, - TPM_HMAC_DATA_PRIV_KEY)) { - LOG(LOG_ERROR, "Failed to generate TPM data protection " - "key.\n"); - return ret; - } - - ret = 0; - LOG(LOG_DEBUG, "TPM data protection key generated successfully.\n"); - #else uint8_t hmac_key[PLATFORM_HMAC_KEY_DEFAULT_LEN] = {0}; diff --git a/crypto/include/tpm20_Utils.h b/crypto/include/tpm20_Utils.h index fc4fde4d..32e5f125 100644 --- a/crypto/include/tpm20_Utils.h +++ b/crypto/include/tpm20_Utils.h @@ -36,78 +36,87 @@ static const TPM2B_PUBLIC in_public_primary_key_template = { .size = 0, - .publicArea = - { - .type = TPM2_ALG_ECC, - .nameAlg = FDO_TPM2_ALG_SHA, - .objectAttributes = - (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | - TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM | - TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN), - .authPolicy = - { - .size = 0, - }, + .publicArea = { + .type = TPM2_ALG_ECC, + .nameAlg = FDO_TPM2_ALG_SHA, + .objectAttributes = + (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_RESTRICTED | + TPMA_OBJECT_DECRYPT | TPMA_OBJECT_FIXEDTPM | + TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN), + .authPolicy = + { + .size = 0, + }, + .parameters + .eccDetail = {.symmetric = {.algorithm = TPM2_ALG_AES, + .keyBits.aes = TPM_AES_BITS, + .mode.aes = TPM2_ALG_CFB}, + .scheme = {.scheme = TPM2_ALG_NULL, .details = {{0}}}, + .curveID = FDO_TPM2_CURVE_ID, + .kdf = {.scheme = TPM2_ALG_NULL, .details = {{0}}}}, + .unique.ecc = {.x = {.size = 0, .buffer = {0}}, + .y = {.size = 0, .buffer = {0}}}}}; - .parameters.eccDetail = {.symmetric = - { - .algorithm = TPM2_ALG_AES, - .keyBits.aes = TPM_AES_BITS, - .mode.aes = TPM2_ALG_CFB, - }, - .scheme = - { - .scheme = TPM2_ALG_NULL, - .details = {{0}}, - }, - .curveID = FDO_TPM2_CURVE_ID, - .kdf = {.scheme = TPM2_ALG_NULL, - .details = {{0}}}}, - .unique.ecc = - { - .x = {.size = 0, .buffer = {0}}, - .y = {.size = 0, .buffer = {0}}, - }, - }, -}; +static const TPM2B_PUBLIC in_publicECKey_template = { + .size = 0, + .publicArea = { + .type = TPM2_ALG_ECC, + .nameAlg = FDO_TPM2_ALG_SHA, + .objectAttributes = + (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_SIGN_ENCRYPT | + TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | + TPMA_OBJECT_SENSITIVEDATAORIGIN), + .authPolicy = + { + .size = 0, + }, + .parameters.eccDetail = + {.symmetric = {.algorithm = TPM2_ALG_NULL, + .keyBits.aes = 0, + .mode.aes = 0}, + .scheme = {.scheme = TPM2_ALG_ECDSA, + .details = {.ecdsa = {.hashAlg = FDO_TPM2_ALG_SHA}}}, + .curveID = FDO_TPM2_CURVE_ID, + .kdf = {.scheme = TPM2_ALG_NULL, .details = {{0}}}}, + .unique.ecc = {.x = {.size = 0, .buffer = {0}}, + .y = {.size = 0, .buffer = {0}}}}}; static const TPM2B_PUBLIC in_publicHMACKey_template = { .size = 0, - .publicArea = - { - .type = TPM2_ALG_KEYEDHASH, - .nameAlg = FDO_TPM2_ALG_SHA, - .objectAttributes = - (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_DECRYPT | - TPMA_OBJECT_SIGN_ENCRYPT | TPMA_OBJECT_FIXEDTPM | - TPMA_OBJECT_FIXEDPARENT | TPMA_OBJECT_SENSITIVEDATAORIGIN), - .authPolicy = - { - .size = 0, - }, - - .parameters.keyedHashDetail = - { - .scheme = - { - .scheme = TPM2_ALG_NULL, - .details = {{0}}, - }, - }, - .unique.keyedHash = - { - .size = 0, - .buffer = {0}, - }, - }, -}; + .publicArea = { + .type = TPM2_ALG_KEYEDHASH, + .nameAlg = FDO_TPM2_ALG_SHA, + .objectAttributes = + (TPMA_OBJECT_USERWITHAUTH | TPMA_OBJECT_SIGN_ENCRYPT | + TPMA_OBJECT_FIXEDTPM | TPMA_OBJECT_FIXEDPARENT | + TPMA_OBJECT_SENSITIVEDATAORIGIN), + .authPolicy = + { + .size = 0, + }, + .parameters.keyedHashDetail = + {.scheme = {.scheme = TPM2_ALG_HMAC, + .details = {.hmac = {.hashAlg = FDO_TPM2_ALG_SHA}}}}, + .unique.keyedHash = + { + .size = 0, + .buffer = {0}, + }, + }}; int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, - size_t hmac_length, char *tpmHMACPub_key, - char *tpmHMACPriv_key); -int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key); -int32_t fdo_tpm_commit_replacement_hmac_key(void); -void fdo_tpm_clear_replacement_hmac_key(void); -int32_t is_valid_tpm_data_protection_key_present(void); + size_t hmac_length, + TPMI_DH_PERSISTENT persistent_handle); +int32_t fdo_tpm_generate_hmac_key(TPMI_DH_PERSISTENT persistent_handle); + +int32_t fdoTPMEsys_context_init(ESYS_CONTEXT **esys_context); +int32_t fdoTPMEsys_auth_session_init(ESYS_CONTEXT *esys_context, + ESYS_TR *session_handle); +int32_t fdoTPMTSSContext_clean_up(ESYS_CONTEXT **esys_context, + ESYS_TR *auth_session_handle, + ESYS_TR *primary_handle); +int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, + ESYS_TR *primary_handle, + ESYS_TR *auth_session_handle); #endif /* #ifndef __TPM20_UTILS_H__ */ diff --git a/crypto/openssl/tpm20_ECDSA_sign_routines.c b/crypto/openssl/tpm20_ECDSA_sign_routines.c index aa82bf25..0cdf91fe 100644 --- a/crypto/openssl/tpm20_ECDSA_sign_routines.c +++ b/crypto/openssl/tpm20_ECDSA_sign_routines.c @@ -15,9 +15,12 @@ #include #include #include +#include #include "safe_lib.h" #include "util.h" #include "fdo_crypto_hal.h" +#include "tpm20_Utils.h" +#include "tpm2_nv_storage.h" /** * Sign a message using provided ECDSA Private Keys. @@ -32,203 +35,123 @@ int32_t crypto_hal_ecdsa_sign(const uint8_t *data, size_t data_len, unsigned char *message_signature, size_t *signature_length) { + int32_t ret = -1; - EVP_PKEY *pkey = NULL; - ECDSA_SIG *sig = NULL; - unsigned char *sig_r = NULL; + TSS2_RC ret_val = TPM2_RC_FAILURE; + ESYS_CONTEXT *esys_context = NULL; + ESYS_TR primary_key_handle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + ESYS_TR tpm_ec_key_handle = ESYS_TR_NONE; int sig_r_len = 0; - unsigned char *sig_s = NULL; int sig_s_len = 0; - unsigned char *der_sig = NULL; - size_t der_sig_len = 0; - OSSL_PROVIDER *prov = NULL; - EVP_MD_CTX *mdctx = NULL; - OSSL_STORE_CTX *ctx = NULL; - OSSL_STORE_INFO *info = NULL; + TPM2B_DIGEST *digest = NULL; + TPMT_TK_HASHCHECK *validation = NULL; + TPMT_SIGNATURE *signature = NULL; + TPM2B_MAX_BUFFER input_data; + // Set the signature scheme to ECDSA with SHA256 + TPMT_SIG_SCHEME inScheme = { + .scheme = TPM2_ALG_ECDSA, + .details = {.rsapss = {.hashAlg = FDO_TPM2_ALG_SHA}}}; if (!data || !data_len || !message_signature || !signature_length) { LOG(LOG_ERROR, "Invalid Parameters received."); goto error; } - // Load OpenSSL TPM provider - if ((prov = OSSL_PROVIDER_load(NULL, "tpm2")) == NULL) { - LOG(LOG_ERROR, "Failed to load tpm provider!\n"); - goto error; - } - - // Read the key - if ((ctx = OSSL_STORE_open(TPM_ECDSA_DEVICE_KEY, NULL, NULL, NULL, - NULL)) == NULL) { - LOG(LOG_ERROR, "Error during OSSL_STORE_open\n"); - goto error; - } - - while (!OSSL_STORE_eof(ctx) && (info = OSSL_STORE_load(ctx)) != NULL) { - if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) { - pkey = OSSL_STORE_INFO_get1_PKEY(info); - break; - } - OSSL_STORE_INFO_free(info); - info = NULL; - } - - if (!pkey) { - LOG(LOG_ERROR, "Error during reading Private key.\n"); - goto error; - } - - LOG(LOG_DEBUG, "Private key successfully loaded in TPM format.\n"); - - // Set EVP properties to use TPM provider - if (EVP_set_default_properties(NULL, "provider=tpm2") == 0) { - LOG(LOG_ERROR, "failed to load tpm provider!\n"); - goto error; - } - - // Create the Message Digest Context - mdctx = EVP_MD_CTX_create(); - if (!mdctx) { - LOG(LOG_ERROR, "Failed to create message digest context\n"); - goto error; - } - -#if defined(ECDSA256_DA) - if (1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey)) { - LOG(LOG_ERROR, "EVP sign init failed \n"); - goto error; - } -#elif defined(ECDSA384_DA) - if (1 != EVP_DigestSignInit(mdctx, NULL, EVP_sha384(), NULL, pkey)) { - LOG(LOG_ERROR, "EVP sign init failed \n"); - goto error; - } -#endif - if (1 != EVP_DigestSignUpdate(mdctx, data, data_len)) { - LOG(LOG_ERROR, "EVP sign update failed \n"); - goto error; - } - // First call with NULL param to obtain the DER encoded signature length - if (1 != EVP_DigestSignFinal(mdctx, NULL, &der_sig_len)) { - LOG(LOG_ERROR, "EVP sign final for size failed \n"); + input_data.size = data_len; + if (memcpy_s(input_data.buffer, input_data.size, (char *)data, + (size_t)data_len) != 0) { + LOG(LOG_ERROR, "Memcpy Failed\n"); goto error; } - if (der_sig_len <= 0) { + if (0 != fdoTPMGenerate_primary_key_context(&esys_context, + &primary_key_handle, + &auth_session_handle)) { LOG(LOG_ERROR, - "EVP_DigestSignFinal returned invalid signature length.\n"); + "Failed to create primary key context from TPM.\n"); goto error; } - der_sig = fdo_alloc(der_sig_len); - if (!der_sig) { - LOG(LOG_ERROR, "Signature alloc Failed\n"); - goto error; - } - // second call with actual param to obtain the DEr encoded signature - if (1 != EVP_DigestSignFinal(mdctx, der_sig, &der_sig_len)) { - LOG(LOG_ERROR, "EVP sign final failed \n"); - goto error; - } + ret_val = Esys_TR_FromTPMPublic( + esys_context, TPM_DEVICE_KEY_PERSISTANT_HANDLE, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, &tpm_ec_key_handle); - // Set EVP properties back to default. - if (EVP_set_default_properties(NULL, "provider=default") == 0) { - LOG(LOG_DEBUG, "failed to load tpm provider!\n"); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to load EC Key Context.\n"); goto error; } - // Decode DER encoded signature to convert to raw format - sig = ECDSA_SIG_new(); - const unsigned char *sig_input = der_sig; - if (!sig || d2i_ECDSA_SIG(&sig, &sig_input, der_sig_len) == NULL) { - LOG(LOG_ERROR, "DER to EC_KEY struct decoding failed!\n"); + ret_val = Esys_Hash(esys_context, ESYS_TR_NONE, ESYS_TR_NONE, + ESYS_TR_NONE, &input_data, FDO_TPM2_ALG_SHA, + ESYS_TR_RH_OWNER, &digest, &validation); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to create hash.\n"); goto error; } - - // both r and s are maintained by sig, no need to free explicitly - const BIGNUM *r = ECDSA_SIG_get0_r(sig); - const BIGNUM *s = ECDSA_SIG_get0_s(sig); - if (!r || !s) { - LOG(LOG_ERROR, "Failed to read r and/or s\n"); + ret_val = Esys_Sign(esys_context, tpm_ec_key_handle, + auth_session_handle, ESYS_TR_NONE, ESYS_TR_NONE, + digest, &inScheme, validation, &signature); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to create Sign Key.\n"); goto error; } - sig_r_len = BN_num_bytes(r); + sig_r_len = signature->signature.ecdsa.signatureR.size; if (sig_r_len <= 0) { LOG(LOG_ERROR, "Sig r len invalid\n"); goto error; } - sig_r = fdo_alloc(sig_r_len); - if (!sig_r) { - LOG(LOG_ERROR, "Sig r alloc Failed\n"); - goto error; - } - if (BN_bn2bin(r, sig_r) <= 0) { - LOG(LOG_ERROR, "Sig r conversion Failed\n"); - goto error; - } - sig_s_len = BN_num_bytes(s); + sig_s_len = signature->signature.ecdsa.signatureS.size; if (sig_r_len <= 0) { LOG(LOG_ERROR, "Sig s len invalid\n"); goto error; } - sig_s = fdo_alloc(sig_s_len); - if (!sig_s) { - LOG(LOG_ERROR, "Sig s alloc Failed\n"); - goto error; - } - if (BN_bn2bin(s, sig_s) <= 0) { - LOG(LOG_ERROR, "Sig s conversion Failed\n"); - goto error; - } *signature_length = sig_r_len + sig_s_len; - if (memcpy_s(message_signature, *signature_length, (char *)sig_r, + if (memcpy_s(message_signature, *signature_length, + (char *)signature->signature.ecdsa.signatureR.buffer, (size_t)sig_r_len) != 0) { LOG(LOG_ERROR, "Memcpy Failed\n"); goto error; } if (memcpy_s(message_signature + sig_r_len, *signature_length, - (char *)sig_s, (size_t)sig_s_len) != 0) { + (char *)signature->signature.ecdsa.signatureS.buffer, + (size_t)sig_s_len) != 0) { LOG(LOG_ERROR, "Memcpy Failed\n"); goto error; } - ret = 0; error: - if (pkey) { - EVP_PKEY_free(pkey); - } - if (sig) { - ECDSA_SIG_free(sig); - } - if (der_sig) { - fdo_free(der_sig); - sig_input = NULL; - } - if (sig_r) { - fdo_free(sig_r); - } - if (sig_s) { - fdo_free(sig_s); - } - if (prov) { - OSSL_PROVIDER_unload(prov); - prov = NULL; - } - if (mdctx) { - EVP_MD_CTX_free(mdctx); - mdctx = NULL; - } - if (ctx) { - OSSL_STORE_close(ctx); - ctx = NULL; - } - if (info) { - OSSL_STORE_INFO_free(info); - info = NULL; + if (esys_context) { + if (tpm_ec_key_handle != ESYS_TR_NONE) { + if (Esys_TR_Close(esys_context, &tpm_ec_key_handle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to flush HMAC key handle.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, + "HMAC key handle flushed successfully.\n"); + tpm_ec_key_handle = ESYS_TR_NONE; + } + } + if (0 != fdoTPMTSSContext_clean_up(&esys_context, + &auth_session_handle, + &primary_key_handle)) { + LOG(LOG_ERROR, + "Failed to tear down all the TSS context.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, "TSS context flushed successfully.\n"); + } } + + TPM2_ZEROISE_FREE(digest); + TPM2_ZEROISE_FREE(validation); + TPM2_ZEROISE_FREE(signature); + return ret; -} +} \ No newline at end of file diff --git a/crypto/openssl/tpm20_Utils.c b/crypto/openssl/tpm20_Utils.c index 7df8c004..ed680caf 100644 --- a/crypto/openssl/tpm20_Utils.c +++ b/crypto/openssl/tpm20_Utils.c @@ -10,20 +10,11 @@ */ #include "util.h" #include "safe_lib.h" +#include "tpm2_nv_storage.h" #include "tpm20_Utils.h" #include "fdo_crypto_hal.h" #include "storage_al.h" -static int32_t fdoTPMEsys_context_init(ESYS_CONTEXT **esys_context); -static int32_t fdoTPMEsys_auth_session_init(ESYS_CONTEXT *esys_context, - ESYS_TR *session_handle); -static int32_t fdoTPMTSSContext_clean_up(ESYS_CONTEXT **esys_context, - ESYS_TR *auth_session_handle, - ESYS_TR *primary_handle); -static int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, - ESYS_TR *primary_handle, - ESYS_TR *auth_session_handle); - /** * Generates HMAC using TPM * @@ -32,29 +23,23 @@ static int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, * @param hmac: output buffer to save the HMAC * @param hmac_length: length of the output HMAC buffer *hash length - * @param tpmHMACPub_key: File name of the TPM HMAC public key - * @param tpmHMACPriv_key: File name of the TPM HMAC private key + * @param persistent_handle: Persistent handle of the TPM HMAC public key * @return * 0, on success * -1, on failure */ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, - size_t hmac_length, char *tpmHMACPub_key, - char *tpmHMACPriv_key) + size_t hmac_length, + TPMI_DH_PERSISTENT persistent_handle) { - int32_t ret = -1, ret_val = -1, file_size = 0; + int32_t ret = -1, ret_val = -1; size_t hashed_length = 0; - size_t offset = 0; - uint8_t bufferTPMHMACPriv_key[TPM_HMAC_PRIV_KEY_CONTEXT_SIZE] = {0}; - uint8_t bufferTPMHMACPub_key[TPM_HMAC_PUB_KEY_CONTEXT_SIZE] = {0}; ESYS_CONTEXT *esys_context = NULL; ESYS_TR primary_key_handle = ESYS_TR_NONE; ESYS_TR auth_session_handle = ESYS_TR_NONE; ESYS_TR hmac_key_handle = ESYS_TR_NONE; ESYS_TR sequence_handle = ESYS_TR_NONE; TPMT_TK_HASHCHECK *validation = NULL; - TPM2B_PUBLIC unmarshalHMACPub_key = {0}; - TPM2B_PRIVATE unmarshalHMACPriv_key = {0}; TPM2B_DIGEST *outHMAC = NULL; TPM2B_MAX_BUFFER block = {0}; TPM2B_AUTH null_auth = {0}; @@ -63,8 +48,8 @@ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, /* Validating all input parameters are passed in the function call*/ - if (!data || !data_length || !tpmHMACPub_key || !tpmHMACPriv_key || - !hmac || (hmac_length != PLATFORM_HMAC_SIZE)) { + if (!data || !data_length || !persistent_handle || !hmac || + (hmac_length != PLATFORM_HMAC_SIZE)) { LOG(LOG_ERROR, "Failed to generate HMAC from TPM, invalid parameter" " received.\n"); @@ -85,86 +70,12 @@ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, LOG(LOG_DEBUG, "TPM Primary Key Context created successfully.\n"); - /* Unmarshalling the HMAC Private key from the HMAC Private key file*/ - - file_size = get_file_size(tpmHMACPriv_key); - - if (file_size != TPM_HMAC_PRIV_KEY_CONTEXT_SIZE_128 && - file_size != TPM_HMAC_PRIV_KEY_CONTEXT_SIZE) { - LOG(LOG_ERROR, "TPM HMAC Private Key file size incorrect.\n"); - goto err; - } - - LOG(LOG_DEBUG, - "TPM HMAC Private Key file size retreived successfully.\n"); - - ret_val = read_buffer_from_file(tpmHMACPriv_key, bufferTPMHMACPriv_key, - file_size); - - if (ret_val != 0) { - LOG(LOG_ERROR, - "Failed to load TPM HMAC Private Key into buffer.\n"); - goto err; - } - - LOG(LOG_DEBUG, "TPM HMAC Private Key file content copied successfully" - " to buffer.\n"); - - ret_val = Tss2_MU_TPM2B_PRIVATE_Unmarshal( - bufferTPMHMACPriv_key, file_size, &offset, &unmarshalHMACPriv_key); - - if (ret_val != TSS2_RC_SUCCESS) { - LOG(LOG_ERROR, "Failed to unmarshal TPM HMAC Private Key.\n"); - goto err; - } - - LOG(LOG_DEBUG, - "TPM HMAC Private Key Unmarshal complete successfully.\n"); - - /* Unmarshalling the HMAC Public key from the HMAC public key file*/ - - file_size = get_file_size(tpmHMACPub_key); - - if (file_size != TPM_HMAC_PUB_KEY_CONTEXT_SIZE) { - LOG(LOG_ERROR, "TPM HMAC Private Key file size incorrect.\n"); - goto err; - } - - LOG(LOG_DEBUG, - "TPM HMAC Public Key file size retreived successfully.\n"); - - ret_val = read_buffer_from_file(tpmHMACPub_key, bufferTPMHMACPub_key, - file_size); - - if (ret_val != 0) { - LOG(LOG_ERROR, - "Failed to load TPM HMAC Public key into buffer.\n"); - goto err; - } - - LOG(LOG_DEBUG, "TPM HMAC Public Key file content copied successfully" - " to buffer.\n"); - - offset = 0; - - ret_val = Tss2_MU_TPM2B_PUBLIC_Unmarshal( - bufferTPMHMACPub_key, file_size, &offset, &unmarshalHMACPub_key); - - if (ret_val != TSS2_RC_SUCCESS) { - LOG(LOG_ERROR, "Failed to unmarshal TPM HMAC Public Key.\n"); - goto err; - } - - LOG(LOG_DEBUG, - "TPM HMAC Public Key Unmarshal complete successfully.\n"); - /* Loading the TPM Primary key, HMAC public key and HMAC Private Key to * generate the HMAC Key Context */ ret_val = - Esys_Load(esys_context, primary_key_handle, auth_session_handle, - ESYS_TR_NONE, ESYS_TR_NONE, &unmarshalHMACPriv_key, - &unmarshalHMACPub_key, &hmac_key_handle); + Esys_TR_FromTPMPublic(esys_context, persistent_handle, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, &hmac_key_handle); if (ret_val != TSS2_RC_SUCCESS) { LOG(LOG_ERROR, "Failed to load HMAC Key Context.\n"); @@ -313,7 +224,7 @@ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, err: if (esys_context) { if (hmac_key_handle != ESYS_TR_NONE) { - if (Esys_FlushContext(esys_context, hmac_key_handle) != + if (Esys_TR_Close(esys_context, &hmac_key_handle) != TSS2_RC_SUCCESS) { LOG(LOG_ERROR, "Failed to flush HMAC key handle.\n"); @@ -336,10 +247,6 @@ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, } TPM2_ZEROISE_FREE(validation); TPM2_ZEROISE_FREE(outHMAC); - memset_s(&unmarshalHMACPriv_key, sizeof(unmarshalHMACPriv_key), 0); - memset_s(&unmarshalHMACPub_key, sizeof(unmarshalHMACPub_key), 0); - memset_s(bufferTPMHMACPriv_key, sizeof(bufferTPMHMACPriv_key), 0); - memset_s(bufferTPMHMACPub_key, sizeof(bufferTPMHMACPub_key), 0); return ret; } @@ -347,19 +254,21 @@ int32_t fdo_tpm_get_hmac(const uint8_t *data, size_t data_length, uint8_t *hmac, /** * Generates HMAC Key inside TPM * - * @param tpmHMACPub_key: File name of the TPM HMAC public key - * @param tpmHMACPriv_key: File name of the TPM HMAC private key + * @param persistent_handle: Persistent handle of the TPM HMAC key * @return * 0, on success * -1, on failure */ -int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key) +int32_t fdo_tpm_generate_hmac_key(TPMI_DH_PERSISTENT persistent_handle) { int32_t ret = -1; TSS2_RC ret_val = TPM2_RC_FAILURE; ESYS_CONTEXT *esys_context = NULL; ESYS_TR primary_key_handle = ESYS_TR_NONE; ESYS_TR auth_session_handle = ESYS_TR_NONE; + ESYS_TR object_handle = ESYS_TR_NONE; + ESYS_TR pub_object_handle = ESYS_TR_NONE; + ESYS_TR persistentHandle = ESYS_TR_NONE; TPM2B_PUBLIC *out_public = NULL; TPM2B_PRIVATE *out_private = NULL; TPM2B_CREATION_DATA *creation_data = NULL; @@ -371,25 +280,13 @@ int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key) TPML_PCR_SELECTION creationPCR = {0}; /* Using same buffer for both public and private context, private context size > public context size */ - uint8_t buffer[TPM_HMAC_PRIV_KEY_CONTEXT_SIZE] = {0}; - size_t offset = 0; - if (!tpmHMACPub_key || !tpmHMACPriv_key) { + if (!persistent_handle) { LOG(LOG_ERROR, "Failed to generate HMAC Key," "invalid parameters received.\n"); goto err; } - if ((file_exists(tpmHMACPub_key) && !remove(tpmHMACPub_key)) && - (file_exists(tpmHMACPriv_key) && !remove(tpmHMACPriv_key))) { - LOG(LOG_DEBUG, "Successfully deleted old HMAC key.\n"); - } else if (file_exists(tpmHMACPub_key) || - file_exists(tpmHMACPriv_key)) { - LOG(LOG_DEBUG, "HMAC key generation failed," - "failed to delete the old HMAC key.\n"); - goto err; - } - if (0 != fdoTPMGenerate_primary_key_context(&esys_context, &primary_key_handle, &auth_session_handle)) { @@ -409,38 +306,62 @@ int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key) goto err; } - ret_val = Tss2_MU_TPM2B_PUBLIC_Marshal(out_public, buffer, - sizeof(buffer), &offset); + ret_val = Esys_Load(esys_context, primary_key_handle, + auth_session_handle, ESYS_TR_NONE, ESYS_TR_NONE, + out_private, out_public, &object_handle); if (ret_val != TSS2_RC_SUCCESS) { - LOG(LOG_ERROR, - "Failed to serialize the public HMAC key context.\n"); + LOG(LOG_ERROR, "Esys_Load failed: 0x%x\n", ret_val); + Esys_Finalize(&esys_context); goto err; } - if ((int32_t)offset != - fdo_blob_write(tpmHMACPub_key, FDO_SDK_RAW_DATA, buffer, offset)) { - LOG(LOG_ERROR, "Failed to save the public HMAC key context.\n"); + // Search the persistent Handle + TPMS_CAPABILITY_DATA *capability_data = NULL; + ret_val = Esys_GetCapability( + esys_context, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_HANDLES, persistent_handle, 1, NULL, &capability_data); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_GetCapability failed!\n"); goto err; } - LOG(LOG_DEBUG, "Saved HMAC public key context of size %zu.\n", offset); - offset = 0; - ret_val = Tss2_MU_TPM2B_PRIVATE_Marshal(out_private, buffer, - sizeof(buffer), &offset); - if (ret_val != TSS2_RC_SUCCESS) { - LOG(LOG_ERROR, - "Failed to serialize the private HMAC key context.\n"); - goto err; + int exists = + (capability_data->data.handles.count > 0 && + capability_data->data.handles.handle[0] == persistent_handle); + if (exists == 1) { + ret_val = Esys_TR_FromTPMPublic( + esys_context, persistent_handle, ESYS_TR_NONE, ESYS_TR_NONE, + ESYS_TR_NONE, &persistentHandle); + + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to load HMAC Key Context.\n"); + goto err; + } + + ret_val = Esys_EvictControl( + esys_context, ESYS_TR_RH_OWNER, persistentHandle, + auth_session_handle, ESYS_TR_NONE, ESYS_TR_NONE, 0, + &pub_object_handle); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_EvictControl failed: 0x%x\n", + ret_val); + Esys_Finalize(&esys_context); + goto err; + } } - if ((int32_t)offset != - fdo_blob_write(tpmHMACPriv_key, FDO_SDK_RAW_DATA, buffer, offset)) { - LOG(LOG_ERROR, - "Failed to save the private HMAC key context.\n"); + ret_val = Esys_EvictControl( + esys_context, ESYS_TR_RH_OWNER, object_handle, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, persistent_handle, &pub_object_handle); + if (ret_val != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_EvictControl failed: 0x%x\n", ret_val); goto err; } - LOG(LOG_DEBUG, "Saved HMAC private key context of size %zu.\n", offset); + LOG(LOG_DEBUG, + "Saved HMAC private key context inside persistance memory at " + "%d.\n", + persistent_handle); LOG(LOG_DEBUG, "HMAC Key generated successfully!.\n"); ret = 0; @@ -452,10 +373,54 @@ int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key) TPM2_ZEROISE_FREE(creation_hash); TPM2_ZEROISE_FREE(creation_ticket); - if (esys_context && - (0 != fdoTPMTSSContext_clean_up(&esys_context, &auth_session_handle, - &primary_key_handle))) { - LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + if (esys_context) { + if (object_handle != ESYS_TR_NONE) { + if (Esys_TR_Close(esys_context, &object_handle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to flush object_handle.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, + "object_handle flushed successfully.\n"); + object_handle = ESYS_TR_NONE; + } + } + + if (pub_object_handle != ESYS_TR_NONE) { + if (Esys_TR_Close(esys_context, &pub_object_handle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to flush pub_object_handle.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, "pub_object_handle flushed " + "successfully.\n"); + pub_object_handle = ESYS_TR_NONE; + } + } + + if (persistentHandle != ESYS_TR_NONE) { + if (Esys_TR_Close(esys_context, &persistentHandle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to flush persistent handle.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, "persistent handle flushed " + "successfully.\n"); + persistentHandle = ESYS_TR_NONE; + } + } + if (0 != fdoTPMTSSContext_clean_up(&esys_context, + &auth_session_handle, + &primary_key_handle)) { + LOG(LOG_ERROR, + "Failed to tear down all the TSS context.\n"); + ret = -1; + } else { + LOG(LOG_DEBUG, "TSS context flushed successfully.\n"); + } } return ret; @@ -471,9 +436,9 @@ int32_t fdo_tpm_generate_hmac_key(char *tpmHMACPub_key, char *tpmHMACPriv_key) * 0, on success * -1, on failure */ -static int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, - ESYS_TR *primary_key_handle, - ESYS_TR *auth_session_handle) +int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, + ESYS_TR *primary_key_handle, + ESYS_TR *auth_session_handle) { int ret = -1; TSS2_RC ret_val = TPM2_RC_FAILURE; @@ -544,7 +509,7 @@ static int32_t fdoTPMGenerate_primary_key_context(ESYS_CONTEXT **esys_context, * 0, on success * -1, on failure */ -static int32_t fdoTPMEsys_context_init(ESYS_CONTEXT **esys_context) +int32_t fdoTPMEsys_context_init(ESYS_CONTEXT **esys_context) { int ret = -1; TSS2_TCTI_CONTEXT *tcti_context = NULL; @@ -588,8 +553,8 @@ static int32_t fdoTPMEsys_context_init(ESYS_CONTEXT **esys_context) * 0, on success * -1, on failure */ -static int32_t fdoTPMEsys_auth_session_init(ESYS_CONTEXT *esys_context, - ESYS_TR *session_handle) +int32_t fdoTPMEsys_auth_session_init(ESYS_CONTEXT *esys_context, + ESYS_TR *session_handle) { int ret = -1; TPMT_SYM_DEF symmetric = {0}; @@ -618,9 +583,9 @@ static int32_t fdoTPMEsys_auth_session_init(ESYS_CONTEXT *esys_context, * 0, on success * -1, on failure */ -static int32_t fdoTPMTSSContext_clean_up(ESYS_CONTEXT **esys_context, - ESYS_TR *auth_session_handle, - ESYS_TR *primary_handle) +int32_t fdoTPMTSSContext_clean_up(ESYS_CONTEXT **esys_context, + ESYS_TR *auth_session_handle, + ESYS_TR *primary_handle) { int ret = -1, is_failed = 0; TSS2_TCTI_CONTEXT *tcti_context = NULL; @@ -677,130 +642,3 @@ static int32_t fdoTPMTSSContext_clean_up(ESYS_CONTEXT **esys_context, return 0; } - -/** - * Replace the TPM_HMAC_PRIV_KEY with TPM_HMAC_REPLACEMENT_PRIV_KEY and - * TPM_HMAC_PUB_KEY with TPM_HMAC_REPLACEMENT_PUB_KEY. - * - * @return - * -1, error - * 0, success - */ -int32_t fdo_tpm_commit_replacement_hmac_key(void) -{ - size_t file_size = 0; - // internal return value - int32_t ret_val = -1; - // function return value - int32_t ret = -1; - uint8_t bufferTPMHMACPriv_key[TPM_HMAC_PRIV_KEY_CONTEXT_SIZE] = {0}; - uint8_t bufferTPMHMACPub_key[TPM_HMAC_PUB_KEY_CONTEXT_SIZE] = {0}; - - if (!file_exists(TPM_HMAC_PRIV_KEY) || !file_exists(TPM_HMAC_PUB_KEY) || - !file_exists(TPM_HMAC_REPLACEMENT_PRIV_KEY) || - !file_exists(TPM_HMAC_REPLACEMENT_PUB_KEY)) { - LOG(LOG_ERROR, "One or more HMAC objects are missing.\n"); - goto err; - } - - // read TPM_HMAC_REPLACEMENT_PRIV_KEY contents and write it into - // TPM_HMAC_PRIV_KEY - file_size = get_file_size(TPM_HMAC_REPLACEMENT_PRIV_KEY); - - if (file_size != TPM_HMAC_PRIV_KEY_CONTEXT_SIZE_128 && - file_size != TPM_HMAC_PRIV_KEY_CONTEXT_SIZE) { - LOG(LOG_ERROR, - "TPM HMAC Replacement Private Key file size incorrect.\n"); - goto err; - } - - LOG(LOG_DEBUG, "TPM HMAC Replacement Private Key file size retreived " - "successfully.\n"); - - ret_val = read_buffer_from_file(TPM_HMAC_REPLACEMENT_PRIV_KEY, - bufferTPMHMACPriv_key, file_size); - - if (ret_val != 0) { - LOG(LOG_ERROR, "Failed to load TPM HMAC Replacement Private " - "Key into buffer.\n"); - goto err; - } - - if ((int32_t)file_size != - fdo_blob_write(TPM_HMAC_PRIV_KEY, FDO_SDK_RAW_DATA, - bufferTPMHMACPriv_key, file_size)) { - LOG(LOG_ERROR, - "Failed to save the private HMAC key context.\n"); - goto err; - } - - // now, read TPM_HMAC_REPLACEMENT_PUB_KEY contents and write it into - // TPM_HMAC_PUB_KEY - file_size = get_file_size(TPM_HMAC_REPLACEMENT_PUB_KEY); - - if (file_size != TPM_HMAC_PUB_KEY_CONTEXT_SIZE) { - LOG(LOG_ERROR, - "TPM HMAC Replacement Public Key file size incorrect.\n"); - goto err; - } - - LOG(LOG_DEBUG, "TPM HMAC Replacement Public Key file size retreived " - "successfully.\n"); - - ret_val = read_buffer_from_file(TPM_HMAC_REPLACEMENT_PUB_KEY, - bufferTPMHMACPub_key, file_size); - - if (ret_val != 0) { - LOG(LOG_ERROR, "Failed to load TPM HMAC Replacement Public key " - "into buffer.\n"); - goto err; - } - - if ((int32_t)file_size != - fdo_blob_write(TPM_HMAC_PUB_KEY, FDO_SDK_RAW_DATA, - bufferTPMHMACPub_key, file_size)) { - LOG(LOG_ERROR, "Failed to save the public HMAC key context.\n"); - goto err; - } - ret = 0; -err: - return ret; -} - -/** - * Clear the Replacement TPM HMAC key objects, if they exist. - * - */ -void fdo_tpm_clear_replacement_hmac_key(void) -{ - // remove the files if they exist, else return - if (file_exists(TPM_HMAC_REPLACEMENT_PRIV_KEY)) { - if (0 != remove(TPM_HMAC_REPLACEMENT_PRIV_KEY)) { - LOG(LOG_ERROR, "Failed to cleanup private object\n"); - } - } - if (file_exists(TPM_HMAC_REPLACEMENT_PUB_KEY)) { - if (0 != remove(TPM_HMAC_REPLACEMENT_PUB_KEY)) { - LOG(LOG_ERROR, "Failed to cleanup public object\n"); - } - } -} - -/** - * Check whether valid data integrity protection HMAC key is present or not. - * - * @return - * 1, present - * 0, not present - */ -int32_t is_valid_tpm_data_protection_key_present(void) -{ - return (file_exists(TPM_HMAC_DATA_PUB_KEY) && - (TPM_HMAC_PUB_KEY_CONTEXT_SIZE == - get_file_size(TPM_HMAC_DATA_PUB_KEY)) && - file_exists(TPM_HMAC_DATA_PRIV_KEY) && - (TPM_HMAC_PRIV_KEY_CONTEXT_SIZE_128 == - get_file_size(TPM_HMAC_DATA_PRIV_KEY) || - TPM_HMAC_PRIV_KEY_CONTEXT_SIZE == - get_file_size(TPM_HMAC_DATA_PRIV_KEY))); -} diff --git a/docs/build_conf.md b/docs/build_conf.md index 49c2e368..512a3298 100644 --- a/docs/build_conf.md +++ b/docs/build_conf.md @@ -78,15 +78,14 @@ $ ./build/linux-client -ss ``` > ***WARN***: Accepting Self Signed Certificates is not recommended. If compromised, self-signed certificates can pose serious security risks. -``` - +```shell Option to enable SNI(Server Name Indication extension in client(device) msgs to server): SNI=true # SNI support is enabled. (default) SNI=false # SNI support is disabled. ``` > ***Note***: If you have server IP configured in no_proxy environment variable, also add the server name in that list for SNI enablement to work as expected. -``` +```shell Option to enable/disable mTLS connection: MTLS=true # mTLS connection enabled MTLS=false # mTLS connection disabled (default) @@ -105,6 +104,10 @@ Option to get device serial from system BIOS table: GET_DEV_SERIAL=true # get device serial enabled GET_DEV_SERIAL=false # get device serial disabled (default) +Option to lock TPM for futher reads and writes: +LOCK_TPM=true # TPM Locked for futher reads and writes (default) +LOCK_TPM=false # TPM not locked for futher reads and writes + List of options to clean targets: pristine # cleanup by remove generated files diff --git a/docs/tpm.md b/docs/tpm.md index db262774..e27d789e 100644 --- a/docs/tpm.md +++ b/docs/tpm.md @@ -387,41 +387,53 @@ After a successful compilation, the FDO Client SDK Linux device executable can b >``` > ***NOTE***: linux-client may require elevated privileges. Please use 'sudo' to execute. +> ***NOTE***: To do the DI again we need to clear the Device status from TPM storage. +> To clear the TPM storage, execute the clear TPM* script. Refer to [Clear TPM](../utils/clear_tpm_nv.sh). + +```shell +sudo utils/clear_tpm_nv.sh +``` + +> ***NOTE***: Enabling LOCK_TPM flag in cmake/cli_input.cmake will lock TPM for further reads/writes. +> This flag is enabled by default. But note that this may require the user to reboot the system before any consecutive execution of linux-client. ### 7.1 Prepare FDO Client SDK Data Folder - Persistent Storage Index in TPM* - Find a persistent storage index that is unused in the TPM* and note it down. It usually starts from 0x81000000. To see the indexes that are already being used, use the following command. FDO uses the 0x81000001 index for the following command examples. - - ```shell - sudo tpm2_getcap handles-persistent - ``` +Find a persistent storage index that is unused in the TPM* and note it down. It usually starts from 0x81000000. To see the indexes that are already being used, use the following command. FDO uses the 0x81020002 index for the following command examples. +```shell +sudo tpm2_getcap handles-persistent +``` - Primary Key Generation from Endorsement Hierarchy - ```shell - sudo tpm2_createprimary -C e -g sha256 -G ecc256:aes128cfb -c data/tpm_primary_key.ctx -V - ``` +```shell +sudo tpm2_createprimary -C e -g sha256 -G ecc256:aes128cfb -c data/tpm_primary_key.ctx -V +sudo tpm2_create -g sha256 -G ecc256 -u data/tpm_ecdsa_pub.key -r data/tpm_ecdsa_priv.key -C data/tpm_primary_key.ctx -a "fixedtpm|sensitivedataorigin|fixedparent|sign|userwithauth" -V +``` -- Load the Primary Key into TPM* Persistent Memory +- Device ECDSA Key-Pair Generation and Load the Primary Key into TPM* Persistent Memory - ```shell - sudo tpm2_evictcontrol -C o 0x81000001 -c data/tpm_primary_key.ctx -V - ``` +```shell +sudo tpm2_load -C data/tpm_primary_key.ctx -u data/tpm_ecdsa_pub.key -r data/tpm_ecdsa_priv.key -c data/tpm_ecdsa_key.ctx -V +sudo tpm2_evictcontrol -C o 0x81020002 -c data/tpm_primary_key.ctx -V +``` -- Device ECDSA Key-Pair Generation +- Generate Device MString - ```shell - sudo tpm2tss-genkey -a ecdsa -c nist_p256 data/tpm_ecdsa_priv_pub_blob.key -v -P 0x81000001 - ``` +```shell +sudo openssl req -new -provider tpm2 -provider default -outform DER -out data/tpm_device_csr -key handle:0x81020002 -subj "/CN=fdo-tpm-device" -verbose +``` -- Generate Device MString +- Define a TPM Non-Volatile (NV) index for TPM Device CSR and Write TPM Device CSR to a Non-Volatile (NV) index - ```shell - sudo openssl req -new -provider tpm2 -provider default -out data/device_mstring -key data/tpm_ecdsa_priv_pub_blob.key -subj "/CN=www.fdoDevice1.intel.com" -verbose; truncate -s -1 data/device_mstring; echo -n "13" > /tmp/m_string.txt; truncate -s +1 /tmp/m_string.txt; echo -n "intel-1234" >> /tmp/m_string.txt; truncate -s +1 /tmp/m_string.txt; echo -n "model-123456" >> /tmp/m_string.txt; truncate -s +1 /tmp/m_string.txt; cat data/device_mstring >> /tmp/m_string.txt; base64 -w 0 /tmp/m_string.txt > data/device_mstring; rm -f /tmp/m_string.txt - ``` +```shell +csr_size=$(wc -c < data/tpm_device_csr) +sudo tpm2_nvdefine -Q 0x01D10005 -C o -s csr_size -a "ownerwrite|authwrite|ownerread|authread|no_da|read_stclear|writedefine" +sudo tpm2_nvwrite -Q 0x01D10005 -C o -i data/tpm_device_csr +``` ## 8. Troubleshooting Details @@ -431,19 +443,19 @@ Clear TPM* from the BIOS. To run the TPM* enabled FDO Client SDK implementation, - Clear the Used Persistent Index in TPM*.
Use the tpm2_evictcontrol command to delete the content or clear TPM* from the BIOS. To run the TPM* based FDO implementation, the TPM* on the device should not be owned. To reset the TPM*, go to your device BIOS and clear the TPM*. To find the location of the option in the BIOS of your device, refer to your device manual. - Assuming that the index is 0x81000001, run the following command to delete the keys. +To clear the TPM storage, execute the clear TPM* script. Refer to [Clear TPM](../utils/clear_tpm_nv.sh). - ```shell - sudo tpm2_evictcontrol -C o -c 0x81000001 -V - ``` +```shell +sudo utils/clear_tpm_nv.sh +``` - OpenSSL* Toolkit Library Linking Related Error While Building FDO Client SDK.
There is a dependency on the OpenSSL* toolkit version 3.0.12 for building and running the FDO Client SDK. Check the version of the OpenSSL* toolkit installed in your machine with the command - ```shell +```shell openssl version - ``` +``` If the OpenSSL* toolkit version in your machine is earlier than version 3.0.12, follow the steps given in section 1 to update the OpenSSL* version to 3.0.12. diff --git a/include/fdomodules.h b/include/fdomodules.h index f08dd31c..b4f6cec1 100644 --- a/include/fdomodules.h +++ b/include/fdomodules.h @@ -39,10 +39,12 @@ typedef enum { } fdo_sdk_si_type; // enum for Sv_info module CB return value -enum { FDO_SI_CONTENT_ERROR, - FDO_SI_INTERNAL_ERROR, - FDO_SI_SUCCESS, - FDO_SI_INVALID_MOD_ERROR }; +enum { + FDO_SI_CONTENT_ERROR, + FDO_SI_INTERNAL_ERROR, + FDO_SI_SUCCESS, + FDO_SI_INVALID_MOD_ERROR +}; typedef struct fdo_sdk_si_key_value { char *key; diff --git a/lib/credentials_from_file.c b/lib/credentials_from_file.c index 597a4a0b..754fe037 100644 --- a/lib/credentials_from_file.c +++ b/lib/credentials_from_file.c @@ -21,10 +21,13 @@ #include "cse_utils.h" #include "cse_tools.h" #endif +#if defined(DEVICE_TPM20_ENABLED) +#include "tpm2_nv_storage.h" +#endif static bool validate_state(fdo_sdk_device_status current_status); -#if !defined(DEVICE_CSE_ENABLED) +#if !defined(DEVICE_CSE_ENABLED) && !defined(DEVICE_TPM20_ENABLED) /** * Write the Device Credentials blob, contains our state * @param dev_cred_file - pointer of type const char to which credentails are @@ -454,7 +457,6 @@ int store_credential(fdo_dev_cred_t *ocred) return -1; } -#if !defined(DEVICE_TPM20_ENABLED) /* Write in the file and save the Secure device credentials */ LOG(LOG_DEBUG, "Writing to %s blob\n", "Secure.blob"); if (!write_secure_device_credentials((char *)FDO_CRED_SECURE, @@ -462,8 +464,305 @@ int store_credential(fdo_dev_cred_t *ocred) LOG(LOG_ERROR, "Could not write to Secure Credentials blob\n"); return -1; } + + return 0; +} +#endif + +#if defined(DEVICE_TPM20_ENABLED) +/** + * Write the Device Credentials to nv, contains our state + * @param nv - tpm nv index to be written. + * @param ocred - pointer of type fdo_dev_cred_t, holds the credentials for + * writing to dev_cred_file. + * @return true if write and parsed correctly, otherwise false + */ + +bool write_tpm_device_credentials(uint32_t nv, fdo_dev_cred_t *ocred) +{ + bool ret = true; + + if (!ocred || !nv) { + return false; + } +#ifndef NO_PERSISTENT_STORAGE + + fdow_t *fdow = fdo_alloc(sizeof(fdow_t)); + if (!fdow || !fdow_init(fdow) || + !fdo_block_alloc_with_size(&fdow->b, BUFF_SIZE_4K_BYTES) || + !fdow_encoder_init(fdow)) { + LOG(LOG_ERROR, "FDOW Initialization/Allocation failed!\n"); + ret = false; + goto end; + } + + /** + * Format: Complete DeviceCredential as per Section 3.4.1 of FDO + *Specification, except the DeviceCredential.DCHmacSecret, and addition + *of 'State'. DeviceCredential = [ State, DCProtVer, + * DCDeviceInfo, + * DCGuid, + * DCRVInfo, + * DCPubKeyHash + * ] + */ + fdow_next_block(fdow, FDO_DI_SET_CREDENTIALS); + if (!fdow_start_array(fdow, 6)) { + ret = false; + goto end; + } + if (!fdow_signed_int(fdow, ocred->ST)) { + ret = false; + goto end; + } + + if (!fdow_signed_int(fdow, ocred->owner_blk->pv)) { + ret = false; + goto end; + } + + if (!fdow_text_string(fdow, ocred->mfg_blk->d->bytes, + ocred->mfg_blk->d->byte_sz)) { + ret = false; + goto end; + } + if (!fdow_byte_string(fdow, ocred->owner_blk->guid->bytes, + ocred->owner_blk->guid->byte_sz)) { + ret = false; + goto end; + } + if (!fdo_rendezvous_list_write(fdow, ocred->owner_blk->rvlst)) { + ret = false; + goto end; + } + if (!fdo_hash_write(fdow, ocred->owner_blk->pkh)) { + ret = false; + goto end; + } + if (!fdow_end_array(fdow)) { + ret = false; + goto end; + } + size_t encoded_cred_length = 0; + if (!fdow_encoded_length(fdow, &encoded_cred_length) || + encoded_cred_length == 0) { + LOG(LOG_ERROR, + "Failed to get DeviceCredential encoded length\n"); + ret = false; + goto end; + } + fdow->b.block_size = encoded_cred_length; + + if (fdo_tpm_write_nv(nv, fdow->b.block, fdow->b.block_size) == -1) { + LOG(LOG_ERROR, "Failed to write DeviceCredential in TPM\n"); + ret = false; + goto end; + } + +end: + if (fdow) { + fdow_flush(fdow); + fdo_free(fdow); + } #endif + return ret; +} + +/** + * Read the Device Credentials from tpm nv, contains our state & owner_blk + * @param nv - tpm nv index to be read. + * @param our_dev_cred - pointer to the device credentials block, + * @return true if read and parsed correctly, otherwise false. + */ +bool read_tpm_device_credentials(uint32_t nv, fdo_dev_cred_t *our_dev_cred) +{ + bool ret = false; + size_t dev_cred_len = 0; + fdor_t *fdor = NULL; + int dev_state = -1; + + if (!nv || !our_dev_cred) { + LOG(LOG_ERROR, "Invalid params\n"); + return false; + } + + if (our_dev_cred->owner_blk != NULL) { + fdo_cred_owner_free(our_dev_cred->owner_blk); + our_dev_cred->owner_blk = NULL; + } + + /* Memory allocating data.inside dev_cred. */ + our_dev_cred->owner_blk = fdo_cred_owner_alloc(); + if (!our_dev_cred->owner_blk) { + LOG(LOG_ERROR, "dev_cred's owner_blk allocation failed\n"); + goto end; + } + + dev_cred_len = fdo_tpm_size_nv(nv); + // Device has not yet been initialized. + if (dev_cred_len == 0) { + LOG(LOG_DEBUG, + "DeviceCredential not found. Proceeding with DI\n"); + our_dev_cred->ST = FDO_DEVICE_STATE_PC; + return true; + } + + LOG(LOG_DEBUG, "Reading DeviceCredential of length %" PRIu64 "\n", + dev_cred_len); + + fdor = fdo_alloc(sizeof(fdor_t)); + if (!fdor || !fdor_init(fdor) || + !fdo_block_alloc_with_size(&fdor->b, dev_cred_len)) { + LOG(LOG_ERROR, "FDOR Initialization/Allocation failed!\n"); + goto end; + } + if (fdo_tpm_read_nv(nv, fdor->b.block, fdor->b.block_size) == -1) { + LOG(LOG_ERROR, "Failed to read TPM DeviceCredential\n"); + goto end; + } + + if (!fdor_parser_init(fdor)) { + LOG(LOG_ERROR, "FDOR Parser Initialization failed!\n"); + goto end; + } + + if (!fdor_start_array(fdor)) { + LOG(LOG_ERROR, + "DeviceCredential read: Begin Array not found\n"); + goto end; + } + + if (!fdor_signed_int(fdor, &dev_state)) { + LOG(LOG_ERROR, "DeviceCredential read: ST not found\n"); + goto end; + } + our_dev_cred->ST = dev_state; + + if (!validate_state(our_dev_cred->ST)) { + LOG(LOG_ERROR, "DeviceCredential read: Invalid ST\n"); + goto end; + } + + if (!fdor_signed_int(fdor, &our_dev_cred->owner_blk->pv)) { + LOG(LOG_ERROR, "DeviceCredential read: DCProtVer not found\n"); + goto end; + } + + size_t device_info_length = 0; + if (!fdor_string_length(fdor, &device_info_length) || + device_info_length == 0) { + LOG(LOG_ERROR, + "DeviceCredential read: Invalid DCDeviceInfo length\n"); + goto end; + } + + our_dev_cred->mfg_blk = fdo_cred_mfg_alloc(); + if (!our_dev_cred->mfg_blk) { + LOG(LOG_ERROR, + "DeviceCredential read: Malloc for DCDeviceInfo failed"); + goto end; + } + + our_dev_cred->mfg_blk->d = fdo_string_alloc_size(device_info_length); + if (!our_dev_cred->mfg_blk->d || + !fdor_text_string(fdor, our_dev_cred->mfg_blk->d->bytes, + our_dev_cred->mfg_blk->d->byte_sz)) { + LOG(LOG_ERROR, + "DeviceCredential read: DCDeviceInfo not found\n"); + goto end; + } + our_dev_cred->mfg_blk->d->bytes[device_info_length] = '\0'; + + size_t guid_length = 0; + if (!fdor_string_length(fdor, &guid_length) || guid_length == 0) { + LOG(LOG_ERROR, + "DeviceCredential read: Invalid DCGuid length\n"); + goto end; + } + our_dev_cred->owner_blk->guid = fdo_byte_array_alloc(guid_length); + if (!our_dev_cred->owner_blk->guid || + !fdor_byte_string(fdor, our_dev_cred->owner_blk->guid->bytes, + our_dev_cred->owner_blk->guid->byte_sz)) { + LOG(LOG_ERROR, "DeviceCredential read: DCGuid not found\n"); + goto end; + } + + our_dev_cred->owner_blk->rvlst = fdo_rendezvous_list_alloc(); + if (!our_dev_cred->owner_blk->rvlst || + !fdo_rendezvous_list_read(fdor, our_dev_cred->owner_blk->rvlst)) { + LOG(LOG_ERROR, "DeviceCredential read: DCRVInfo not found\n"); + goto end; + } + + our_dev_cred->owner_blk->pkh = + fdo_hash_alloc(FDO_CRYPTO_HASH_TYPE_USED, FDO_SHA_DIGEST_SIZE_USED); + if (!our_dev_cred->owner_blk->pkh || + !fdo_hash_read(fdor, our_dev_cred->owner_blk->pkh)) { + LOG(LOG_ERROR, + "DeviceCredential read: DCPubKeyHash not found\n"); + goto end; + } + + if (!fdor_end_array(fdor)) { + LOG(LOG_ERROR, "DeviceCredential read: End Array not found\n"); + goto end; + } + ret = true; +end: + if (fdor) { + fdor_flush(fdor); + fdo_free(fdor); + } + return ret; +} + +/** + * Write and save the device credentials passed as an parameter ocred of type + * fdo_dev_cred_t. + * @param ocred - Pointer of type fdo_dev_cred_t, credentials to be copied + * @return 0 if success, else -1 on failure. + */ +int store_tpm_credential(fdo_dev_cred_t *ocred) +{ + /* Write in the file and save the Normal device credentials */ + LOG(LOG_DEBUG, "Writing to TPM NV storage\n"); + + uint8_t *dc_active = NULL; + size_t dc_active_len = sizeof(uint8_t); + dc_active = fdo_alloc(dc_active_len); + if (NULL == dc_active) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + return -1; + } + + *dc_active = ocred->dc_active; + + if (fdo_tpm_nvwrite(dc_active, dc_active_len, FDO_DCActive_NV_IDX)) { + LOG(LOG_ERROR, "Failed to write DeviceCredential Active\n"); + fdo_free(dc_active); + return -1; + } + + if (dc_active) { + fdo_free(dc_active); + } + + if (!write_tpm_device_credentials(FDO_CRED_NV_IDX, ocred)) { + LOG(LOG_ERROR, "Could not write to Normal Credentials\n"); + return -1; + } +#if defined(LOCK_TPM) + if (fdo_tpm_nvwrite_lock(FDO_CRED_NV_IDX)) { + LOG(LOG_ERROR, "Failed to lock file!\n"); + return -1; + } + + if (fdo_tpm_nvread_lock(FDO_CRED_NV_IDX)) { + LOG(LOG_ERROR, "Failed to lock file!\n"); + return -1; + } +#endif return 0; } #endif @@ -627,6 +926,42 @@ int load_credential(fdo_dev_cred_t *ocred) "Could not parse the Device Credentials form CSE\n"); return -1; } +#elif defined(DEVICE_TPM20_ENABLED) + /* Read and save the device credentials */ + uint8_t *dc_active = NULL; + size_t dc_active_len = fdo_tpm_nvread_size(FDO_DCActive_NV_IDX); + if (dc_active_len == 0) { + LOG(LOG_ERROR, "DeviceCredential Active not found.\n"); + return -1; + } + + dc_active = fdo_alloc(dc_active_len); + if (NULL == dc_active) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + return -1; + } + + if (fdo_tpm_nvread(FDO_DCActive_NV_IDX, dc_active_len, &dc_active)) { + LOG(LOG_ERROR, "Failed to read file!\n"); + fdo_free(dc_active); + return -1; + } + + if (*dc_active == 1) { + ocred->dc_active = true; + if (!read_tpm_device_credentials(FDO_CRED_NV_IDX, ocred)) { + LOG(LOG_ERROR, + "Could not read the Device Credentials from TPM\n"); + fdo_free(dc_active); + return -1; + } + } else { + ocred->dc_active = false; + } + + if (dc_active) { + fdo_free(dc_active); + } #else /* Read in the blob and save the device credentials */ if (!read_normal_device_credentials((char *)FDO_CRED_NORMAL, @@ -684,6 +1019,8 @@ bool load_device_status(fdo_sdk_device_status *state) "DeviceCredential read: Unable to load file form CSE\n"); return false; } +#elif defined(DEVICE_TPM20_ENABLED) + size_t dev_cred_len = fdo_tpm_size_nv(FDO_CRED_NV_IDX); #else size_t dev_cred_len = fdo_blob_size((char *)FDO_CRED_NORMAL, FDO_SDK_NORMAL_DATA); @@ -691,12 +1028,12 @@ bool load_device_status(fdo_sdk_device_status *state) // Device has not yet been initialized. // Since, Normal.blob is empty, the file size will be 0 if (dev_cred_len == 0) { - LOG(LOG_DEBUG, + LOG(LOG_INFO, "DeviceCredential is empty. Set state to run DI\n"); *state = FDO_DEVICE_STATE_PC; } else { - LOG(LOG_DEBUG, "DeviceCredential is non-empty. Set state to " - "run TO1/TO2\n"); + LOG(LOG_INFO, "DeviceCredential is non-empty. Set state to " + "run TO1/TO2\n"); // No Device state is being set currently } return true; diff --git a/lib/fdo.c b/lib/fdo.c index 391816e7..ba42bbb4 100644 --- a/lib/fdo.c +++ b/lib/fdo.c @@ -871,6 +871,12 @@ fdo_sdk_status fdo_sdk_init(fdo_sdk_errorCB error_handling_callback, } } + if (g_fdo_data->devcred->ST != FDO_DEVICE_STATE_PC && + g_fdo_data->devcred->dc_active == false) { + g_fdo_data->devcred->ST = FDO_DEVICE_STATE_IDLE; + return FDO_SUCCESS; + } + if ((num_modules == 0) || (num_modules > FDO_MAX_MODULES) || (module_information == NULL) || (module_information->service_info_callback == NULL)) { @@ -1158,8 +1164,11 @@ fdo_sdk_status fdo_sdk_resale(void) LOG(LOG_ERROR, "Reading {Mfg|Secret} blob failied!\n"); return FDO_ERROR; } - +#if defined(DEVICE_TPM20_ENABLED) + ret = store_tpm_credential(g_fdo_data->devcred); +#else ret = store_credential(g_fdo_data->devcred); +#endif #endif if (!ret) { LOG(LOG_INFO, "Set Resale complete\n"); @@ -1172,7 +1181,7 @@ fdo_sdk_status fdo_sdk_resale(void) if (r == FDO_ERROR) { LOG(LOG_ERROR, "Failed to set Resale\n"); } else if (r == FDO_RESALE_NOT_READY) { - LOG(LOG_DEBUG, "Device is not ready for Resale\n"); + LOG(LOG_INFO, "Device is not ready for Resale\n"); } if (g_fdo_data->devcred) { fdo_dev_cred_free(g_fdo_data->devcred); diff --git a/lib/include/load_credentials.h b/lib/include/load_credentials.h index 8e3ecd95..2b21d2aa 100644 --- a/lib/include/load_credentials.h +++ b/lib/include/load_credentials.h @@ -32,4 +32,10 @@ int store_credential(fdo_dev_cred_t *ocred); bool load_device_status(fdo_sdk_device_status *state); bool store_device_status(fdo_sdk_device_status *state); +#if defined(DEVICE_TPM20_ENABLED) +bool read_tpm_device_credentials(uint32_t nv, fdo_dev_cred_t *our_dev_cred); +bool write_tpm_device_credentials(uint32_t nv, fdo_dev_cred_t *our_dev_cred); +int store_tpm_credential(fdo_dev_cred_t *ocred); +#endif + #endif /* __LOAD_CREDENTIALS_H__ */ diff --git a/lib/m-string.c b/lib/m-string.c index 7ee46554..d248c280 100644 --- a/lib/m-string.c +++ b/lib/m-string.c @@ -20,6 +20,11 @@ #include "cse_utils.h" #include "cse_tools.h" #endif +#if defined(DEVICE_TPM20_ENABLED) +#include "tpm20_Utils.h" +#include "fdo_crypto.h" +#include "tpm2_nv_storage.h" +#endif #include #include @@ -257,7 +262,7 @@ int ps_get_m_string(fdo_prot_t *ps) /* Get the CSR data */ #if defined(DEVICE_TPM20_ENABLED) - size_t m_string_sz = get_file_size(TPM_DEVICE_CSR); + size_t m_string_sz = fdo_tpm_nvread_size(TPM_DEVICE_CSR_NV_IDX); csr = fdo_byte_array_alloc(m_string_sz); if (!csr) { @@ -266,11 +271,18 @@ int ps_get_m_string(fdo_prot_t *ps) goto err; } - ret = read_buffer_from_file(TPM_DEVICE_CSR, csr->bytes, csr->byte_sz); - if (0 != ret) { - LOG(LOG_ERROR, "Failed to read %s file!\n", TPM_DEVICE_CSR); + if (fdo_tpm_read_nv(TPM_DEVICE_CSR_NV_IDX, csr->bytes, csr->byte_sz) == + -1) { + LOG(LOG_ERROR, "Failed to load TPM DEVICE CSR into buffer.\n"); goto err; } +#if defined(LOCK_TPM) + if (fdo_tpm_nvread_lock(TPM_DEVICE_CSR_NV_IDX)) { + LOG(LOG_ERROR, "Failed to lock file!\n"); + goto err; + } +#endif + ret = 0; #elif defined(DEVICE_CSE_ENABLED) // CSR will be NULL for CSE csr = fdo_byte_array_alloc(0); diff --git a/lib/prot/di/msg13.c b/lib/prot/di/msg13.c index 5d2fe429..e3f9c80f 100644 --- a/lib/prot/di/msg13.c +++ b/lib/prot/di/msg13.c @@ -84,6 +84,13 @@ int32_t msg13(fdo_prot_t *ps) return -1; } LOG(LOG_DEBUG, "FDO OVH COMMIT succeeded %u\n", fdo_status); +#elif defined(DEVICE_TPM20_ENABLED) + ps->dev_cred->dc_active = true; + + if (store_tpm_credential(ps->dev_cred) != 0) { + LOG(LOG_ERROR, "TO2.Done: Failed to store new device creds\n"); + goto err; + } #else if (store_credential(ps->dev_cred) != 0) { LOG(LOG_ERROR, "Failed to store updated device credentials\n"); diff --git a/lib/prot/to2/msg70.c b/lib/prot/to2/msg70.c index b9e3e1c6..ed4dd9b5 100644 --- a/lib/prot/to2/msg70.c +++ b/lib/prot/to2/msg70.c @@ -58,14 +58,17 @@ int32_t msg70(fdo_prot_t *ps) fdo_public_key_free(ps->dev_cred->owner_blk->pk); ps->dev_cred->owner_blk->pk = ps->osc->pubkey; + ps->dev_cred->dc_active = false; if (ps->reuse_enabled && reuse_supported) { // Reuse scenario, moving to post DI state ps->dev_cred->ST = FDO_DEVICE_STATE_READY1; + ps->dev_cred->dc_active = true; } else if (resale_supported) { // Done with FIDO Device Onboard. // As of now moving to done state for resale ps->dev_cred->ST = FDO_DEVICE_STATE_IDLE; + ps->dev_cred->dc_active = true; // create new Owner's public key hash fdo_hash_free(ps->dev_cred->owner_blk->pkh); ps->dev_cred->owner_blk->pkh = @@ -124,6 +127,11 @@ int32_t msg70(fdo_prot_t *ps) goto err; } LOG(LOG_DEBUG, "TO2.Done: FDO OVH COMMIT succeeded %u\n", fdo_status); +#elif defined(DEVICE_TPM20_ENABLED) + if (store_tpm_credential(ps->dev_cred) != 0) { + LOG(LOG_ERROR, "TO2.Done: Failed to store new device creds\n"); + goto err; + } #else if (store_credential(ps->dev_cred) != 0) { LOG(LOG_ERROR, "TO2.Done: Failed to store new device creds\n"); diff --git a/storage/CMakeLists.txt b/storage/CMakeLists.txt index 191ffdb6..94fab877 100644 --- a/storage/CMakeLists.txt +++ b/storage/CMakeLists.txt @@ -16,4 +16,10 @@ client_sdk_sources_with_lib( storage util.c ) +if (${DA} MATCHES tpm) + client_sdk_sources_with_lib( storage + linux/tpm2_nv_storage.c + ) +endif() + target_link_libraries(storage PUBLIC client_sdk_interface) diff --git a/storage/include/storage_al.h b/storage/include/storage_al.h index 7b2e7fa0..f6c7e699 100644 --- a/storage/include/storage_al.h +++ b/storage/include/storage_al.h @@ -17,6 +17,9 @@ #include #include #include +#if defined(DEVICE_TPM20_ENABLED) +#include "tpm2_nv_storage.h" +#endif // platform HMAC and blob size #if defined(DEVICE_TPM20_ENABLED) && defined(ECDSA384_DA) @@ -46,6 +49,14 @@ size_t fdo_blob_size(const char *blob_name, fdo_sdk_blob_flags flags); int32_t create_hmac_normal_blob(void); +#if defined(DEVICE_TPM20_ENABLED) +int32_t fdo_tpm_read_nv(TPMI_RH_NV_INDEX nv, uint8_t *buffer, uint32_t length); + +int32_t fdo_tpm_write_nv(TPMI_RH_NV_INDEX nv, const uint8_t *buffer, + uint32_t length); + +size_t fdo_tpm_size_nv(TPMI_RH_NV_INDEX nv); +#endif #ifdef __cplusplus } // endof externc (CPP code) #endif diff --git a/storage/include/tpm2_nv_storage.h b/storage/include/tpm2_nv_storage.h new file mode 100644 index 00000000..2a833b24 --- /dev/null +++ b/storage/include/tpm2_nv_storage.h @@ -0,0 +1,85 @@ +#include +#include +#include +#include +#include +#include + +#define FDO_DCActive_NV_IDX 0x01D10000 +#define FDO_CRED_NV_IDX 0x01D10001 +#define TPM_DEVICE_KEY_PERSISTANT_HANDLE 0x81020002 +#define TPM_HMAC_KEY_PERSISTANT_HANDLE 0x81020003 +#define TPM_DEVICE_CSR_NV_IDX 0x01D10005 + +#if defined(ECDSA256_DA) +#define FDO_TPM2_ALG_SHA TPM2_ALG_SHA256 +#else +#define FDO_TPM2_ALG_SHA TPM2_ALG_SHA384 +#endif + +/** Define space at NV index. + * + * @param[in] nv NV index to delete. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvdefine(TPMI_RH_NV_INDEX nv, size_t data_size); + +/** Store a data in a NV index. + * + * @param[in] data Key to store to NVRAM. + * @param[in] data_size Size of the data. + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvwrite(const uint8_t *data, size_t data_size, TPMI_RH_NV_INDEX nv); + +/** Lock the NV index for further writes. + * + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvwrite_lock(TPMI_RH_NV_INDEX nv); + +/** Load data size from a NV index. + * + * @param[in] nv NV index of the data. + * @retval data size on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +size_t fdo_tpm_nvread_size(TPMI_RH_NV_INDEX nv); + +/** Load a data from a NV index. + * + * @param[in] nv NV index of the data. + * @param[out] data Loaded data. + * @param[out] data_size Size of the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvread(TPMI_RH_NV_INDEX nv, size_t data_size, uint8_t **data); + +/** Lock the NV index for further reads. + * + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvread_lock(TPMI_RH_NV_INDEX nv); + +/** Delete data from a NV index. + * + * @param[in] nv NV index to delete. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvdel(TPMI_RH_NV_INDEX nv); \ No newline at end of file diff --git a/storage/linux/storage_if_linux.c b/storage/linux/storage_if_linux.c index 7c53bfe4..1aaa2990 100644 --- a/storage/linux/storage_if_linux.c +++ b/storage/linux/storage_if_linux.c @@ -20,6 +20,9 @@ #include "fdo_crypto.h" #include "crypto_utils.h" #include "platform_utils.h" +#if defined(DEVICE_TPM20_ENABLED) +#include "tpm20_Utils.h" +#endif /**************************************************** * @@ -562,3 +565,278 @@ int32_t fdo_blob_write(const char *name, fdo_sdk_blob_flags flags, } return retval; } + +#if defined(DEVICE_TPM20_ENABLED) +/** + * fdo_tpm_size_nv Get specified FDO nv size + * @param nv - ptpm nv index + * @return file size on success, 0 if file does not exist or on other failure + */ + +size_t fdo_tpm_size_nv(TPMI_RH_NV_INDEX nv) +{ + size_t retval = 0; + const size_t NORMAL_NV_OVERHEAD = + PLATFORM_HMAC_SIZE + BLOB_CONTENT_SIZE; + + if (!nv) { + LOG(LOG_ERROR, "Invalid parameters!\n"); + goto end; + } + + /* Normal cred is stored as: + * [HMAC(32bytes)||data-content-size(4bytes)||data-content(?)] + */ + retval = fdo_tpm_nvread_size(nv); + + // Return 0 if the file is empty. + if (retval == 0) { + LOG(LOG_DEBUG, "NV is empty!\n"); + retval = 0; + goto end; + } + + if (retval >= NORMAL_NV_OVERHEAD) { + retval -= NORMAL_NV_OVERHEAD; + } else { + /* File format is not correct, not enough data in the + * file */ + retval = 0; + } + +end: + if (retval > R_MAX_SIZE) { + LOG(LOG_ERROR, "File size is more than R_MAX_SIZE\n"); + retval = 0; + } + return retval; +} + +/** + * fdo_blob_read Read FDO blob(file) into specified buffer, + * fdo_blob_read ensures authenticity & integrity for non-secure + * data & additionally confidentiality for secure data. + * Note: FDO_SDK_OTP_DATA flag is not supported for this platform. + * @param nv - tpm nv index + * @param flags - descriptor telling type of file + * @param buf - pointer to buf where data is read into + * @param n_bytes - length of data(in bytes) to be read + * @return num of bytes read if success, -1 on error + */ +int32_t fdo_tpm_read_nv(TPMI_RH_NV_INDEX nv, uint8_t *buf, uint32_t n_bytes) +{ + int retval = -1; + uint8_t *data = NULL; + uint32_t data_length = 0; + uint8_t *sealed_data = NULL; + uint32_t sealed_data_len = 0; + uint8_t stored_hmac[PLATFORM_HMAC_SIZE] = {0}; + uint8_t computed_hmac[PLATFORM_HMAC_SIZE] = {0}; + int strcmp_result = -1; + + if (!nv || !buf || n_bytes == 0) { + LOG(LOG_ERROR, "Invalid parameters in %s!\n", __func__); + goto exit; + } + + if (n_bytes > R_MAX_SIZE) { + LOG(LOG_ERROR, + "file read buffer is more than R_MAX_SIZE in " + "%s!\n", + __func__); + goto exit; + } + + if (nv == FDO_CRED_NV_IDX) { + /* HMAC-256 is being used to store files under + * FDO_SDK_NORMAL_DATA flag. + * File content to be stored as: + * [HMAC(32 bytes)||Sizeof_plaintext(4 bytes)||Plaintext(n_bytes + * bytes)] + */ + + sealed_data_len = + PLATFORM_HMAC_SIZE + BLOB_CONTENT_SIZE + n_bytes; + + sealed_data = fdo_alloc(sealed_data_len); + if (NULL == sealed_data) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + goto exit; + } + + if (fdo_tpm_nvread(nv, sealed_data_len, &sealed_data)) { + LOG(LOG_ERROR, "Failed to read file!\n"); + goto exit; + } + + // get actual data length + data_length |= sealed_data[PLATFORM_HMAC_SIZE] << 24; + data_length |= sealed_data[PLATFORM_HMAC_SIZE + 1] << 16; + data_length |= sealed_data[PLATFORM_HMAC_SIZE + 2] << 8; + data_length |= + (sealed_data[PLATFORM_HMAC_SIZE + 3] & 0x000000FF); + + // check if input buffer is sufficient ? + if (n_bytes < data_length) { + LOG(LOG_ERROR, + "Failed to read data, Buffer is not enough, " + "buf_len:%d,\t Lengthstoredinfilesystem:%d\n", + n_bytes, data_length); + goto exit; + } + + if (memcpy_s(stored_hmac, PLATFORM_HMAC_SIZE, sealed_data, + PLATFORM_HMAC_SIZE) != 0) { + LOG(LOG_ERROR, + "Copying stored HMAC failed during " + "%s!\n", + __func__); + goto exit; + } + + data = sealed_data + PLATFORM_HMAC_SIZE + BLOB_CONTENT_SIZE; + + if (0 != fdo_tpm_get_hmac(data, data_length, computed_hmac, + PLATFORM_HMAC_SIZE, + TPM_HMAC_KEY_PERSISTANT_HANDLE)) { + LOG(LOG_ERROR, + "HMAC computation dailed during" + " %s!\n", + __func__); + goto exit; + } + + // compare HMAC + if (memcmp_s(stored_hmac, PLATFORM_HMAC_SIZE, computed_hmac, + PLATFORM_HMAC_SIZE, &strcmp_result) != 0) { + LOG(LOG_ERROR, "Failed to compare HMAC\n"); + goto exit; + } + if (strcmp_result != 0) { + LOG(LOG_ERROR, "%s: HMACs do not compare!\n", __func__); + goto exit; + } + + // copy data into supplied buffer + if (memcpy_s(buf, n_bytes, data, data_length) != 0) { + LOG(LOG_ERROR, + "%s: Copying data into " + "buffer failed!\n", + __func__); + goto exit; + } + } else { + if (0 != fdo_tpm_nvread(nv, n_bytes, &buf)) { + LOG(LOG_ERROR, "Failed to read file!\n"); + goto exit; + } + } + + retval = (int32_t)n_bytes; + +exit: + return retval; +} + +/** + * fdo_blob_write Write FDO blob(file) from specified buffer + * fdo_blob_write ensures integrity & authenticity for non-secure + * data & additionally confidentiality for secure data. + * Note: FDO_SDK_OTP_DATA flag is not supported for this platform. + * @param nv - ptpm nv index + * @param flags - descriptor telling type of file + * @param buf - pointer to buf from where data is read and then written + * @param n_bytes - length of data(in bytes) to be written + * @return num of bytes write if success, -1 on error + */ + +int32_t fdo_tpm_write_nv(TPMI_RH_NV_INDEX nv, const uint8_t *buf, + uint32_t n_bytes) +{ + int retval = -1; + uint32_t write_context_len = 0; + uint8_t *write_context = NULL; + + if (!buf || !nv || n_bytes == 0) { + LOG(LOG_ERROR, "Invalid parameters in %s!\n", __func__); + goto exit; + } + + if (n_bytes > R_MAX_SIZE) { + LOG(LOG_ERROR, + "file write buffer is more than R_MAX_SIZE in " + "%s!\n", + __func__); + goto exit; + } + + if (nv == FDO_CRED_NV_IDX) { + /* HMAC-256 is being used to store files under + * FDO_SDK_NORMAL_DATA flag. + * File content to be stored as: + * [HMAC(32 bytes)||Sizeof_plaintext(4 bytes)||Plaintext(n_bytes + * bytes)] + */ + write_context_len = + PLATFORM_HMAC_SIZE + BLOB_CONTENT_SIZE + n_bytes; + + write_context = fdo_alloc(write_context_len); + if (NULL == write_context) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + goto exit; + } + + if (0 != fdo_tpm_get_hmac(buf, n_bytes, write_context, + PLATFORM_HMAC_SIZE, + TPM_HMAC_KEY_PERSISTANT_HANDLE)) { + LOG(LOG_ERROR, "Computing HMAC failed!\n"); + goto exit; + } + + // copy plain-text size + write_context[PLATFORM_HMAC_SIZE + 3] = n_bytes >> 0; + write_context[PLATFORM_HMAC_SIZE + 2] = n_bytes >> 8; + write_context[PLATFORM_HMAC_SIZE + 1] = n_bytes >> 16; + write_context[PLATFORM_HMAC_SIZE + 0] = n_bytes >> 24; + + // copy plain-text content + if (memcpy_s(write_context + PLATFORM_HMAC_SIZE + + BLOB_CONTENT_SIZE, + (write_context_len - PLATFORM_HMAC_SIZE - + BLOB_CONTENT_SIZE), + buf, n_bytes) != 0) { + LOG(LOG_ERROR, + "Copying data failed during Normal Blob write!\n"); + goto exit; + } + } else { + write_context_len = n_bytes; + + write_context = fdo_alloc(write_context_len); + if (NULL == write_context) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + goto exit; + } + + if (memcpy_s(write_context, write_context_len, buf, n_bytes) != + 0) { + LOG(LOG_ERROR, + "Copying data failed during RAW Blob write!\n"); + goto exit; + } + } + + if (fdo_tpm_nvwrite(write_context, write_context_len, nv)) { + LOG(LOG_ERROR, "Failed to write in TPM NV!\n"); + goto exit; + } + + retval = (int32_t)n_bytes; + +exit: + if (write_context) { + fdo_free(write_context); + } + return retval; +} +#endif \ No newline at end of file diff --git a/storage/linux/tpm2_nv_storage.c b/storage/linux/tpm2_nv_storage.c new file mode 100644 index 00000000..9e67dca4 --- /dev/null +++ b/storage/linux/tpm2_nv_storage.c @@ -0,0 +1,747 @@ +#include "util.h" +#include "tpm2_nv_storage.h" +#include "safe_lib.h" + +/** + * Initialize Esys context. + * + * @param esys_context : output Esys Context + * + * @return + * TPM2_RC_SUCCESS, on success + * -1, on failure + */ +static int32_t fdo_tpm_esys_context_init(ESYS_CONTEXT **esys_context) +{ + int ret = -1; + TSS2_TCTI_CONTEXT *tcti_context = NULL; + + if ((TSS2_RC_SUCCESS != + Tss2_TctiLdr_Initialize(TPM2_TCTI_TYPE, &tcti_context)) || + (!tcti_context)) { + LOG(LOG_ERROR, "TCTI Context initialization failed.\n"); + goto err; + } + + if (Esys_Initialize(esys_context, tcti_context, NULL) != + TPM2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + return TPM2_RC_SUCCESS; + +err: + if (tcti_context) { + Tss2_TctiLdr_Finalize(&tcti_context); + } + return ret; +} + +/** + * Create HMAC based auth session for Esys Context + * + * @param esys_context : input Esys Context + * @param session_handle : output authentication session Handle + * + * @return + * TPM2_RC_SUCCESS, on success + * -1, on failure + */ +static int32_t fdo_tpm_esys_auth_session_init(ESYS_CONTEXT *esys_context, + ESYS_TR *session_handle) +{ + int ret = -1; + TSS2_RC rval; + TPMT_SYM_DEF symmetric = {.algorithm = TPM2_ALG_AES, + .keyBits = {.aes = 128}, + .mode = {.aes = TPM2_ALG_CFB}}; + + rval = Esys_StartAuthSession(esys_context, ESYS_TR_NONE, ESYS_TR_NONE, + ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + NULL, TPM2_SE_HMAC, &symmetric, + FDO_TPM2_ALG_SHA, session_handle); + + if (rval != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start the auth session.\n"); + return ret; + } + + rval = Esys_TRSess_SetAttributes(esys_context, *session_handle, + TPMA_SESSION_DECRYPT | + TPMA_SESSION_ENCRYPT | + TPMA_SESSION_CONTINUESESSION, + 0xff); + if (rval != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to Set session attributes.\n"); + return ret; + } + + return rval; +} + +/** + * Clear Esys, TCTI, contexts and Auth Session, Primary Key handles. + * + * @param esys_context : Esys Context to be cleared + * @param auth_session_handle : Auth session Handle to be flushed + * @param nv_handle : NV handle to be cleared + * @return + * 0, on success + * -1, on failure + */ +static int32_t fdo_tpm_context_clean_up(ESYS_CONTEXT **esys_context, + ESYS_TR *auth_session_handle, + ESYS_TR *nv_handle) +{ + int ret = -1, is_failed = 0; + TSS2_TCTI_CONTEXT *tcti_context = NULL; + TSS2_RC rc = TPM2_RC_FAILURE; + + if (!esys_context || !*esys_context) { + LOG(LOG_ERROR, "Invalid parameter received.\n"); + return ret; + } + + if (auth_session_handle && (*auth_session_handle != ESYS_TR_NONE)) { + if (Esys_FlushContext(*esys_context, *auth_session_handle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to flush auth session handle.\n"); + is_failed = 1; + } else { + LOG(LOG_DEBUG, + "Auth session handle flushed successfully.\n"); + *auth_session_handle = ESYS_TR_NONE; + } + } + + if (nv_handle && (*nv_handle != ESYS_TR_NONE)) { + if (Esys_TR_Close(*esys_context, nv_handle) != + TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to flush primary key handle.\n"); + is_failed = 1; + } else { + LOG(LOG_DEBUG, + "Primary key handle flushed successfully.\n"); + *nv_handle = ESYS_TR_NONE; + } + } + + rc = Esys_GetTcti(*esys_context, &tcti_context); + if (rc != TPM2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to cleanup TCTI.\n"); + is_failed = 1; + } + Esys_Finalize(esys_context); + + if (tcti_context) { + Tss2_TctiLdr_Finalize(&tcti_context); + if (tcti_context) { + LOG(LOG_ERROR, "Failed to finalize context.\n"); + is_failed = 1; + } + } + + if (is_failed) { + return ret; + } + + return 0; +} + +/** Define space at NV index. + * + * @param[in] nv NV index to delete. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvdefine(TPMI_RH_NV_INDEX nv, size_t data_size) +{ + + if (!nv) { + return -1; + } + + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + TPM2B_AUTH emptyAuth = { + .size = 0, + }; + + TPM2B_NV_PUBLIC publicInfo = { + .size = 0, + .nvPublic = { + .nvIndex = nv, + .nameAlg = FDO_TPM2_ALG_SHA, + .attributes = + (TPMA_NV_OWNERWRITE | TPMA_NV_AUTHWRITE | TPMA_NV_AUTHREAD | + TPMA_NV_OWNERREAD | TPMA_NV_NO_DA | TPMA_NV_READ_STCLEAR | + TPMA_NV_WRITE_STCLEAR | TPMA_NV_WRITEDEFINE), + .authPolicy = + { + .size = 0, + .buffer = {0}, + }, + .dataSize = data_size, + }}; + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys context.\n"); + goto err; + } + + // Search the NV index + TPMS_CAPABILITY_DATA *capability_data = NULL; + rc = + Esys_GetCapability(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_HANDLES, nv, 1, NULL, &capability_data); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_GetCapability failed!\n"); + goto err; + } + + int exists = (capability_data->data.handles.count > 0 && + capability_data->data.handles.handle[0] == nv); + if (exists == 1) { + LOG(LOG_DEBUG, "NV index already exist.\n"); + ret = 0; + goto err; + } + + rc = Esys_NV_DefineSpace(ctx, ESYS_TR_RH_OWNER, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, &emptyAuth, + &publicInfo, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to define Esys NV space.\n"); + goto err; + } + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + + return ret; +} + +/** Store a data in a NV index. + * + * @param[in] data Key to store to NVRAM. + * @param[in] data_size Size of the data. + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvwrite(const uint8_t *data, size_t data_size, TPMI_RH_NV_INDEX nv) +{ + if (!data || !nv) { + return -1; + } + + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + TPM2B_AUTH emptyAuth = { + .size = 0, + }; + + TPM2B_NV_PUBLIC publicInfo = { + .size = 0, + .nvPublic = { + .nvIndex = nv, + .nameAlg = FDO_TPM2_ALG_SHA, + .attributes = + (TPMA_NV_OWNERWRITE | TPMA_NV_AUTHWRITE | TPMA_NV_AUTHREAD | + TPMA_NV_OWNERREAD | TPMA_NV_NO_DA | TPMA_NV_READ_STCLEAR | + TPMA_NV_WRITEDEFINE), + .authPolicy = + { + .size = 0, + .buffer = {0}, + }, + .dataSize = data_size, + }}; + + TPM2B_MAX_NV_BUFFER blob = {.size = data_size}; + if (blob.size > sizeof(blob.buffer)) { + LOG(LOG_ERROR, "Data too large.\n"); + return -1; + } + + if (memcpy_s(&blob.buffer[0], blob.size, data, data_size) != 0) { + LOG(LOG_ERROR, "Failed to copy data to blob!\n"); + goto err; + } + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys context.\n"); + goto err; + } + + // Search the NV index + TPMS_CAPABILITY_DATA *capability_data = NULL; + rc = + Esys_GetCapability(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_HANDLES, nv, 1, NULL, &capability_data); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_GetCapability failed!\n"); + goto err; + } + + int exists = (capability_data->data.handles.count > 0 && + capability_data->data.handles.handle[0] == nv); + if (exists == 1) { + LOG(LOG_DEBUG, "NV index already exist. Deleting it.\n"); + rc = Esys_TR_FromTPMPublic(ctx, nv, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, + &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, + "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + rc = Esys_NV_UndefineSpace(ctx, ESYS_TR_RH_OWNER, nvHandle, + auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to undefine Esys NV space.\n"); + goto err; + } + } + + rc = Esys_NV_DefineSpace(ctx, ESYS_TR_RH_OWNER, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, &emptyAuth, + &publicInfo, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to define Esys NV space.\n"); + goto err; + } + + rc = Esys_NV_Write(ctx, nvHandle, nvHandle, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, &blob, 0 /*=offset*/); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to write in Esys NV space.\n"); + goto err; + } + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + return ret; +} + +/** Lock the NV index for further writes. + * + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvwrite_lock(TPMI_RH_NV_INDEX nv) +{ + if (!nv) { + return -1; + } + + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys context.\n"); + goto err; + } + + rc = Esys_TR_FromTPMPublic(ctx, nv, auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + + rc = Esys_NV_WriteLock(ctx, ESYS_TR_RH_OWNER, nvHandle, + auth_session_handle, ESYS_TR_NONE, ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to lock NV write.\n"); + goto err; + } + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + return ret; +} + +/** Load data size from a NV index. + * + * @param[in] nv NV index of the data. + * @retval data size on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +size_t fdo_tpm_nvread_size(TPMI_RH_NV_INDEX nv) +{ + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + TPM2B_NV_PUBLIC *publicInfo = NULL; + size_t data_size; + + if (!nv) { + return -1; + } + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys API.\n"); + goto err; + } + + // Search the NV index + TPMS_CAPABILITY_DATA *capability_data = NULL; + rc = + Esys_GetCapability(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_HANDLES, nv, 1, NULL, &capability_data); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_GetCapability failed!\n"); + goto err; + } + + int exists = (capability_data->data.handles.count > 0 && + capability_data->data.handles.handle[0] == nv); + if (exists != 1) { + LOG(LOG_DEBUG, "NV index doesn't exist.\n"); + ret = 0; + goto err; + } + + rc = Esys_TR_FromTPMPublic(ctx, nv, auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + + rc = Esys_NV_ReadPublic(ctx, nvHandle, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, &publicInfo, NULL); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to read publicinfo from NV.\n"); + goto err; + } + + data_size = publicInfo->nvPublic.dataSize; + + ret = data_size; + +err: + + if (publicInfo) { + free(publicInfo); + } + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + return ret; +} + +/** Load data from a NV index. + * + * @param[in] nv NV index of the data. + * @param[in] data_size Size of the data. + * @param[out] data Loaded data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvread(TPMI_RH_NV_INDEX nv, size_t data_size, uint8_t **data) +{ + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + TPM2B_MAX_NV_BUFFER *blob; + + if (!nv) { + return -1; + } + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys API.\n"); + goto err; + } + + rc = Esys_TR_FromTPMPublic(ctx, nv, auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + + rc = Esys_NV_Read(ctx, ESYS_TR_RH_OWNER, nvHandle, auth_session_handle, + ESYS_TR_NONE, ESYS_TR_NONE, data_size, 0 /*=offset*/, + &blob); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to read data from NV storage.\n"); + goto err; + } + + if (memcpy_s(*data, data_size, &blob->buffer[0], blob->size) != 0) { + LOG(LOG_ERROR, "Failed to copy data to blob!\n"); + goto err; + } + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + return ret; +} + +/** Lock the NV index for further reads. + * + * @param[in] nv NV index to store the data. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvread_lock(TPMI_RH_NV_INDEX nv) +{ + if (!nv) { + return -1; + } + + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys context.\n"); + goto err; + } + + rc = Esys_TR_FromTPMPublic(ctx, nv, auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + + rc = Esys_NV_ReadLock(ctx, ESYS_TR_RH_OWNER, nvHandle, + auth_session_handle, ESYS_TR_NONE, ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to lock NV read.\n"); + goto err; + } + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + return ret; +} + +/** Delete data from a NV index. + * + * @param[in] nv NV index to delete. + * @retval 0 on success. + * @retval -1 on undefined/general failure. + * @retval TSS2_RC response code for failures relayed from the TSS library. + */ +int fdo_tpm_nvdel(TPMI_RH_NV_INDEX nv) +{ + int ret = -1; + TSS2_RC rc; + ESYS_CONTEXT *ctx; + ESYS_TR nvHandle = ESYS_TR_NONE; + ESYS_TR auth_session_handle = ESYS_TR_NONE; + + if (!nv) { + return -1; + } + + rc = fdo_tpm_esys_context_init(&ctx); + if (rc != TSS2_RC_SUCCESS || !ctx) { + LOG(LOG_ERROR, "Failed to intitialize Esys context.\n"); + goto err; + } + + rc = fdo_tpm_esys_auth_session_init(ctx, &auth_session_handle); + if (rc != TSS2_RC_SUCCESS || !auth_session_handle) { + LOG(LOG_ERROR, "Failed to create Auth Session for Esys API.\n"); + goto err; + } + + rc = Esys_Startup(ctx, TPM2_SU_CLEAR); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to start Esys API.\n"); + goto err; + } + + // Search the NV index + TPMS_CAPABILITY_DATA *capability_data = NULL; + rc = + Esys_GetCapability(ctx, ESYS_TR_NONE, ESYS_TR_NONE, ESYS_TR_NONE, + TPM2_CAP_HANDLES, nv, 1, NULL, &capability_data); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Esys_GetCapability failed!\n"); + goto err; + } + + int exists = (capability_data->data.handles.count > 0 && + capability_data->data.handles.handle[0] == nv); + if (exists != 1) { + LOG(LOG_DEBUG, "NV index doesn't exist.\n"); + ret = 0; + goto err; + } + + rc = Esys_TR_FromTPMPublic(ctx, nv, ESYS_TR_NONE, ESYS_TR_NONE, + ESYS_TR_NONE, &nvHandle); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to constructs an ESYS_TR object.\n"); + goto err; + } + + rc = Esys_NV_UndefineSpace(ctx, ESYS_TR_RH_OWNER, nvHandle, + auth_session_handle, ESYS_TR_NONE, + ESYS_TR_NONE); + if (rc != TSS2_RC_SUCCESS) { + LOG(LOG_ERROR, "Failed to undefine Esys NV space.\n"); + goto err; + } + nvHandle = ESYS_TR_NONE; + + ret = 0; + +err: + + if (ctx && (0 != fdo_tpm_context_clean_up(&ctx, &auth_session_handle, + &nvHandle))) { + LOG(LOG_ERROR, "Failed to tear down all the TSS context.\n"); + ret = -1; + } + + return ret; +} diff --git a/utils/clear_tpm_nv.sh b/utils/clear_tpm_nv.sh new file mode 100644 index 00000000..92faf761 --- /dev/null +++ b/utils/clear_tpm_nv.sh @@ -0,0 +1,35 @@ +#!/bin/bash +TPM_DEVICE_KEY_PERSISTANT_HANDLE=0x81020002 + +execute_cmd_on_failure_exit() +{ + eval exec_cmd="$1" + eval success_msg="$2" + eval failure_msg="$3" + + echo -e "\e[2;33mExecuting :\e[0m ${exec_cmd}" + out=$(eval ${exec_cmd}" 2>&1") + if [ `echo $?` != 0 ]; then + echo -e "\e[2;31m${failure_msg}\e[0m" + else + echo -e "\e[2;32m${success_msg}\e[0m" + fi + + return 0 +} + +task="Delete keys if exists from persistance memory" +cmd="tpm2_evictcontrol -C o -c $TPM_DEVICE_KEY_PERSISTANT_HANDLE -V" +success_string="$task completed successfully at $TPM_DEVICE_KEY_PERSISTANT_HANDLE !!" +failure_string="$task failed [probably ignore it]" +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" + +for n in {0,1,5}; +do + task="Deleting a Non-Volatile (NV) index at 0x01D1000$n" + cmd="tpm2_nvundefine 0x01D1000$n" + success_string="$task completed successfully!!" + failure_string="Non-Volatile (NV) index at 0x01D1000$n is not defined!!" + execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" +done +echo "TPM NV storage cleared!" diff --git a/utils/tpm_make_ready_ecdsa.sh b/utils/tpm_make_ready_ecdsa.sh index 3a9972a7..45c51a61 100644 --- a/utils/tpm_make_ready_ecdsa.sh +++ b/utils/tpm_make_ready_ecdsa.sh @@ -1,12 +1,14 @@ export TPM2TOOLS_TCTI="tabrmd" export OPENSSL3_BIN=/opt/openssl/bin -TPM_KEY_FILE_INSIDE_DATA_DIR="tpm_ecdsa_priv_pub_blob.key" +TPM_PUB_KEY_FILE_INSIDE_DATA_DIR="tpm_ecdsa_pub.key" +TPM_PRIV_KEY_FILE_INSIDE_DATA_DIR="tpm_ecdsa_priv.key" DEVICE_CSR_FILE_INSIDE_DATA_DIR="tpm_device_csr" PARENT_DIR="" TPM_ENDORSEMENT_PRIMARY_KEY_CTX=tpm_primary_key.ctx -TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE=0x81000001 +TPM_ECDSA_KEY_CTX=tpm_ecdsa_key.ctx +TPM_DEVICE_KEY_PERSISTANT_HANDLE=0x81020002 found_path=0 verbose=0 @@ -84,41 +86,64 @@ echo "$TPM2TOOLS_TCTI in use as Resource Manager" #Prepare all files path tpm_endorsement_primary_key_ctx=$PARENT_DIR"/"$TPM_ENDORSEMENT_PRIMARY_KEY_CTX -tpm_device_key_file=$PARENT_DIR"/"$TPM_KEY_FILE_INSIDE_DATA_DIR +tpm_ecdsa_key_ctx=$PARENT_DIR"/"$TPM_ECDSA_KEY_CTX +tpm_device_pub_key_file=$PARENT_DIR"/"$TPM_PUB_KEY_FILE_INSIDE_DATA_DIR +tpm_device_priv_key_file=$PARENT_DIR"/"$TPM_PRIV_KEY_FILE_INSIDE_DATA_DIR device_csr_file=$PARENT_DIR"/"$DEVICE_CSR_FILE_INSIDE_DATA_DIR -echo "TPM Device Key file location : $tpm_device_key_file" -echo "TPM Device CSR file location : $device_csr_file" - -rm -f $tpm_endorsement_primary_key_ctx - -task="Delete keys if exists from persistance memory" -cmd="tpm2_evictcontrol -C o -c $TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE -V" -success_string="$task completed successfully at $TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE !!" -failure_string="$task failed [probably ignore it]" -execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 0 - task="Primary key generation from endorsement seed" cmd="tpm2_createprimary -C e -g sha$ecc -G $primary_key_type -c $tpm_endorsement_primary_key_ctx -V" success_string="$task completed successfully at $tpm_endorsement_primary_key_ctx !!" failure_string="$task failed" execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 -task="Load primary key inside persistance memory at $TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE" -cmd="tpm2_evictcontrol -C o $TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE -c $tpm_endorsement_primary_key_ctx -V" -success_string="$task completed successfully at $TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE !!" +task="TPM ECDSA keys generation" +cmd="tpm2_create -g sha$ecc -G ecc$ecc -u $tpm_device_pub_key_file -r $tpm_device_priv_key_file -C $tpm_endorsement_primary_key_ctx -a \"fixedtpm|sensitivedataorigin|fixedparent|sign|userwithauth\" -V" +success_string="$task completed successfully at $tpm_endorsement_primary_key_ctx !!" +failure_string="$task failed" +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 + +task="Load ECDSA keys" +cmd="tpm2_load -C $tpm_endorsement_primary_key_ctx -u $tpm_device_pub_key_file -r $tpm_device_priv_key_file -c $tpm_ecdsa_key_ctx -V" +success_string="$task completed successfully!!" failure_string="$task failed" execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 -task="TPM ECDSA key generation using $curve" -cmd="$OPENSSL3_BIN/openssl genpkey -provider tpm2 -algorithm EC -pkeyopt group:P-$ecc -pkeyopt parent:$TPM_ENDORSEMENT_PRIMARY_KEY_PERSISTANT_HANDLE -out $tpm_device_key_file" -success_string="$task completed successfully at $tpm_device_key_file !!" +task="Copy ECDSA keys inside persistance memory at $TPM_DEVICE_KEY_PERSISTANT_HANDLE" +cmd="tpm2_evictcontrol -C o $TPM_DEVICE_KEY_PERSISTANT_HANDLE -c $tpm_ecdsa_key_ctx -V" +success_string="$task completed successfully!!" failure_string="$task failed" -execute_cmd_on_failure_exit "\${cmd}" "\${success_string}" "\${failure_string}" 1 1 +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 task="Device CSR generation from TPM" -cmd="$OPENSSL3_BIN/openssl req -new -provider tpm2 -provider default -outform DER -out $device_csr_file -key $tpm_device_key_file -subj \"/CN=sdo-tpm-device\" -verbose" -success_string="$task completed successfully at $device_csr_file !!" +cmd="$OPENSSL3_BIN/openssl req -new -provider tpm2 -provider default -outform DER -out $device_csr_file -key handle:$TPM_DEVICE_KEY_PERSISTANT_HANDLE -subj \"/CN=fdo-tpm-device\" -verbose" +success_string="$task completed successfully!!" +failure_string="$task failed" +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 + +# # write device csr inside tpm +task="Define a TPM Non-Volatile (NV) index for TPM Device CSR" +csr_size=$(wc -c < $device_csr_file) +cmd="tpm2_nvdefine -Q 0x01D10005 -C o -s $csr_size -a \"ownerwrite|authwrite|ownerread|authread|no_da|read_stclear|writedefine\"" +success_string="$task completed successfully!!" +failure_string="$task failed" +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 + +task="Write TPM Device CSR to a Non-Volatile (NV) index" +cmd="tpm2_nvwrite -Q 0x01D10005 -C o -i $device_csr_file" +success_string="$task completed successfully!!" failure_string="$task failed" execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 +task="Lock the Device CSR Non-Volatile (NV) index for further writes" +cmd="tpm2_nvwritelock -C o 0x01D10005" +success_string="$task completed successfully!!" +failure_string="$task failed" +execute_cmd_on_failure_exit "\$cmd" "\$success_string" "\$failure_string" 1 1 + +rm -f $tpm_device_pub_key_file +rm -f $tpm_device_priv_key_file +rm -f $device_csr_file +rm -f $tpm_endorsement_primary_key_ctx +rm -f $tpm_ecdsa_key_ctx +