core/spdm/spdm_transcript_manager.c (412 lines of code) (raw):

// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. #include <string.h> #include "spdm_commands.h" #include "spdm_protocol.h" #include "spdm_transcript_manager.h" #include "common/array_size.h" #include "common/common_math.h" #include "common/unused.h" /** * Add a message to the hash context. A new hash will be started with the message data using * the negotiated hash algorithm if there is no active hash for the context. * * There is no validation on the parameters since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param hash_context Hash context to add the message to. * @param message Message to add to the hash context. * @param message_size Size of message. * @param add_vca Flag indicating if the VCA buffer should be added to the hash. * * @return 0 if the message was added to the hash successfully or an error code. */ static int spdm_transcript_manager_add_msg ( const struct spdm_transcript_manager *transcript_manager, struct spdm_transcript_manager_hash_context *hash_context, const void *message, size_t message_size, bool add_vca) { int status; const struct hash_engine *hash_engine; hash_engine = transcript_manager->hash_engine[hash_context->hash_engine_idx]; /* Start the hash if it has not been started. */ if (hash_context->hash_started == false) { status = hash_start_new_hash (hash_engine, transcript_manager->state->hash_algo); if (status != 0) { goto exit; } hash_context->hash_started = true; /* Add the VCA buffer to the hash context if requested by the caller * and only if the hash was just started. */ if (add_vca == true) { status = hash_engine->update (hash_engine, transcript_manager->state->message_vca.buffer, transcript_manager->state->message_vca.buffer_size); if (status != 0) { goto exit; } } } /* Update the hash context with the message. */ status = hash_engine->update (hash_engine, message, message_size); if (status != 0) { goto exit; } exit: return status; } /** * Update VCA cache. * * There is no validation on the parameters since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param message_size Size of message to append to message VCA cache. * @param message Message to append to message A cache. * * @return 0 if the message was appended to the cache successfully or an error code. */ static int spdm_transcript_manager_update_vca ( const struct spdm_transcript_manager *transcript_manager, const uint8_t *message, size_t message_size) { int status = 0; struct spdm_transcript_manager_vca_managed_buffer *message_vca; message_vca = &transcript_manager->state->message_vca; if (message_size > (ARRAY_SIZE (message_vca->buffer) - message_vca->buffer_size)) { status = SPDM_TRANSCRIPT_MANAGER_BUFFER_FULL; goto exit; } memcpy (message_vca->buffer + message_vca->buffer_size, message, message_size); message_vca->buffer_size += message_size; exit: return status; } /** * Update the M1M2 Hash. * * There is no validation on the parameter since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param message Message to add to the hash. * @param message_size Size of message. * * @return 0 if the message was added to the hash successfully or an error code. */ static int spdm_transcript_manager_update_m1m2 ( const struct spdm_transcript_manager *transcript_manager, const uint8_t *message, size_t message_size) { int status = 0; struct spdm_transcript_manager_state *state = transcript_manager->state; struct spdm_transcript_manager_hash_context *hash_context = &state->m1m2; /* Add the message to the hash context. */ status = spdm_transcript_manager_add_msg (transcript_manager, hash_context, message, message_size, true); if (status != 0) { goto exit; } exit: return status; } /** * Update the L1L2 Hash. * * There is no validation on the parameter since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param message Message to add to the hash. * @param message_size Size of message. * * @return 0 if the message was added to the hash successfully or an error code. */ static int spdm_transcript_manager_update_l1l2 ( const struct spdm_transcript_manager *transcript_manager, const uint8_t *message, size_t message_size, bool use_session_context, uint32_t session_idx) { int status = 0; struct spdm_transcript_manager_state *state = transcript_manager->state; struct spdm_transcript_manager_hash_context *hash_context; if (use_session_context == true) { hash_context = &state->session_transcript[session_idx].l1l2; } else { hash_context = &state->l1l2; } /* Add the message to the hash context. */ status = spdm_transcript_manager_add_msg (transcript_manager, hash_context, message, message_size, (state->spdm_version > SPDM_VERSION_1_1)); if (status != 0) { goto exit; } exit: return status; } /** * Update the TH Hash. * * There is no validation on the parameters since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param message Message to add to the hash. * @param message_size Size of message. * @param session_idx Index of the session transcript context to update the hash for. * * @return 0 if the message was added to the hash successfully or an error code. */ static int spdm_transcript_manager_update_th ( const struct spdm_transcript_manager *transcript_manager, const uint8_t *message, size_t message_size, uint32_t session_idx) { int status = 0; struct spdm_transcript_manager_state *state = transcript_manager->state; struct spdm_transcript_manager_hash_context *hash_context; hash_context = &state->session_transcript[session_idx].th; /* Add the message to the hash context. */ status = spdm_transcript_manager_add_msg (transcript_manager, hash_context, message, message_size, true); if (status != 0) { goto exit; } exit: return status; } /** * Clear the VCA message buffer. * * There is no validation on the parameter since this is an internal function. * * @param transcript_manager Transcript manager instance. */ static void spdm_transcript_manager_reset_vca ( const struct spdm_transcript_manager *transcript_manager) { struct spdm_transcript_manager_vca_managed_buffer *managed_buffer = &transcript_manager->state->message_vca; if (managed_buffer->buffer_size != 0) { memset (managed_buffer->buffer, 0, managed_buffer->buffer_size); managed_buffer->buffer_size = 0; } } /** * Reset the M1M2 hash engine context. * * There is no validation on the parameter since this is an internal function. * * @param transcript_manager Transcript manager instance. */ static void spdm_transcript_manager_reset_m1m2 ( const struct spdm_transcript_manager *transcript_manager) { const struct hash_engine *m1m2; struct spdm_transcript_manager_state *state = transcript_manager->state; if (state->m1m2.hash_started == true) { m1m2 = transcript_manager->hash_engine[state->m1m2.hash_engine_idx]; m1m2->cancel (m1m2); state->m1m2.hash_started = false; } } /** * Reset the L1L2 global or session hash engine context. * * There is no validation on the parameter since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param use_session_context Flag indicating if the session context should be used. * @param session_idx Index of the session transcript context to reset the hash for. */ static void spdm_transcript_manager_reset_l1l2 ( const struct spdm_transcript_manager *transcript_manager, bool use_session_context, uint8_t session_idx) { const struct hash_engine *l1l2; struct spdm_transcript_manager_hash_context *hash_context; struct spdm_transcript_manager_state *state = transcript_manager->state; hash_context = (use_session_context == true) ? &state->session_transcript[session_idx].l1l2 : &state->l1l2; if (hash_context->hash_started == true) { l1l2 = transcript_manager->hash_engine[hash_context->hash_engine_idx]; l1l2->cancel (l1l2); hash_context->hash_started = false; } } /** * Reset the TH hash engine context. * * There is no validation on the parameters since this is an internal function. * * @param transcript_manager Transcript manager instance. * @param session_idx Index of the session transcript context to reset the hash for. */ static void spdm_transcript_manager_reset_th ( const struct spdm_transcript_manager *transcript_manager, uint8_t session_idx) { const struct hash_engine *th; struct spdm_transcript_manager_hash_context *hash_context; struct spdm_transcript_manager_state *state = transcript_manager->state; hash_context = &state->session_transcript[session_idx].th; if (hash_context->hash_started == true) { th = transcript_manager->hash_engine[hash_context->hash_engine_idx]; th->cancel (th); hash_context->hash_started = false; } } void spdm_transcript_manager_reset_session_transcript ( const struct spdm_transcript_manager *transcript_manager, uint8_t session_idx) { struct spdm_transcript_manager_state *state; if (transcript_manager == NULL) { return; } state = transcript_manager->state; if (session_idx >= state->session_transcript_count) { return; } spdm_transcript_manager_reset_l1l2 (transcript_manager, true, session_idx); spdm_transcript_manager_reset_th (transcript_manager, session_idx); } void spdm_transcript_manager_reset_context ( const struct spdm_transcript_manager *transcript_manager, enum spdm_transcript_manager_context_type context_type, bool use_session_context, uint8_t session_idx) { struct spdm_transcript_manager_state *state; if (transcript_manager != NULL) { state = transcript_manager->state; /* M1/M2 hash is only valid for the global SPDM requester/responder. */ /* TH hash is only valid for an SPDM session. */ if (((use_session_context == true) && (context_type == TRANSCRIPT_CONTEXT_TYPE_M1M2)) || ((use_session_context == false) && (context_type == TRANSCRIPT_CONTEXT_TYPE_TH))) { return; } if ((use_session_context == true) && (session_idx >= state->session_transcript_count)) { return; } switch (context_type) { case TRANSCRIPT_CONTEXT_TYPE_VCA: spdm_transcript_manager_reset_vca (transcript_manager); break; case TRANSCRIPT_CONTEXT_TYPE_M1M2: spdm_transcript_manager_reset_m1m2 (transcript_manager); break; case TRANSCRIPT_CONTEXT_TYPE_L1L2: case TRANSCRIPT_CONTEXT_TYPE_TH: if (context_type == TRANSCRIPT_CONTEXT_TYPE_L1L2) { spdm_transcript_manager_reset_l1l2 (transcript_manager, use_session_context, session_idx); } else { spdm_transcript_manager_reset_th (transcript_manager, session_idx); } break; default: break; } } return; } void spdm_transcript_manager_reset (const struct spdm_transcript_manager *transcript_manager) { uint8_t session_idx; struct spdm_transcript_manager_state *state; if (transcript_manager != NULL) { state = transcript_manager->state; state->hash_algo = HASH_TYPE_INVALID; /* Reset global transcripts. */ spdm_transcript_manager_reset_vca (transcript_manager); spdm_transcript_manager_reset_m1m2 (transcript_manager); spdm_transcript_manager_reset_l1l2 (transcript_manager, false, SPDM_MAX_SESSION_COUNT); /* Reset session transcript(s). */ for (session_idx = 0; session_idx < state->session_transcript_count; session_idx++) { spdm_transcript_manager_reset_session_transcript (transcript_manager, session_idx); } } } int spdm_transcript_manager_set_hash_algo ( const struct spdm_transcript_manager *transcript_manager, enum hash_type hash_algo) { int status = 0; if ((transcript_manager == NULL) || (hash_algo >= HASH_TYPE_INVALID)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } if (transcript_manager->state->hash_algo != HASH_TYPE_INVALID) { status = SPDM_TRANSCRIPT_MANAGER_HASH_ALGO_ALREADY_SET; goto exit; } transcript_manager->state->hash_algo = hash_algo; exit: return status; } void spdm_transcript_manager_set_spdm_version ( const struct spdm_transcript_manager *transcript_manager, uint8_t spdm_version) { if (transcript_manager != NULL) { transcript_manager->state->spdm_version = spdm_version; } } int spdm_transcript_manager_update ( const struct spdm_transcript_manager *transcript_manager, enum spdm_transcript_manager_context_type context_type, const uint8_t *message, size_t message_size, bool use_session_context, uint8_t session_idx) { int status; if ((transcript_manager == NULL) || (message == NULL) || (message_size == 0)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } switch (context_type) { case TRANSCRIPT_CONTEXT_TYPE_VCA: status = spdm_transcript_manager_update_vca (transcript_manager, message, message_size); break; case TRANSCRIPT_CONTEXT_TYPE_M1M2: status = spdm_transcript_manager_update_m1m2 (transcript_manager, message, message_size); break; case TRANSCRIPT_CONTEXT_TYPE_L1L2: case TRANSCRIPT_CONTEXT_TYPE_TH: if ((use_session_context == true) && (session_idx >= SPDM_MAX_SESSION_COUNT)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_SESSION_IDX; goto exit; } if (context_type == TRANSCRIPT_CONTEXT_TYPE_L1L2) { status = spdm_transcript_manager_update_l1l2 (transcript_manager, message, message_size, use_session_context, session_idx); } else { status = spdm_transcript_manager_update_th (transcript_manager, message, message_size, session_idx); } break; default: status = SPDM_TRANSCRIPT_MANAGER_UNSUPPORTED_CONTEXT_TYPE; break; } exit: return status; } int spdm_transcript_manager_get_hash ( const struct spdm_transcript_manager *transcript_manager, enum spdm_transcript_manager_context_type context_type, bool finish_hash, bool use_session_context, uint8_t session_idx, uint8_t *hash, size_t hash_size) { int status; struct spdm_transcript_manager_state *state; struct spdm_transcript_manager_hash_context *hash_context; const struct hash_engine *hash_engine; if ((transcript_manager == NULL) || (hash == NULL) || (hash_size == 0)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } /* M1/M2 hash is only valid for the global SPDM requester/responder. */ /* TH hash is only valid for an SPDM session. */ if (((use_session_context == true) && (context_type == TRANSCRIPT_CONTEXT_TYPE_M1M2)) || ((use_session_context == false) && (context_type == TRANSCRIPT_CONTEXT_TYPE_TH))) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } state = transcript_manager->state; if ((use_session_context == true) && (session_idx >= state->session_transcript_count)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_SESSION_IDX; goto exit; } switch (context_type) { case TRANSCRIPT_CONTEXT_TYPE_M1M2: hash_context = &state->m1m2; break; case TRANSCRIPT_CONTEXT_TYPE_L1L2: case TRANSCRIPT_CONTEXT_TYPE_TH: if (context_type == TRANSCRIPT_CONTEXT_TYPE_L1L2) { hash_context = (use_session_context == true) ? &state->session_transcript[session_idx].l1l2 : &state->l1l2; } else { hash_context = &state->session_transcript[session_idx].th; } break; default: status = SPDM_TRANSCRIPT_MANAGER_UNSUPPORTED_CONTEXT_TYPE; goto exit; } if (hash_context->hash_started == false) { status = SPDM_TRANSCRIPT_MANAGER_HASH_NOT_STARTED; goto exit; } hash_engine = transcript_manager->hash_engine[hash_context->hash_engine_idx]; status = finish_hash ? hash_engine->finish (hash_engine, hash, hash_size) : hash_engine->get_hash (hash_engine, hash, hash_size); if (status != 0) { goto exit; } hash_context->hash_started = !finish_hash; exit: return status; } /** * Initialize a Transcript manager for transcript hashing. * * @param transcript_manager Transcript manager to initialize. * @param hash_engine Array of hash engine instances. * @param hash_engine_count Number of hash engine instances provided. * * @return 0 if a transcript manager was instantiated successfully or an error code. */ int spdm_transcript_manager_init (struct spdm_transcript_manager *transcript_manager, struct spdm_transcript_manager_state *state, const struct hash_engine *const *hash_engine, uint8_t hash_engine_count) { int status = 0; if ((transcript_manager == NULL) || (state == NULL)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } memset (transcript_manager, 0, sizeof (struct spdm_transcript_manager)); /* Save the reference to the hash engine array. Validation of the hash engine array is done in * the spdm_transcript_manager_init_state function. */ transcript_manager->hash_engine = hash_engine; transcript_manager->hash_engine_count = hash_engine_count; /* Save the state. */ transcript_manager->state = state; /* Set the functions pointers. */ transcript_manager->set_hash_algo = spdm_transcript_manager_set_hash_algo; transcript_manager->set_spdm_version = spdm_transcript_manager_set_spdm_version; transcript_manager->update = spdm_transcript_manager_update; transcript_manager->get_hash = spdm_transcript_manager_get_hash; transcript_manager->reset_transcript = spdm_transcript_manager_reset_context; transcript_manager->reset = spdm_transcript_manager_reset; transcript_manager->reset_session_transcript = spdm_transcript_manager_reset_session_transcript; /* Initialize the state. */ status = spdm_transcript_manager_init_state (transcript_manager); exit: return status; } /** * Initialize the Transcript manager state. * * @param transcript_manager Transcript manager whose state is to be initialized. * * @return 0 if a transcipt manager state was initialize successfully or an error code. */ int spdm_transcript_manager_init_state (const struct spdm_transcript_manager *transcript_manager) { int status = 0; int max_session_count; struct spdm_transcript_manager_session_context *session_transcript; uint8_t hash_engine_idx; uint8_t session_idx; struct spdm_transcript_manager_state *state; if ((transcript_manager == NULL) || (transcript_manager->state == NULL) || (transcript_manager->hash_engine_count < SPDM_TRANSCRIPT_MANAGER_HASH_ENGINE_REQUIRED_COUNT) || (transcript_manager->hash_engine == NULL)) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } /* Check if the hash engine instances are valid. */ for (hash_engine_idx = 0; hash_engine_idx < transcript_manager->hash_engine_count; hash_engine_idx++) { if (transcript_manager->hash_engine[hash_engine_idx] == NULL) { status = SPDM_TRANSCRIPT_MANAGER_INVALID_ARGUMENT; goto exit; } } state = transcript_manager->state; memset (state, 0, sizeof (struct spdm_transcript_manager_state)); state->hash_algo = HASH_TYPE_INVALID; /* Process hash engines for global SPDM. */ state->m1m2.hash_engine_idx = SPDM_TRANSCRIPT_MANAGER_HASH_ENGINE_INDEX_M1M2; state->l1l2.hash_engine_idx = SPDM_TRANSCRIPT_MANAGER_HASH_ENGINE_INDEX_L1L2; /* Process hash engines for SPDM session(s). */ session_transcript = state->session_transcript; max_session_count = (transcript_manager->hash_engine_count - SPDM_TRANSCRIPT_MANAGER_HASH_ENGINE_REQUIRED_COUNT) / SPDM_TRANSCRIPT_MANAGER_SESSION_HASH_ENGINE_REQUIRED_COUNT; state->session_transcript_count = min (max_session_count, SPDM_MAX_SESSION_COUNT); /* Assig indices from the hash_engine array to session transcript hashes. */ for (session_idx = 0, hash_engine_idx = SPDM_TRANSCRIPT_MANAGER_HASH_ENGINE_REQUIRED_COUNT; session_idx < state->session_transcript_count; session_idx++) { session_transcript[session_idx].l1l2.hash_engine_idx = hash_engine_idx++; session_transcript[session_idx].th.hash_engine_idx = hash_engine_idx++; } exit: return status; } /** * Deinitialize the transcript manager. * * @param transcript_manager Transcript manager to deinitialize. */ void spdm_transcript_manager_release (const struct spdm_transcript_manager *transcript_manager) { if (transcript_manager != NULL) { spdm_transcript_manager_reset (transcript_manager); } }