From 1f366453ff51658d217f70f276ea11de40682df3 Mon Sep 17 00:00:00 2001 From: Shrikant Temburwar Date: Mon, 13 Nov 2023 18:34:43 +0530 Subject: [PATCH] Add support to store device credentials and device status inside TPM NV storage Signed-off-by: Shrikant Temburwar --- app/blob.c | 1 + app/main.c | 2 +- cmake/blob_path.cmake | 16 +- lib/credentials_from_file.c | 299 +++++++++++++- lib/fdo.c | 5 +- lib/include/load_credentials.h | 8 + lib/prot/di/msg13.c | 5 + lib/prot/to2/msg70.c | 5 + network/network_if_linux.c | 29 +- storage/CMakeLists.txt | 6 + storage/include/storage_al.h | 9 + storage/include/tpm2_nv_storage.h | 64 +++ storage/linux/storage_if_linux.c | 504 ++++++++++++++++++++++++ storage/linux/tpm2_nv_storage.c | 633 ++++++++++++++++++++++++++++++ 14 files changed, 1558 insertions(+), 28 deletions(-) create mode 100644 storage/include/tpm2_nv_storage.h create mode 100644 storage/linux/tpm2_nv_storage.c diff --git a/app/blob.c b/app/blob.c index 55b75acc..7e12e434 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) diff --git a/app/main.c b/app/main.c index 695fe1ad..54011c32 100644 --- a/app/main.c +++ b/app/main.c @@ -272,7 +272,7 @@ int app_main(bool is_resale) } #endif /* SECURE_ELEMENT */ -#if !defined(DEVICE_CSE_ENABLED) +#if !defined(DEVICE_CSE_ENABLED) && !defined(DEVICE_TPM20_ENABLED) LOG(LOG_DEBUG, "CSE not enabled, Normal Blob Modules loaded!\n"); if (-1 == configure_normal_blob()) { LOG(LOG_ERROR, diff --git a/cmake/blob_path.cmake b/cmake/blob_path.cmake index ec008ae1..0ec57d79 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\" @@ -176,9 +176,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/lib/credentials_from_file.c b/lib/credentials_from_file.c index 597a4a0b..6e552b41 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,291 @@ 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 dev_cred_file - pointer of type const char to which credentails are + * to be written. + * @param flags + * @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_sdk_blob_flags flags, + 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; + } + + /** + * Blob format: Complete DeviceCredential as per Section 3.4.1 of FDO + *Specification, except the DeviceCredential.DCHmacSecret, and addition + *of 'State'. DeviceCredential = [ State, DCActive, DCProtVer, + * DCDeviceInfo, + * DCGuid, + * DCRVInfo, + * DCPubKeyHash + * ] + */ + fdow_next_block(fdow, FDO_DI_SET_CREDENTIALS); + if (!fdow_start_array(fdow, 7)) { + ret = false; + goto end; + } + if (!fdow_signed_int(fdow, ocred->ST)) { + ret = false; + goto end; + } + if (!fdow_boolean(fdow, true)) { + 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_blob_write_nv(nv, flags, fdow->b.block, fdow->b.block_size) == + -1) { + LOG(LOG_ERROR, "Failed to write DeviceCredential blob\n"); + ret = false; + goto end; + } + +end: + if (fdow) { + fdow_flush(fdow); + fdo_free(fdow); + } #endif + return ret; +} + +/** + * Read the Device Credentials blob from tpm nv, contains our state & owner_blk + * @param dev_cred_file - the blob the credentials are saved in + * @param flags - descriptor telling type of file + * @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_sdk_blob_flags flags, + 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_blob_size_nv(nv, flags); + // 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, + "DeviceCredential not found. Proceeding with DI\n"); + our_dev_cred->ST = FDO_DEVICE_STATE_PC; + return true; + } + + LOG(LOG_DEBUG, "Reading DeviceCredential blob 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_blob_read_nv(nv, flags, fdor->b.block, fdor->b.block_size) == + -1) { + LOG(LOG_ERROR, + "Failed to read DeviceCredential blob : Normal.blob\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_boolean(fdor, &our_dev_cred->dc_active)) { + LOG(LOG_ERROR, "DeviceCredential read: DCActive not found\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"); + if (!write_tpm_device_credentials(FDO_CRED_NORMAL_NV_IDX, + FDO_SDK_NORMAL_DATA, ocred)) { + LOG(LOG_ERROR, "Could not write to Normal Credentials blob\n"); + return -1; + } return 0; } #endif @@ -627,6 +912,13 @@ 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 in the blob and save the device credentials */ + if (!read_normal_tpm_credentials(FDO_CRED_NORMAL_NV_IDX, + FDO_SDK_NORMAL_DATA, ocred)) { + LOG(LOG_ERROR, "Could not parse the Device Credentials blob\n"); + return -1; + } #else /* Read in the blob and save the device credentials */ if (!read_normal_device_credentials((char *)FDO_CRED_NORMAL, @@ -684,6 +976,9 @@ 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_blob_size_nv(FDO_CRED_NORMAL_NV_IDX, FDO_SDK_NORMAL_DATA); #else size_t dev_cred_len = fdo_blob_size((char *)FDO_CRED_NORMAL, FDO_SDK_NORMAL_DATA); diff --git a/lib/fdo.c b/lib/fdo.c index 737f9998..aefd0cba 100644 --- a/lib/fdo.c +++ b/lib/fdo.c @@ -1160,8 +1160,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"); diff --git a/lib/include/load_credentials.h b/lib/include/load_credentials.h index 8e3ecd95..78877652 100644 --- a/lib/include/load_credentials.h +++ b/lib/include/load_credentials.h @@ -32,4 +32,12 @@ 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_sdk_blob_flags flags, + fdo_dev_cred_t *our_dev_cred); +bool write_tpm_device_credentials(uint32_t nv, fdo_sdk_blob_flags flags, + 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/prot/di/msg13.c b/lib/prot/di/msg13.c index 5d2fe429..5ff4bf0a 100644 --- a/lib/prot/di/msg13.c +++ b/lib/prot/di/msg13.c @@ -84,6 +84,11 @@ 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) + 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..7be9e029 100644 --- a/lib/prot/to2/msg70.c +++ b/lib/prot/to2/msg70.c @@ -124,6 +124,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/network/network_if_linux.c b/network/network_if_linux.c index 47658b85..4235a238 100644 --- a/network/network_if_linux.c +++ b/network/network_if_linux.c @@ -471,8 +471,7 @@ int32_t fdo_curl_connect(fdo_ip_address_t *ip_addr, const char *dn, curlCode = curl_easy_setopt(curl, CURLOPT_URL, url); if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, - "CURL_ERROR: Unable to pass url.\n"); + LOG(LOG_ERROR, "CURL_ERROR: Unable to pass url.\n"); goto err; } @@ -893,8 +892,7 @@ int32_t fdo_con_send_recv_message(uint32_t protocol_version, curlCode = curl_easy_setopt(curl, CURLOPT_SSLKEY, (char *)SSL_KEY); if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, - "CURL_ERROR: Unable to select client key.\n"); + LOG(LOG_ERROR, "CURL_ERROR: Unable to select client key.\n"); goto err; } #endif @@ -925,8 +923,7 @@ int32_t fdo_con_send_recv_message(uint32_t protocol_version, curlCode = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, buf); if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, - "CURL_ERROR: Unable to pass POST data.\n"); + LOG(LOG_ERROR, "CURL_ERROR: Unable to pass POST data.\n"); goto err; } @@ -957,8 +954,7 @@ int32_t fdo_con_send_recv_message(uint32_t protocol_version, curlCode = curl_easy_setopt(curl, CURLOPT_HEADERDATA, (void *)&temp_header_buf); if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, - "CURL_ERROR: Unable to pass header buffer.\n"); + LOG(LOG_ERROR, "CURL_ERROR: Unable to pass header buffer.\n"); goto err; } @@ -973,8 +969,7 @@ int32_t fdo_con_send_recv_message(uint32_t protocol_version, curlCode = curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&temp_body_buf); if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, - "CURL_ERROR: Unable to pass body buffer.\n"); + LOG(LOG_ERROR, "CURL_ERROR: Unable to pass body buffer.\n"); goto err; } @@ -985,13 +980,13 @@ int32_t fdo_con_send_recv_message(uint32_t protocol_version, goto err; } -#ifdef DEBUG_LOGS - curlCode = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - if (curlCode != CURLE_OK) { - LOG(LOG_ERROR, "CURL_ERROR: Could not enable curl logs.\n"); - goto err; - } -#endif + // #ifdef DEBUG_LOGS + // curlCode = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + // if (curlCode != CURLE_OK) { + // LOG(LOG_ERROR, "CURL_ERROR: Could not enable curl + // logs.\n"); goto err; + // } + // #endif curlCode = curl_easy_perform(curl); if (curlCode != CURLE_OK) { 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..ae3f6523 100644 --- a/storage/include/storage_al.h +++ b/storage/include/storage_al.h @@ -46,6 +46,15 @@ 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_blob_read_nv(uint32_t nv, fdo_sdk_blob_flags flags, uint8_t *buffer, + uint32_t length); + +int32_t fdo_blob_write_nv(uint32_t nv, fdo_sdk_blob_flags flags, + const uint8_t *buffer, uint32_t length); + +size_t fdo_blob_size_nv(uint32_t nv, fdo_sdk_blob_flags flags); +#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..6f8e1049 --- /dev/null +++ b/storage/include/tpm2_nv_storage.h @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include + +#define FDO_CRED_NORMAL_NV_IDX 0x1000001 +#define FDO_CRED_SECURE_NV_IDX 0x1000002 + +#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(uint32_t 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, uint32_t 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(uint32_t 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(uint32_t nv, size_t data_size, uint8_t **data); + +/** 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(uint32_t 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..9859aedd 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 "tpm2_nv_storage.h" +#endif /**************************************************** * @@ -562,3 +565,504 @@ int32_t fdo_blob_write(const char *name, fdo_sdk_blob_flags flags, } return retval; } + +#if defined(DEVICE_TPM20_ENABLED) +/** + * fdo_blob_size Get specified FDO blob(file) size + * Note: FDO_SDK_OTP_DATA flag is not supported for this platform. + * @param nv - ptpm nv index + * @param flags - descriptor telling type of file + * @return file size on success, 0 if file does not exist or on other failure + */ + +size_t fdo_blob_size_nv(uint32_t nv, fdo_sdk_blob_flags flags) +{ + size_t retval = 0; + const size_t NORMAL_BLOB_OVERHEAD = + PLATFORM_HMAC_SIZE + BLOB_CONTENT_SIZE; + const size_t SECURE_BLOB_OVERHEAD = + AES_TAG_LEN + PLATFORM_IV_DEFAULT_LEN + BLOB_CONTENT_SIZE; + + if (!nv) { + LOG(LOG_ERROR, "Invalid parameters!\n"); + goto end; + } + + // Return 0 if the file is empty. + if (fdo_tpm_nvread_size(nv) == 0) { + LOG(LOG_DEBUG, "NV is empty!\n"); + retval = 0; + goto end; + } + + switch (flags) { + case FDO_SDK_RAW_DATA: + /* Raw Files are stored as plain files */ + retval = fdo_tpm_nvread_size(nv); + break; + case FDO_SDK_NORMAL_DATA: + /* Normal blob is stored as: + * [HMAC(32bytes)||data-content-size(4bytes)||data-content(?)] + */ + retval = fdo_tpm_nvread_size(nv); + + if (retval >= NORMAL_BLOB_OVERHEAD) { + retval -= NORMAL_BLOB_OVERHEAD; + } else { + /* File format is not correct, not enough data in the + * file */ + retval = 0; + } + break; + case FDO_SDK_SECURE_DATA: + /* Secure blob is stored as: + * [IV_data(12byte)||TAG(16bytes)|| + * data-content-size(4bytes)||data-content(?)] + */ + retval = fdo_tpm_nvread_size(nv); + if (retval >= SECURE_BLOB_OVERHEAD) { + retval -= SECURE_BLOB_OVERHEAD; + } else { + /* File format is not correct, not enough data in the + * file */ + retval = 0; + } + break; + default: + LOG(LOG_ERROR, "Invalid storage flag:%d!\n", flags); + goto end; + } + +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_blob_read_nv(uint32_t nv, fdo_sdk_blob_flags flags, 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 *encrypted_data = NULL; + uint32_t encrypted_data_len = 0; + uint8_t stored_hmac[PLATFORM_HMAC_SIZE] = {0}; + uint8_t computed_hmac[PLATFORM_HMAC_SIZE] = {0}; + uint8_t stored_tag[AES_TAG_LEN] = {0}; + int strcmp_result = -1; + uint8_t iv[PLATFORM_IV_DEFAULT_LEN] = {0}; + uint8_t aes_key[PLATFORM_AES_KEY_DEFAULT_LEN] = {0}; + size_t dat_len_offst = 0; + + 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; + } + + switch (flags) { + case FDO_SDK_RAW_DATA: + if (0 != fdo_tpm_nvread(nv, n_bytes, &buf)) { + LOG(LOG_ERROR, "Failed to read file!\n"); + goto exit; + } + break; + + case FDO_SDK_NORMAL_DATA: + /* 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_compute_storage_hmac(data, data_length, + computed_hmac, + PLATFORM_HMAC_SIZE)) { + 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; + } + break; + + case FDO_SDK_SECURE_DATA: + /* AES GCM authenticated encryption is being used to store files + * under + * FDO_SDK_SECURE_DATA flag. File content to be stored as: + * [IV_data(12byte)||[AuthenticatedTAG(16 bytes)|| + * Sizeof_ciphertext(8 * bytes)||Ciphertet(n_bytes bytes)] + */ + + encrypted_data_len = PLATFORM_IV_DEFAULT_LEN + AES_TAG_LEN + + BLOB_CONTENT_SIZE + n_bytes; + + encrypted_data = fdo_alloc(encrypted_data_len); + if (NULL == encrypted_data) { + LOG(LOG_ERROR, "Malloc Failed in %s!\n", __func__); + goto exit; + } + + if (fdo_tpm_nvread(nv, encrypted_data_len, &encrypted_data)) { + LOG(LOG_ERROR, "Failed to read file!\n"); + goto exit; + } + + dat_len_offst = AES_TAG_LEN + PLATFORM_IV_DEFAULT_LEN; + // get actual data length + data_length |= encrypted_data[dat_len_offst] << 24; + data_length |= encrypted_data[dat_len_offst + 1] << 16; + data_length |= encrypted_data[dat_len_offst + 2] << 8; + data_length |= (encrypted_data[dat_len_offst + 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; + } + /* read the iv from blob */ + if (memcpy_s(iv, PLATFORM_IV_DEFAULT_LEN, encrypted_data, + PLATFORM_IV_DEFAULT_LEN) != 0) { + LOG(LOG_ERROR, + "Copying stored IV failed during " + "%s!\n", + __func__); + goto exit; + } + + if (memcpy_s(stored_tag, AES_TAG_LEN, + encrypted_data + PLATFORM_IV_DEFAULT_LEN, + AES_TAG_LEN) != 0) { + LOG(LOG_ERROR, + "Copying stored TAG failed during " + "%s!\n", + __func__); + goto exit; + } + + data = encrypted_data + PLATFORM_IV_DEFAULT_LEN + AES_TAG_LEN + + BLOB_CONTENT_SIZE; + + if (!get_platform_aes_key(aes_key, + PLATFORM_AES_KEY_DEFAULT_LEN)) { + LOG(LOG_ERROR, "Could not get platform AES Key!\n"); + goto exit; + } + + // decrypt and authenticate cipher-text content and fill the + // given buffer with clear-text + if (crypto_hal_aes_decrypt( + buf, &n_bytes, data, data_length, 16, iv, aes_key, + PLATFORM_AES_KEY_DEFAULT_LEN, stored_tag, AES_TAG_LEN, + NULL, 0) < 0) { + LOG(LOG_ERROR, "Decryption failed during Secure " + "Blob Read!\n"); + goto exit; + } + break; + + default: + LOG(LOG_ERROR, "Invalid FDO blob flag!!\n"); + goto exit; + } + + retval = (int32_t)n_bytes; + +exit: + if (sealed_data) { + fdo_free(sealed_data); + } + if (encrypted_data) { + fdo_free(encrypted_data); + } + if (memset_s(aes_key, PLATFORM_AES_KEY_DEFAULT_LEN, 0)) { + LOG(LOG_ERROR, "Failed to clear AES key\n"); + retval = -1; + } + 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_blob_write_nv(uint32_t nv, fdo_sdk_blob_flags flags, + const uint8_t *buf, uint32_t n_bytes) +{ + int retval = -1; + FILE *f = NULL; + uint32_t write_context_len = 0; + uint32_t write_context_len_temp = 0; + uint8_t *write_context = NULL; + uint8_t tag[AES_TAG_LEN] = {0}; + uint8_t iv[PLATFORM_IV_DEFAULT_LEN] = {0}; + uint8_t aes_key[PLATFORM_AES_KEY_DEFAULT_LEN] = {0}; + size_t dat_len_offst = 0; + + 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; + } + + switch (flags) { + case FDO_SDK_RAW_DATA: + // Raw Files are stored as plain files + 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; + } + break; + + case FDO_SDK_NORMAL_DATA: + /* 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_compute_storage_hmac(buf, n_bytes, write_context, + PLATFORM_HMAC_SIZE)) { + LOG(LOG_ERROR, "Computing HMAC failed during Normal " + "Blob write!\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; + } + break; + + case FDO_SDK_SECURE_DATA: + /* AES GCM authenticated encryption is being used to store files + * under + * FDO_SDK_SECURE_DATA flag. File content to be stored as: + * [IV_data(12byte)||[AuthenticatedTAG(16 bytes)|| + * Sizeof_ciphertext(8 * bytes)||Ciphertet(n_bytes bytes)] + */ + + write_context_len = PLATFORM_IV_DEFAULT_LEN + AES_TAG_LEN + + 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 (!get_platform_iv(iv, PLATFORM_IV_DEFAULT_LEN, n_bytes)) { + LOG(LOG_ERROR, "Could not get platform IV!\n"); + goto exit; + } + + if (!get_platform_aes_key(aes_key, + PLATFORM_AES_KEY_DEFAULT_LEN)) { + LOG(LOG_ERROR, "Could not get platform AES Key!\n"); + goto exit; + } + + write_context_len_temp = + write_context_len - + (PLATFORM_IV_DEFAULT_LEN + AES_TAG_LEN + BLOB_CONTENT_SIZE); + // encrypt plain-text and copy cipher-text content + if (crypto_hal_aes_encrypt( + buf, n_bytes, + &write_context[PLATFORM_IV_DEFAULT_LEN + AES_TAG_LEN + + BLOB_CONTENT_SIZE], + &write_context_len_temp, 16, iv, aes_key, + PLATFORM_AES_KEY_DEFAULT_LEN, tag, AES_TAG_LEN, NULL, + 0) < 0) { + LOG(LOG_ERROR, "Encypting data failed during Secure " + "Blob write!\n"); + goto exit; + } + // copy used IV for encryption + if (memcpy_s(write_context, PLATFORM_IV_DEFAULT_LEN, iv, + PLATFORM_IV_DEFAULT_LEN) != 0) { + LOG(LOG_ERROR, "Copying TAG value failed during Secure " + "Blob write!\n"); + goto exit; + } + + // copy Authenticated TAG value + if (memcpy_s(write_context + PLATFORM_IV_DEFAULT_LEN, + write_context_len - PLATFORM_IV_DEFAULT_LEN, tag, + AES_TAG_LEN) != 0) { + LOG(LOG_ERROR, "Copying TAG value failed during Secure " + "Blob write!\n"); + goto exit; + } + + dat_len_offst = AES_TAG_LEN + PLATFORM_IV_DEFAULT_LEN; + /* copy cipher-text size; CT size= PT size (AES GCM uses AES CTR + * mode internally for encryption) + */ + write_context[dat_len_offst + 3] = n_bytes >> 0; + write_context[dat_len_offst + 2] = n_bytes >> 8; + write_context[dat_len_offst + 1] = n_bytes >> 16; + write_context[dat_len_offst + 0] = n_bytes >> 24; + break; + + default: + LOG(LOG_ERROR, "Invalid FDO blob flag!!\n"); + goto exit; + } + + if (fdo_tpm_nvwrite(write_context, write_context_len, nv)) { + LOG(LOG_ERROR, "Could not get platform AES Key!\n"); + goto exit; + } + + retval = (int32_t)n_bytes; + +exit: + if (write_context) { + fdo_free(write_context); + } + if (f) { + if (fclose(f) == EOF) { + LOG(LOG_ERROR, "fclose() Failed in %s\n", __func__); + } + } + if (memset_s(aes_key, PLATFORM_AES_KEY_DEFAULT_LEN, 0)) { + LOG(LOG_ERROR, "Failed to clear AES key\n"); + retval = -1; + } + 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..93609e1b --- /dev/null +++ b/storage/linux/tpm2_nv_storage.c @@ -0,0 +1,633 @@ +#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(uint32_t 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_WRITE_STCLEAR | TPMA_NV_READ_STCLEAR | + TPMA_NV_AUTHREAD | TPMA_NV_OWNERREAD), + .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, uint32_t 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_WRITE_STCLEAR | TPMA_NV_READ_STCLEAR | + TPMA_NV_AUTHREAD | TPMA_NV_OWNERREAD), + .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; + } + // memcpy(&blob.buffer[0], data, blob.size); + + 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, 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; + } + } + + 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; +} + +/** 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(uint32_t 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, 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_ReadPublic(ctx, nvHandle, ESYS_TR_NONE, 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(uint32_t 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; + TPM2B_NV_PUBLIC *publicInfo; + + 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, 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_ReadPublic(ctx, nvHandle, ESYS_TR_NONE, 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; + } + + 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; + } + + // *data_size = blob->size; + // *data = malloc(blob->size); + // memcpy(*data, &blob->buffer[0], blob->size); + 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 (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; +} + +/** 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(uint32_t 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_ERROR, "NV index doesn't exist.\n"); + 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; +}