/* esp32_sha.c * * Copyright (C) 2006-2026 wolfSSL Inc. * * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ /* * ESP32-C3: esp32-c3_technical_reference_manual_en.pdf * see page 335: no SHA-512 * */ #ifdef HAVE_CONFIG_H #include #endif /* Reminder: user_settings.h is needed and included from settings.h * Be sure to define WOLFSSL_USER_SETTINGS, typically in CMakeLists.txt */ #include #if defined(WOLFSSL_ESPIDF) /* Entire file is only for Espressif EDP-IDF */ #include "sdkconfig.h" /* programmatically generated from sdkconfig */ #include /*****************************************************************************/ /* this entire file content is excluded when NO_SHA, NO_SHA256 * or when using WC_SHA384 or WC_SHA512 */ #if !defined(NO_SHA) || !defined(NO_SHA256) || defined(WC_SHA384) || \ defined(WC_SHA512) /* this entire file content is excluded if not using HW hash acceleration */ #if defined(WOLFSSL_ESP32_CRYPT) && \ !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) #include #include #include #if ESP_IDF_VERSION_MAJOR >= 6 #include "sha/sha_core.h" #endif #elif defined(CONFIG_IDF_TARGET_ESP32) || \ defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) #include #else #include /* ESP32-WROOM */ #endif /* wolfSSL */ #include #include #include #include #include "wolfssl/wolfcrypt/port/Espressif/esp32-crypt.h" #include "wolfssl/wolfcrypt/error-crypt.h" #ifdef NO_INLINE #include #else #define WOLFSSL_MISC_INCLUDED #include #endif /* A value for an initialized, but not-yet-known SHA: */ #define WC_UNKNOWN_SHA (-1) #define WC_ESP_MAX_IDLE_WAIT 10000 static const char* TAG = "wolf_hw_sha"; #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* Keep track of the currently active SHA hash object for interleaving. */ const static word32 ** _active_digest_address = 0; #endif #ifdef NO_SHA #define WC_SHA_DIGEST_SIZE 20 #endif #if defined(DEBUG_WOLFSSL) /* Only when debugging, we'll keep tracking of SHA block numbers. */ static int this_block_num = 0; #endif /* RTOS mutex or just InUse variable */ #if defined(SINGLE_THREADED) static int InUse = 0; #else static wolfSSL_Mutex sha_mutex = NULL; #endif #ifdef WOLFSSL_DEBUG_MUTEX #ifdef WOLFSSL_TEST_STRAY #define WOLFSSL_TEST_STRAY_INJECT (esp_sha_call_count() == 10) #else /* unless turned on, we won't be testing for strays */ #define WOLFSSL_TEST_STRAY 0 #define WOLFSSL_TEST_STRAY_INJECT 0 #endif #endif /* usage metrics can be turned on independently of debugging */ #ifdef WOLFSSL_HW_METRICS static unsigned long esp_sha_hw_copy_ct = 0; static unsigned long esp_sha1_hw_usage_ct = 0; static unsigned long esp_sha1_sw_fallback_usage_ct = 0; static unsigned long esp_sha_reverse_words_ct = 0; static unsigned long esp_sha1_hw_hash_usage_ct = 0; static unsigned long esp_sha2_224_hw_hash_usage_ct = 0; static unsigned long esp_sha2_256_hw_hash_usage_ct = 0; static unsigned long esp_sha256_sw_fallback_usage_ct = 0; static unsigned long esp_byte_reversal_checks_ct = 0; static unsigned long esp_byte_reversal_needed_ct = 0; #endif static uintptr_t mutex_ctx_owner = NULLPTR; #if (defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED)) \ || defined(WOLFSSL_DEBUG_MUTEX) static portMUX_TYPE sha_crit_sect = portMUX_INITIALIZER_UNLOCKED; #endif #if defined(ESP_MONITOR_HW_TASK_LOCK) || !defined(SINGLE_THREADED) #ifdef SINGLE_THREADED uintptr_t esp_sha_mutex_ctx_owner(void) { return mutex_ctx_owner; } uintptr_t esp_sha_mutex_ctx_owner_set(uintptr_t new_mutex_ctx_owner) { mutex_ctx_owner = new_mutex_ctx_owner; return new_mutex_ctx_owner; } uintptr_t esp_sha_mutex_ctx_owner_clear(void) { return esp_sha_mutex_ctx_owner_set(NULLPTR); } #else #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) static TaskHandle_t mutex_ctx_task = NULL; #endif uintptr_t esp_sha_mutex_ctx_owner(void) { uintptr_t ret = 0; taskENTER_CRITICAL(&sha_crit_sect); { ret = mutex_ctx_owner; } taskEXIT_CRITICAL(&sha_crit_sect); return ret; }; uintptr_t esp_sha_mutex_ctx_owner_set(uintptr_t new_mutex_ctx_owner) { taskENTER_CRITICAL(&sha_crit_sect); { mutex_ctx_owner = new_mutex_ctx_owner; } taskEXIT_CRITICAL(&sha_crit_sect); return new_mutex_ctx_owner; }; uintptr_t esp_sha_mutex_ctx_owner_clear(void) { return esp_sha_mutex_ctx_owner_set(NULLPTR); } #endif /* ! SINGLE_THREADED */ #ifdef WOLFSSL_DEBUG_MUTEX WC_ESP32SHA* stray_ctx; /* each ctx keeps track of the initializer for HW. when debugging * we'll have a global variable to indicate which has the lock. */ static int _sha_lock_count = 0; static int _sha_call_count = 0; int esp_sha_call_count(void) { return _sha_call_count; } int esp_sha_lock_count(void) { return _sha_lock_count; } #endif #endif /* esp_set_hw - set hardware lock, but only if there's no other known * current mutex owner. */ int esp_set_hw(WC_ESP32SHA* ctx) { int ret = ESP_FAIL; if ((uintptr_t)ctx == mutex_ctx_owner || mutex_ctx_owner == NULLPTR) { ESP_LOGV(TAG, "Initializing current mutext owner!"); if (esp_sha_hw_islocked(ctx)) { ESP_LOGV(TAG, "esp_set_hw already locked: 0x%x", (intptr_t)ctx); } ctx->mode = ESP32_SHA_HW; #if defined(ESP_MONITOR_HW_TASK_LOCK) || !defined(SINGLE_THREADED) mutex_ctx_owner = esp_sha_mutex_ctx_owner_set((uintptr_t)ctx); #else mutex_ctx_owner = (uintptr_t)ctx; #endif ret = ESP_OK; } else { ESP_LOGV(TAG, "esp_sha_init_ctx HW for non-owner 0x%x", (intptr_t)ctx); } return ret; } /* ** The wolfCrypt functions for LITTLE_ENDIAN_ORDER typically ** reverse the byte order. Except when the hardware doesn't expect it. ** ** For SoC devices with no HW (Hardware Acceleration) support: ** ctx->sha_type will be SHA_INVALID ** ctx->mode will be ESP32_SHA_SW ** ** Returns 0 (FALSE) or 1 (TRUE); see wolfSSL types.h */ int esp_sha_need_byte_reversal(WC_ESP32SHA* ctx) { int ret = 1; /* Assume we'll need reversal, look for exceptions. */ CTX_STACK_CHECK(ctx); #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) if (ctx == NULL) { ESP_LOGE(TAG, " ctx is null"); /* Return true for bad params */ } else { #ifdef WOLFSSL_HW_METRICS { esp_byte_reversal_checks_ct++; } #endif if (ctx->mode == ESP32_SHA_HW) { ESP_LOGV(TAG, " No reversal, ESP32_SHA_HW"); ret = 0; } else { ret = 1; ESP_LOGV(TAG, " Need byte reversal, %d", ctx->mode); /* Return true for SW; only HW C3 skips reversal at this time. */ #ifdef WOLFSSL_HW_METRICS { esp_byte_reversal_needed_ct++; } #endif if (ctx->mode == ESP32_SHA_INIT) { ESP_LOGW(TAG, "esp_sha_need_byte_reversal during init?"); ESP_LOGW(TAG, "forgot to try HW lock first?"); } } } #else /* Other platforms always return true. */ #endif CTX_STACK_CHECK(ctx); return ret; } /* esp_sha_init ** ** ctx: any wolfSSL ctx from any hash algo ** hash_type: the specific wolfSSL enum for hash type ** ** Initializes ctx based on chipset capabilities and current state. ** Active HW states, such as from during a copy operation, are demoted to SW. ** For hash_type not available in HW, set SW mode. ** ** For ctx, mode will be ** ESP32_SHA_INIT - For initialized, hardware-ready ** ESP32_SHA_SW - Software only ** ** See esp_sha_init_ctx(ctx) for common initialization of ctx. */ int esp_sha_init(WC_ESP32SHA* ctx, enum wc_HashType hash_type) { int ret = ESP_OK; #ifdef DEBUG_WOLFSSL_SHA_MUTEX ESP_LOGV(TAG, "\n\nesp_sha_init for ctx %p\n\n", ctx); #endif if (ctx == NULL) { return ESP_FAIL; } #if defined(WOLFSSL_STACK_CHECK) ctx->first_word = 0; ctx->last_word = 0; #endif CTX_STACK_CHECK(ctx); ret = esp_sha_init_ctx(ctx); #if defined(CONFIG_IDF_TARGET_ESP32) || \ defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) /* ESP32 Xtensa Architecture SoC. Each has different features: */ switch (hash_type) { /* check each wolfSSL hash type WC_[n] */ #ifndef NO_SHA case WC_HASH_TYPE_SHA: ctx->sha_type = SHA1; /* assign Espressif SHA HW type */ break; #endif case WC_HASH_TYPE_SHA224: #if defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) ctx->sha_type = SHA2_224; /* assign Espressif SHA HW type */ #else /* Don't call init, always SW as there's no HW. */ ctx->mode = ESP32_SHA_SW; #endif break; case WC_HASH_TYPE_SHA256: ctx->sha_type = SHA2_256; /* assign Espressif SHA HW type */ break; #if defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) case WC_HASH_TYPE_SHA384: ctx->mode = ESP32_SHA_SW; break; #else case WC_HASH_TYPE_SHA384: ctx->sha_type = SHA2_384; /* assign Espressif SHA HW type */ break; #endif case WC_HASH_TYPE_SHA512: ctx->sha_type = SHA2_512; /* assign Espressif SHA HW type */ break; #ifndef WOLFSSL_NOSHA512_224 case WC_HASH_TYPE_SHA512_224: /* Don't call init, always SW as there's no HW. */ ctx->mode = ESP32_SHA_SW; break; #endif #ifndef WOLFSSL_NOSHA512_256 case WC_HASH_TYPE_SHA512_256: /* Don't call init, always SW as there's no HW. */ ctx->mode = ESP32_SHA_SW; break; #endif default: ctx->mode = ESP32_SHA_SW; ESP_LOGW(TAG, "Unexpected hash_type in esp_sha_init"); break; } #elif defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* ESP32 RISC-V Architecture SoC. Each has different features: */ switch (hash_type) { /* check each wolfSSL hash type WC_[n] */ #ifndef NO_SHA case WC_HASH_TYPE_SHA: ctx->sha_type = SHA1; /* assign Espressif SHA HW type */ break; #endif case WC_HASH_TYPE_SHA224: ctx->sha_type = SHA2_224; /* assign Espressif SHA HW type */ break; case WC_HASH_TYPE_SHA256: ctx->sha_type = SHA2_256; /* assign Espressif SHA HW type */ break; default: /* We fall through to SW when there's no enabled HW, above. */ ctx->mode = ESP32_SHA_SW; ESP_LOGW(TAG, "Unsupported hash_type = %d in esp_sha_init, " "falling back to SW", hash_type); break; } #else /* Other chipsets will be implemented here, fallback to SW for now: */ ESP_LOGW(TAG, "SW Fallback; CONFIG_IDF_TARGET = %s", CONFIG_IDF_TARGET); ctx->mode = ESP32_SHA_SW; #endif /* CONFIG_IDF_TARGET_[nnn] */ CTX_STACK_CHECK(ctx); return ret; } /* we'll call a common init for non-chip-specific settings */ int esp_sha_init_ctx(WC_ESP32SHA* ctx) { CTX_STACK_CHECK(ctx); ctx->mode = ESP32_SHA_INIT; /* This is a generic init; we don't yet know SHA type. */ ctx->sha_type = WC_UNKNOWN_SHA; /* Reminder: always start isfirstblock = 1 (true) when using HW engine. */ /* We're always on the first block at init time. (not zero-based!) */ ctx->isfirstblock = 1; ctx->lockDepth = 0; /* new objects will always start with lock depth = 0 */ #if defined(MUTEX_DURING_INIT) if ((uintptr_t)ctx == mutex_ctx_owner || mutex_ctx_owner == NULLPTR) { ESP_LOGV(TAG, "Initializing current mutext owner!"); if (esp_sha_hw_islocked(ctx)) { esp_sha_hw_unlock(ctx); } mutex_ctx_owner = esp_sha_mutex_ctx_owner_set((uintptr_t)ctx); } else { ESP_LOGI(TAG, "MUTEX_DURING_INIT esp_sha_init_ctx for non-owner: " "0x%x", (intptr_t)ctx); } #endif CTX_STACK_CHECK(ctx); return ESP_OK; /* Always return success. * We assume all issues handled, above. */ } /* esp_sha_init_ctx */ #ifndef NO_SHA /* ** internal SHA ctx copy for ESP HW */ int esp_sha_ctx_copy(struct wc_Sha* src, struct wc_Sha* dst) { int ret; if (src->ctx.mode == ESP32_SHA_HW) { /* this is an interesting situation to copy HW digest to SW */ ESP_LOGV(TAG, "esp_sha_ctx_copy esp_sha_digest_process"); #ifdef WOLFSSL_HW_METRICS { esp_sha_hw_copy_ct++; } #endif /* Get a copy of the HW digest, but don't process it. */ ret = esp_sha_digest_process(dst, 0); if (ret == 0) { /* initializer will be set during init */ ret = esp_sha_init(&(dst->ctx), WC_HASH_TYPE_SHA); if (ret != 0) { ESP_LOGE(TAG, "Error during esp_sha_ctx_copy " "in esp_sha_init."); } /* As src is HW, the copy will be SW. TODO: Future interleave. */ dst->ctx.mode = ESP32_SHA_SW; } else { ESP_LOGE(TAG, "Error during esp_sha_ctx_copy " "in esp_sha_digest_process."); } if (dst->ctx.mode == ESP32_SHA_SW) { #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* Reverse digest for C2/C3/C6 RISC-V platform * only when HW enabled but fallback to SW. */ ByteReverseWords(dst->digest, dst->digest, WC_SHA_DIGEST_SIZE); #ifdef WOLFSSL_HW_METRICS esp_sha_reverse_words_ct++; #endif #endif /* The normal revert to SW in copy is expected */ ESP_LOGV(TAG, "Confirmed SHA Copy set to SW"); } else { /* However NOT reverting to SW is not right. ** This should never happen. */ ESP_LOGW(TAG, "SHA Copy NOT set to SW from %d", dst->ctx.mode); } } /* (src->ctx.mode == ESP32_SHA_HW */ else { /* src not in HW mode, ok to copy. */ /* ** reminder XMEMCOPY, above: dst->ctx = src->ctx; ** No special HW init needed in SW mode. ** but we need to set our initializer breadcrumb: */ dst->ctx.initializer = (uintptr_t)&(dst->ctx); #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) { /* not HW mode for copy, so we are not interested in task owner */ dst->ctx.task_owner = 0; } #endif ret = 0; } return ret; } /* esp_sha_ctx_copy */ #endif /* ** Internal sha224 ctx copy (no ESP HW) */ #ifndef NO_WOLFSSL_ESP32_CRYPT_HASH_SHA224 int esp_sha224_ctx_copy(struct wc_Sha256* src, struct wc_Sha256* dst) { /* There's no 224 hardware on ESP32. * Initializer for dst is this ctx address for use as a breadcrumb. */ dst->ctx.initializer = (uintptr_t)&dst->ctx; #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) { /* Not HW mode for copy, so we are not interested in task owner: */ dst->ctx.task_owner = 0; } #endif dst->ctx.mode = ESP32_SHA_SW; return ESP_OK; } /* esp_sha224_ctx_copy */ #endif #ifndef NO_WOLFSSL_ESP32_CRYPT_HASH_SHA256 /* ** internal sha256 ctx copy for ESP HW */ int esp_sha256_ctx_copy(struct wc_Sha256* src, struct wc_Sha256* dst) { int ret; if (src->ctx.mode == ESP32_SHA_HW) { /* Get a copy of the HW digest, but don't process it. */ #ifdef WOLFSSL_DEBUG_MUTEX { ESP_LOGI(TAG, "esp_sha256_ctx_copy esp_sha512_digest_process"); } #endif ret = esp_sha256_digest_process(dst, FALSE); if (ret == ESP_OK) { /* initializer breadcrumb will be set during init */ ret = esp_sha_init(&(dst->ctx), WC_HASH_TYPE_SHA256); /* As src is HW, the copy will be SW. TODO: Future interleave. */ dst->ctx.mode = ESP32_SHA_SW; } else { ESP_LOGE(TAG, "Unexpected error during sha256 ctx copy: %d", ret); } if (dst->ctx.mode == ESP32_SHA_SW) { #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) { /* Reverse digest byte order for C3 fallback to SW. */ ByteReverseWords(dst->digest, dst->digest, WC_SHA256_DIGEST_SIZE); } #endif ESP_LOGV(TAG, "Confirmed wc_Sha256 Copy set to SW"); } else { ESP_LOGW(TAG, "wc_Sha256 Copy (mode = %d) set to SW", dst->ctx.mode); dst->ctx.mode = ESP32_SHA_SW; } } /* (src->ctx.mode == ESP32_SHA_HW) */ else { ret = ESP_OK; /* ** reminder this happened in XMEMCOPY: dst->ctx = src->ctx; ** No special HW init needed in SW mode. ** but we need to set our initializer (helpful in multi-task RTOS) */ dst->ctx.initializer = (uintptr_t)&(dst->ctx); #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) { /* not HW mode, so we are not interested in task owner */ dst->ctx.task_owner = 0; } #endif } /* not (src->ctx.mode == ESP32_SHA_HW) */ return ret; } /* esp_sha256_ctx_copy */ #endif #if !(defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA384) && \ defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) \ ) && \ (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) /* ** internal sha384 ctx copy for ESP HW */ int esp_sha384_ctx_copy(struct wc_Sha512* src, struct wc_Sha512* dst) { int ret = 0; #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) { /* We should ever be calling the HW sHA384 copy for this target. */ ESP_LOGW(TAG, "Warning: esp_sha384_ctx_copy() called for %s!", CONFIG_IDF_TARGET); ESP_LOGW(TAG, "There's no SHA384 HW for this CONFIG_IDF_TARGET"); } #else if (src->ctx.mode == ESP32_SHA_HW) { /* Get a copy of the HW digest, but don't process it. */ ESP_LOGV(TAG, "esp_sha384_ctx_copy esp_sha512_digest_process"); ret = esp_sha512_digest_process(dst, 0); if (ret == 0) { /* provide init hint to SW revert */ dst->ctx.mode = ESP32_SHA_HW_COPY; /* initializer will be set during init */ ret = esp_sha_init(&(dst->ctx), WC_HASH_TYPE_SHA384); if (ret != 0) { ESP_LOGE(TAG, "Error during esp_sha384_ctx_copy " "in esp_sha_init."); } } else { ESP_LOGE(TAG, "Error during esp_sha384_ctx_copy " "in esp_sha512_digest_process."); } /* just some diagnostic runtime info */ if (dst->ctx.mode == ESP32_SHA_SW) { ESP_LOGV(TAG, "Confirmed wc_Sha512 Copy set to SW"); } else { ESP_LOGW(TAG, "wc_Sha512 Copy NOT set to SW"); } } /* src->ctx.mode == ESP32_SHA_HW */ else { ret = 0; /* ** Reminder this happened in XMEMCOPY, above: dst->ctx = src->ctx; ** No special HW init needed in SW mode. ** But we need to set our initializer in dst as a breadcrumb: */ dst->ctx.initializer = (uintptr_t)&(dst->ctx); #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) { /* not HW mode for copy, so we are not interested in task owner */ dst->ctx.task_owner = 0; } #endif } /* not (src->ctx.mode == ESP32_SHA_HW) */ #endif return ret; } /* esp_sha384_ctx_copy */ #endif #if !(defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA384) && \ defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) \ ) && \ (defined(WOLFSSL_SHA384) || defined(WOLFSSL_SHA512)) /* ** Internal sha512 ctx copy for ESP HW. ** If HW already active, fall back to SW for this ctx. */ int esp_sha512_ctx_copy(struct wc_Sha512* src, struct wc_Sha512* dst) { int ret = ESP_OK; /* Assume success (zero) */ #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* There's no SHA512 HW on these RISC-V SoC so there's nothing to do. * (perhaps a future one will?) */ #elif defined(CONFIG_IDF_TARGET_ESP32) || \ defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) if (src->ctx.mode == ESP32_SHA_HW) { /* Get a copy of the HW digest, but don't process it. */ ESP_LOGV(TAG, "esp_sha512_ctx_copy esp_sha512_digest_process"); ret = esp_sha512_digest_process(dst, FALSE); if (ret == ESP_OK) { /* provide init hint to SW revert */ dst->ctx.mode = ESP32_SHA_HW_COPY; /* initializer will be set during init ** reminder we should never arrive here for ** ESP32 SHA512/224 or SHA512/224, as there's no HW */ ret = esp_sha_init(&(dst->ctx), WC_HASH_TYPE_SHA512); } if (dst->ctx.mode == ESP32_SHA_SW) { ESP_LOGV(TAG, "Confirmed wc_Sha512 Copy set to SW"); } else { ESP_LOGW(TAG, "wc_Sha512 Copy set to SW"); dst->ctx.mode = ESP32_SHA_SW; } } /* src->ctx.mode == ESP32_SHA_HW */ else { ret = ESP_OK; /* reminder this happened in XMEMCOPY, above: dst->ctx = src->ctx; ** No special HW init needed when not in active HW mode. ** but we need to set our initializer breadcrumb: */ #if !defined(CONFIG_IDF_TARGET_ESP32C2) && \ !defined(CONFIG_IDF_TARGET_ESP32C3) && \ !defined(CONFIG_IDF_TARGET_ESP32C6) dst->ctx.initializer = (uintptr_t)&(dst->ctx); #endif #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) { /* not HW mode for copy, so we are not interested in task owner */ dst->ctx.task_owner = 0; } #endif } /* else src->ctx.mode != ESP32_SHA_HW */ #endif return ret; } /* esp_sha512_ctx_copy */ #endif /* ** Determine the digest size, depending on SHA type. ** ** See FIPS PUB 180-4, Instruction Section 1. ** ** See ESP32 sha.h for values: ** ** enum SHA_TYPE { ** SHA1 = 0, ** SHA2_256, ** SHA2_384, ** SHA2_512, ** SHA_TYPE_MAX = -1, ** }; ** ** given the SHA_TYPE (see Espressif sha.h) return WC digest size. ** ** Returns zero for bad digest size type request. ** */ static word32 wc_esp_sha_digest_size(WC_ESP_SHA_TYPE type) { int ret = 0; ESP_LOGV(TAG, " esp_sha_digest_size"); #if CONFIG_IDF_TARGET_ARCH_RISCV /* * SHA1 = 0, * SHA2_224, * SHA2_256, */ switch (type) { #ifndef NO_SHA case SHA1: /* typically 20 bytes */ ret = WC_SHA_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA224 case SHA2_224: ret = WC_SHA224_DIGEST_SIZE; break; #endif #ifndef NO_SHA256 case SHA2_256: /* typically 32 bytes */ ret = WC_SHA256_DIGEST_SIZE; break; #endif default: ESP_LOGE(TAG, "Bad SHA type in wc_esp_sha_digest_size"); ret = 0; break; } #else /* Xtensa */ switch (type) { #ifndef NO_SHA case SHA1: /* typically 20 bytes */ ret = WC_SHA_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA224 #if defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) case SHA2_224: ret = WC_SHA224_DIGEST_SIZE; break; #endif #endif #ifndef NO_SHA256 case SHA2_256: /* typically 32 bytes */ ret = WC_SHA256_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA384 case SHA2_384: ret = WC_SHA384_DIGEST_SIZE; break; #endif #ifdef WOLFSSL_SHA512 case SHA2_512: /* typically 64 bytes */ ret = WC_SHA512_DIGEST_SIZE; break; #endif default: ESP_LOGE(TAG, "Bad SHA type in wc_esp_sha_digest_size"); ret = 0; break; } #endif return ret; /* Return value is a size, not an error code. */ } /* wc_esp_sha_digest_size */ /* ** wait until all engines becomes idle */ static int wc_esp_wait_until_idle(void) { int ret = 0; /* assume success */ int loop_ct = WC_ESP_MAX_IDLE_WAIT; #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* ESP32-C3 and ESP32-C6 RISC-V */ while ((sha_ll_busy() == 1) && (loop_ct > 0)) { loop_ct--; /* do nothing while waiting. */ } #elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) while (REG_READ(SHA_BUSY_REG)) { /* do nothing while waiting. */ } #else while ((DPORT_REG_READ(SHA_1_BUSY_REG) != 0) || (DPORT_REG_READ(SHA_256_BUSY_REG) != 0) || (DPORT_REG_READ(SHA_384_BUSY_REG) != 0) || (DPORT_REG_READ(SHA_512_BUSY_REG) != 0)) { /* do nothing while waiting. */ } #endif if (loop_ct <= 0) { ESP_LOGW(TAG, "Too long to exit wc_esp_wait_until_idle"); } return ret; } /* wc_esp_wait_until_idle */ /* ** hack alert. there really should have been something implemented ** in Espressif periph_ctrl.c to detect ref_counts[periph] depth. ** ** since there is not at this time, we have this brute-force method. ** ** when trying to unwrap an arbitrary depth of peripheral-enable(s), ** we'll check the register upon *enable* to see if we actually did. ** ** Note that enable / disable only occurs when ref_counts[periph] == 0 ** */ int esp_unroll_sha_module_enable(WC_ESP32SHA* ctx) { /* if we end up here, there was a prior unexpected fail and * we need to unroll enables */ int ret = 0; /* assume success unless proven otherwise */ int actual_unroll_count = 0; int max_unroll_count = 1000; /* never get stuck in a hardware wait loop */ #if defined(CONFIG_IDF_TARGET_ESP32) word32 this_sha_mask; /* this is the bit-mask for our SHA CLK_EN_REG */ #endif CTX_STACK_CHECK(ctx); if (ctx == NULL) { ESP_LOGE(TAG, "esp_unroll_sha_module_enable called with null ctx."); return BAD_FUNC_ARG; } #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /************* RISC-V Architecture *************/ (void)max_unroll_count; (void)_active_digest_address; ets_sha_disable(); /* We don't check for unroll as done below, for Xtensa*/ #else /************* Xtensa Architecture *************/ /* unwind prior calls to THIS ctx. decrement ref_counts[periph] ** only when ref_counts[periph] == 0 does something actually happen. */ /* once the value we read is a 0 in the DPORT_PERI_CLK_EN_REG bit * then we have fully unrolled the enables via ref_counts[periph]==0 */ #if defined(CONFIG_IDF_TARGET_ESP32S2) ||defined(CONFIG_IDF_TARGET_ESP32S3) /* once the value we read is a 0 in the DPORT_PERI_CLK_EN_REG bit * then we have fully unrolled the enables via ref_counts[periph]==0 */ while (periph_ll_periph_enabled(PERIPH_SHA_MODULE)) { #else /* this is the bit-mask for our SHA CLK_EN_REG */ this_sha_mask = periph_ll_get_clk_en_mask(PERIPH_SHA_MODULE); asm volatile("memw"); while ((this_sha_mask & *(uint32_t*)DPORT_PERI_CLK_EN_REG) != 0) { #endif /* CONFIG_IDF_TARGET_ESP32S3 */ periph_module_disable(PERIPH_SHA_MODULE); asm volatile("memw"); actual_unroll_count++; ESP_LOGW(TAG, "unroll not yet successful. try #%d", actual_unroll_count); /* we'll only try this some unreasonable number of times * before giving up */ if (actual_unroll_count > max_unroll_count) { ret = ESP_FAIL; /* failed to unroll */ break; } } #endif /* else; not RISC-V */ if (ret == 0) { if (ctx->lockDepth != actual_unroll_count) { /* this could be a warning of wonkiness in RTOS environment. ** we were successful, but not expected depth count. ** ** This should never happen unless someone else called ** periph_module_disable() or threading not working properly. **/ ESP_LOGW(TAG, "warning lockDepth mismatch: %d", ctx->lockDepth); if (actual_unroll_count == 0 && ctx->lockDepth > 2) { ESP_LOGW(TAG, "Large lockDepth discrepancy often indicates " "stack overflow or memory corruption"); } } ctx->lockDepth = 0; ctx->mode = ESP32_SHA_INIT; } else { /* This should never occur. Something must have gone seriously ** wrong. Check for non-wolfSSL outside calls that may have enabled HW. */ ESP_LOGE(TAG, "Failed to unroll after %d attempts.", actual_unroll_count); ESP_LOGI(TAG, "Setting ctx->mode = ESP32_SHA_SW"); ctx->mode = ESP32_SHA_SW; } CTX_STACK_CHECK(ctx); return ret; } /* esp_unroll_sha_module_enable */ /* Set and return a stray ctx value stray_ctx. Useful for multi-task debugging. * Returns zero if not debugging. */ uintptr_t esp_sha_set_stray(WC_ESP32SHA* ctx) { uintptr_t ret = 0; CTX_STACK_CHECK(ctx); #ifdef WOLFSSL_DEBUG_MUTEX stray_ctx = ctx; ret = (uintptr_t)stray_ctx; #endif CTX_STACK_CHECK(ctx); return ret; } /* Return 1 if the SHA HW is in use, 0 otherwise. */ int esp_sha_hw_in_use() { int ret; #ifdef SINGLE_THREADED ret = InUse; #else ret = (mutex_ctx_owner != NULLPTR); ESP_LOGV(TAG, "mutex_ctx_owner is 0x%x", mutex_ctx_owner); #endif ESP_LOGV(TAG, "esp_sha_hw_in_use is %d", ret); return ret; } /* ** return HW lock owner, otherwise zero if not locked. ** ** When WOLFSSL_DEBUG_MUTEX is defined, additional ** debugging capabilities are available. */ uintptr_t esp_sha_hw_islocked(WC_ESP32SHA* ctx) { uintptr_t ret = 0; #if !defined(WOLFSSL_DEBUG_MUTEX) && !defined(SINGLE_THREADED) TaskHandle_t mutexHolder; #endif CTX_STACK_CHECK(ctx); #ifdef WOLFSSL_DEBUG_MUTEX ret = esp_sha_mutex_ctx_owner(); if (ctx == 0) { ESP_LOGV(TAG, "ctx == 0; Not checking if a given ctx has the lock"); } else { if (ret == (uintptr_t)ctx->initializer) { ESP_LOGV(TAG, "confirmed this object is the owner"); } else { ESP_LOGV(TAG, "this object is not the lock owner"); } } /* ctx != 0 */ #else #ifdef SINGLE_THREADED { ret = InUse; } #else { if (sha_mutex == NULL) { mutexHolder = NULL; } else { mutexHolder = xSemaphoreGetMutexHolder(sha_mutex); } if (mutexHolder == NULL) { /* Mutex is not in use */ ESP_LOGV(TAG, "multi-threaded esp_mp_hw_islocked = false"); ret = 0; } else { ESP_LOGV(TAG, "multi-threaded esp_mp_hw_islocked = true"); ret = mutex_ctx_owner; } /* Verbose debug diagnostics */ if (NULLPTR == mutex_ctx_owner) { ESP_LOGV(TAG, "not esp_sha_hw_islocked, mutex_ctx_owner is Null"); } else { ESP_LOGV(TAG, "esp_sha_hw_islocked for 0x%x", mutex_ctx_owner); } } #endif return ret; #endif #ifdef WOLFSSL_DEBUG_MUTEX if (ret == 0) { ESP_LOGV(TAG, ">> NOT LOCKED esp_sha_hw_islocked"); } else { ESP_LOGV(TAG, ">> LOCKED esp_sha_hw_islocked for %x", (int)esp_sha_mutex_ctx_owner()); } #endif CTX_STACK_CHECK(ctx); return ret; } /* * The HW is typically unlocked when the SHA hash wc_Sha[nn]Final() is called. * However, in the case of TLS connections the in-progress hash may at times be * abandoned. Thus this function should be called at free time. See internal.c * * Returns the owner of the current lock, typically used for debugging. * Returns zero if there was no unfinished lock found to clean up. */ uintptr_t esp_sha_release_unfinished_lock(WC_ESP32SHA* ctx) { uintptr_t ret = 0; CTX_STACK_CHECK(ctx); ret = esp_sha_hw_islocked(ctx); /* get the owner of the current lock */ if (ret == 0) { #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGV(TAG, "No unfinished lock to clean up for ctx %p.", ctx); #endif } else { #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "Unfinished lock clean up: %p.", ctx); #endif if (ret == (uintptr_t)ctx) { /* found a match for this object */ if (ret == ctx->initializer) { /* confirmed match*/ ESP_LOGW(TAG, "New mutex_ctx_owner = NULL"); #ifdef ESP_MONITOR_HW_TASK_LOCK { esp_sha_mutex_ctx_owner_clear(); } #endif } else { #if defined(WOLFSSL_DEBUG_MUTEX) || defined(WOLFSSL_ESP32_HW_LOCK_DEBUG) if (ctx->initializer == 0) { /* A zero likely indicates prior cleanup for abandoned hash. * Check the calling code to confirm this is the case. */ ESP_LOGW(TAG, "Release already finished lock for %x ?", ctx->initializer); } else { /* Mismatch expected may be in a multi-thread RTOS. */ ESP_LOGW(TAG, "ERROR: Release unfinished lock for %x but " "found %x", ret, ctx->initializer); } #endif } /* ret != ctx->initializer */ #ifdef WOLFSSL_DEBUG_MUTEX ESP_LOGW(TAG, "\n>>>> esp_sha_release_unfinished_lock %x\n", ret); #endif /* unlock only if this ctx is the initializer of the lock */ #ifdef SINGLE_THREADED { ret = esp_sha_hw_unlock(ctx); } #else #if defined(ESP_MONITOR_HW_TASK_LOCK) { if (ctx->task_owner == xTaskGetCurrentTaskHandle()) { ESP_LOGV(TAG, "esp_sha_hw_unlock!"); } else { /* We cannot free a SHA object lock from a different task. * So give the ctx a hint for other task to clean it up. */ ctx->mode = ESP32_SHA_FREED; ESP_LOGV(TAG, "ESP32_SHA_FREED"); } } #else /* Here we assume only 1 task, so no ESP32_SHA_FREED hint. */ ret = esp_sha_hw_unlock(ctx); #endif /* ESP_MONITOR_HW_TASK_LOCK */ #endif /* SINGLE_THREADED or not */ } /* ret == ctx */ } /* else not locked */ CTX_STACK_CHECK(ctx); if (ctx->mode != ESP32_SHA_INIT) { #if defined(WOLFSSL_ESP32_HW_LOCK_DEBUG) ESP_LOGW(TAG, "esp_sha_release_unfinished_lock mode = %d", ctx->mode); #endif if (ctx->mode == ESP32_SHA_HW) { #if defined(DEBUG_WOLFSSL_ESP32_UNFINISHED_HW) ESP_LOGW(TAG, "esp_sha_release_unfinished_lock HW!"); #endif } } return ret; } /* esp_sha_release_unfinished_lock */ /* ** lock HW engine. ** this should be called before using engine. */ int esp_sha_try_hw_lock(WC_ESP32SHA* ctx) { int ret = 0; #if defined(SINGLE_THREADED) /* no mutex monitoring available in single thread mode */ #else /* thread safe get of global static mutex_ctx_owner: */ uintptr_t this_mutex_owner; /* mutex_ctx_owner could change in multiple threads, assign once here: */ this_mutex_owner = esp_sha_mutex_ctx_owner(); #endif CTX_STACK_CHECK(ctx); #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "enter esp_sha_hw_lock for %x, initializer %x", (uintptr_t)ctx, (uintptr_t)ctx->initializer); #endif #ifdef WOLFSSL_DEBUG_MUTEX taskENTER_CRITICAL(&sha_crit_sect); { /* let's keep track of how many times we call this */ _sha_call_count++; } taskEXIT_CRITICAL(&sha_crit_sect); #endif if (ctx == NULL) { ESP_LOGE(TAG, " esp_sha_try_hw_lock called with NULL ctx"); return BAD_FUNC_ARG; } /* Init mutex * * Note that even single thread mode may calculate separate hashes * concurrently, so we still need to keep track of the engine being * busy or not. */ #if defined(SINGLE_THREADED) if (ctx->mode == ESP32_SHA_INIT) { if (InUse) { /* Revert to SW when HW is busy */ ctx->mode = ESP32_SHA_SW; } else { /* Set single-threaded hardware mode. */ ctx->mode = ESP32_SHA_HW; InUse = 1; #ifdef WOLFSSL_DEBUG_MUTEX ESP_LOGW(TAG, "\n\nHW in use\n\n"); #endif } ret = ESP_OK; } else { /* this should not happen */ ESP_LOGE(TAG, "unexpected error in esp_sha_try_hw_lock."); return ESP_FAIL; } #else /* not SINGLE_THREADED */ /* ** there's only one SHA engine for all the hash types ** so when any hash is in use, no others can use it. ** fall back to SW. ** ** here is some sample code to test the unrolling of SHA enables: ** periph_module_enable(PERIPH_SHA_MODULE); ctx->lockDepth++; periph_module_enable(PERIPH_SHA_MODULE); ctx->lockDepth++; ctx->mode = ESP32_FAIL_NEED_INIT; ** */ if (sha_mutex == NULL) { ESP_LOGV(TAG, "Initializing sha_mutex"); /* created, but not yet locked */ ret = esp_CryptHwMutexInit(&sha_mutex); if (ret == 0) { ESP_LOGV(TAG, "esp_CryptHwMutexInit sha_mutex init success."); esp_sha_mutex_ctx_owner_clear(); /* No one has the mutex yet. */ #ifdef WOLFSSL_DEBUG_MUTEX { /* Take mutex for lock/unlock test drive to ensure it works: */ ret = esp_CryptHwMutexLock(&sha_mutex, (TickType_t)0); if (ret == ESP_OK) { ret = esp_CryptHwMutexUnLock(&sha_mutex); if (ret != ESP_OK) { ESP_LOGE(TAG, "esp_CryptHwMutexInit fail init lock."); } } else { ESP_LOGE(TAG, "esp_CryptHwMutexInit fail init unlock."); } } #endif } /* ret == 0 for esp_CryptHwMutexInit */ else { ESP_LOGE(TAG, "esp_CryptHwMutexInit sha_mutex failed."); #ifdef WOLFSSL_DEBUG_MUTEX { ESP_LOGV(TAG, "Current mutext owner = %x", this_mutex_owner); } #endif sha_mutex = NULL; ESP_LOGV(TAG, "Revert to ctx->mode = ESP32_SHA_SW."); ctx->mode = ESP32_SHA_SW; return ESP_OK; /* success, just not using HW */ } } #if defined(ESP_MONITOR_HW_TASK_LOCK) || \ (defined(WOLFSSL_DEBUG_MUTEX) && WOLFSSL_DEBUG_MUTEX) /* Nothing happening here other than messages based on mutex states */ if (mutex_ctx_task == 0 || mutex_ctx_owner == 0) { /* no known stray mutex task owner */ } else { if (mutex_ctx_task == xTaskGetCurrentTaskHandle()) { ESP_LOGV(TAG, "Found mutex_ctx_task"); if (((WC_ESP32SHA*)mutex_ctx_owner)->mode == ESP32_SHA_FREED) { ESP_LOGW(TAG, "ESP32_SHA_FREED unlocking mutex_ctx_task = %x" " for mutex_ctx_owner = %x", (uintptr_t)mutex_ctx_task, (uintptr_t)this_mutex_owner); } else { if (ctx->mode == ESP32_SHA_FREED) { ESP_LOGW(TAG, "ESP32_SHA_FREED unlocking (disabled) " "ctx = %x for ctx.initializer = %x", (uintptr_t)ctx, (uintptr_t)ctx->initializer); } else { /* Not very interesting during init. */ if (ctx->mode == ESP32_SHA_INIT) { ESP_LOGV(TAG, "mutex_ctx_owner = 0x%x", this_mutex_owner); ESP_LOGV(TAG, "This ctx = 0x%x is ESP32_SHA_INIT", (uintptr_t)ctx); } else { ESP_LOGW(TAG, "Not Freed!"); } } /* ctx ESP32_SHA_FREED check */ } /* mutex owner ESP32_SHA_FREED check */ } /* mutex_ctx_task is current task */ else { #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGW(TAG, "Warning: sha mutex unlock from unexpected task."); ESP_LOGW(TAG, "Locking task: 0x%x", (word32)mutex_ctx_task); ESP_LOGW(TAG, "This xTaskGetCurrentTaskHandle: 0x%x", (word32)xTaskGetCurrentTaskHandle()); #endif } } #endif /* ESP_MONITOR_HW_TASK_LOCK || WOLFSSL_DEBUG_MUTEX */ /* check if this SHA has been operated as SW or HW, or not yet init */ if (ctx->mode == ESP32_SHA_INIT) { /* try to lock the HW engine */ #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "ESP32_SHA_INIT for %x, initializer %x\n", (uintptr_t)ctx, (uintptr_t)ctx->initializer); #endif ESP_LOGV(TAG, "Init; release unfinished ESP32_SHA_INIT lock " "for ctx 0x%x", (uintptr_t)ctx); esp_sha_release_unfinished_lock(ctx); /* lock hardware; there should be exactly one instance * of esp_CryptHwMutexLock(&sha_mutex ...) in code. * * we don't wait: * either the engine is free, or we fall back to SW. * * TODO: allow for SHA interleave on chips that support it. */ if ((mutex_ctx_owner == NULLPTR) && esp_CryptHwMutexLock(&sha_mutex, (TickType_t)0) == ESP_OK) { /* we've successfully locked */ this_mutex_owner = (uintptr_t)ctx; esp_sha_mutex_ctx_owner_set(this_mutex_owner); ESP_LOGV(TAG, "Assigned mutex_ctx_owner to 0x%x", this_mutex_owner); #ifdef ESP_MONITOR_HW_TASK_LOCK mutex_ctx_task = xTaskGetCurrentTaskHandle(); #endif #ifdef WOLFSSL_DEBUG_MUTEX if (WOLFSSL_TEST_STRAY_INJECT) { ESP_LOGW(TAG, "Introducing SHA stray for testing"); /* Once we've locked [n] times here, * we'll force a fallback to SW until other thread unlocks. */ taskENTER_CRITICAL(&sha_crit_sect); { (void)stray_ctx; if (stray_ctx == NULL) { /* no peek task */ } else { stray_ctx->initializer = (intptr_t)stray_ctx; mutex_ctx_owner = (intptr_t)stray_ctx->initializer; this_mutex_owner = mutex_ctx_owner; } } taskEXIT_CRITICAL(&sha_crit_sect); if (stray_ctx == NULL) { ESP_LOGW(TAG, "WOLFSSL_DEBUG_MUTEX on, but stray_ctx " "is NULL; are you running the peek task to " "set the stay test?"); } else { ESP_LOGI(TAG, "%x", (uintptr_t)stray_ctx->initializer); ESP_LOGI(TAG, "%x", (uintptr_t)&stray_ctx); ESP_LOGW(TAG, "\n\nLocking with stray\n\n" "WOLFSSL_DEBUG_MUTEX call count 8, " "ctx->mode = ESP32_SHA_SW %x\n\n", this_mutex_owner); #if defined(ESP_MONITOR_HW_TASK_LOCK) && !defined(SINGLE_THREADED) /* ctx->task_owner is only available for multi-threaded */ ctx->task_owner = xTaskGetCurrentTaskHandle(); #endif ctx->mode = ESP32_SHA_SW; return ESP_OK; /* success, but revert to SW */ } } #endif /* check to see if we had a prior fail and need to unroll enables */ #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGW(TAG, "Locking for ctx %x, current mutex_ctx_owner = %x", (uintptr_t)&ctx, this_mutex_owner); ESP_LOGI(TAG, "ctx->lockDepth = %d", ctx->lockDepth); #endif if (ctx->mode == ESP32_SHA_INIT) { /* Set non-single-threaded hardware mode */ esp_set_hw(ctx); } #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "Hardware Mode Active, lock depth = %d, for %x", ctx->lockDepth, (uintptr_t)ctx->initializer); #endif #ifdef WOLFSSL_DEBUG_MUTEX taskENTER_CRITICAL(&sha_crit_sect); { mutex_ctx_owner = (uintptr_t)ctx->initializer; /* let's keep track of how many times we lock this */ _sha_lock_count++; } taskEXIT_CRITICAL(&sha_crit_sect); #endif if (ctx->lockDepth > 0) { /* it is unlikely that this would ever occur, ** as the mutex should be gate keeping */ ESP_LOGW(TAG, "WARNING: Hardware Mode " "interesting lock depth = %d, for this %x", ctx->lockDepth, (uintptr_t)ctx->initializer); } } else { /* When the lock is already in use: is it for this ctx? */ if ((uintptr_t)ctx == this_mutex_owner) { ESP_LOGV(TAG, "I'm the owner! 0x%x", (uintptr_t)ctx); ctx->mode = ESP32_SHA_SW; } else { #ifdef WOLFSSL_DEBUG_MUTEX ESP_LOGW(TAG, "\nHardware in use by %x; " "Mode REVERT to ESP32_SHA_SW for %x\n", this_mutex_owner, (uintptr_t)ctx->initializer); ESP_LOGI(TAG, "Software Mode, lock depth = %d, for this %x", ctx->lockDepth, (uintptr_t)ctx->initializer); ESP_LOGI(TAG, "Current mutext owner = %x", this_mutex_owner); #endif ESP_LOGV(TAG, "I'm not owner! 0x%x; owner = 0x%x", (uintptr_t)ctx, mutex_ctx_owner); if (this_mutex_owner) { #ifdef WOLFSSL_DEBUG_MUTEX ESP_LOGW(TAG, "revert to SW since mutex_ctx_owner = %x" " but we are currently ctx = %x", this_mutex_owner, (intptr_t)ctx); #endif } else { /* No ctx mutex owner, so hardware must be free. */ } ESP_LOGV(TAG, "Set update ctx->mode = SW (from %d) for 0x%x", ctx->mode, (uintptr_t)ctx ); ctx->mode = ESP32_SHA_SW; } return ESP_OK; /* success, but revert to SW */ } } /* (ctx->mode == ESP32_SHA_INIT) */ else { /* this should not happen: called during mode != ESP32_SHA_INIT */ ESP_LOGE(TAG, "unexpected error in esp_sha_try_hw_lock."); return ESP_FAIL; } #endif /* not defined(SINGLE_THREADED) */ ESP_LOGV(TAG, "ctx->mode = %d", ctx->mode); if ((ret == ESP_OK) && (ctx->mode == ESP32_SHA_HW)) { ctx->lockDepth++; /* depth for THIS ctx (there could be others!) */ #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG { ESP_LOGI(TAG, "1) Lock depth @ %d = %d for WC_ESP32SHA @ %0x\n", __LINE__, ctx->lockDepth, (unsigned)ctx); } #endif #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) { ESP_LOGV(TAG, "ets_sha_enable for RISC-V"); ets_sha_enable(); } #else ESP_LOGV(TAG, "ets_sha_enable for Xtensa"); periph_module_enable(PERIPH_SHA_MODULE); #endif } else { /* Set to SW */ #ifdef WOLFSSL_ESP32_CRYPT_DEBUG if (ret == ESP_OK) { ESP_LOGW(TAG, "Normal SHA Software fallback mode."); } else { ESP_LOGW(TAG, "Warning: Unexpected Mode REVERT to ESP32_SHA_SW" ", err = %d", ret); } #endif ctx->mode = ESP32_SHA_SW; } ESP_LOGV(TAG, "leave esp_sha_hw_lock"); CTX_STACK_CHECK(ctx); return ret; } /* esp_sha_try_hw_lock */ /* ** Release HW engine. when we don't have it locked, SHA module is DISABLED. ** Note this is not the semaphore tracking who has the HW. */ int esp_sha_hw_unlock(WC_ESP32SHA* ctx) { int ret = ESP_OK; /* assume success (zero) */ CTX_STACK_CHECK(ctx); #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGV(TAG, "enter esp_sha_hw_unlock"); #endif /* we'll keep track of our lock depth. * in case of unexpected results, all the periph_module_disable() calls * and periph_module_disable() need to be unwound. * * see ref_counts[periph] in file: periph_ctrl.c */ #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "2) esp_sha_hw_unlock Lock depth @ %d = %d " "for WC_ESP32SHA ctx @ %p\n", __LINE__, ctx->lockDepth, ctx); #endif if (ctx->lockDepth > 0) { #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) ets_sha_disable(); /* disable also resets active, ongoing hash */ ESP_LOGV(TAG, "ets_sha_disable in esp_sha_hw_unlock()"); #else periph_module_disable(PERIPH_SHA_MODULE); #endif ctx->lockDepth--; } else { ESP_LOGW(TAG, "lockDepth <= 0; Disable SHA module skipped for %x", (uintptr_t)ctx->initializer); ctx->lockDepth = 0; } #if defined(ESP_MONITOR_HW_TASK_LOCK) && defined(WOLFSSL_ESP32_HW_LOCK_DEBUG) ESP_LOGI(TAG, "3) esp_sha_hw_unlock Lock depth @ %d = %d " "for WC_ESP32SHA @ %0x\n", __LINE__, ctx->lockDepth, (uintptr_t)ctx); #endif if (0 != ctx->lockDepth) { /* If the lockdepth is not zero, unlock success unknown. */ ESP_LOGE(TAG, "ERROR Non-zero lockDepth. Stray code lock?"); ret = ESP_FAIL; } else { #if defined(SINGLE_THREADED) #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG { ESP_LOGW(TAG, "HW released, not in use."); } #endif InUse = 0; #else /* Hardware was unlocked above, now update semaphores. */ #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG { ESP_LOGW(TAG, "Unlocking for mutex_ctx_owner %x, from ctx 0x%x", esp_sha_mutex_ctx_owner(), (uintptr_t)ctx); ESP_LOGV(TAG, "&sha_mutex = %x", (intptr_t)&sha_mutex); } #endif /* WOLFSSL_ESP32_HW_LOCK_DEBUG */ /* There should be exactly 1 instance of SHA unlock, and it's here: */ esp_CryptHwMutexUnLock(&sha_mutex); /* We don't set owner to zero here. The HW is not in use, * but there may be a WIP hash calc (e.g. sha update). * NO: mutex_ctx_owner = NULLPTR; */ #ifdef ESP_MONITOR_HW_TASK_LOCK mutex_ctx_task = 0; #endif #endif #ifdef WOLFSSL_DEBUG_MUTEX taskENTER_CRITICAL(&sha_crit_sect); { mutex_ctx_owner = 0; } taskEXIT_CRITICAL(&sha_crit_sect); #endif } #ifdef WOLFSSL_ESP32_HW_LOCK_DEBUG ESP_LOGI(TAG, "leave esp_sha_hw_unlock, %x", (uintptr_t)ctx->initializer); #endif CTX_STACK_CHECK(ctx); return ret; } /* esp_sha_hw_unlock */ /* * Start SHA process by using HW engine. * Assumes register already loaded. * Returns a negative value error code upon failure. */ #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* ESP32-C3 HAL has built-in process start, nothing to declare here. */ #else /* Everything else uses esp_sha_start_process() */ static int esp_sha_start_process(WC_ESP32SHA* sha) { int ret = ESP_OK; #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) uint8_t HardwareAlgorithm; #endif if (sha == NULL) { return BAD_FUNC_ARG; } CTX_STACK_CHECK(sha); ESP_LOGV(TAG, " enter esp_sha_start_process"); #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) ESP_LOGV(TAG, "SHA1 SHA_START_REG"); if (sha->isfirstblock) { sha_ll_start_block(SHA2_256); sha->isfirstblock = 0; ESP_LOGV(TAG, " set sha->isfirstblock = 0"); #if defined(DEBUG_WOLFSSL) this_block_num = 1; /* one-based counter, just for debug info */ #endif } /* first block */ else { sha_ll_continue_block(SHA2_256); #if defined(DEBUG_WOLFSSL) this_block_num++; /* one-based counter */ ESP_LOGV(TAG, " continue block #%d", this_block_num); #endif } /* not first block */ /***** END CONFIG_IDF_TARGET_ESP32C2 aka ESP8684 or * CONFIG_IDF_TARGET_ESP32C3 or * CONFIG_IDF_TARGET_ESP32C6 *****/ #elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) /* Translate from Wolf SHA type to hardware algorithm. */ HardwareAlgorithm = 0; switch (sha->sha_type) { case SHA1: HardwareAlgorithm = 0; break; case SHA2_224: HardwareAlgorithm = 1; break; case SHA2_256: HardwareAlgorithm = 2; break; #if defined(WOLFSSL_SHA384) case SHA2_384: HardwareAlgorithm = 3; break; #endif #if defined(WOLFSSL_SHA512) case SHA2_512: HardwareAlgorithm = 4; break; #endif default: /* Unsupported SHA mode. */ sha->mode = ESP32_SHA_FAIL_NEED_UNROLL; return ESP_FAIL; } REG_WRITE(SHA_MODE_REG, HardwareAlgorithm); if (sha->isfirstblock) { REG_WRITE(SHA_START_REG, 1); sha->isfirstblock = 0; ESP_LOGV(TAG, " set sha->isfirstblock = 0"); #if defined(DEBUG_WOLFSSL) this_block_num = 1; /* one-based counter, just for debug info */ #endif } /* first block */ else { REG_WRITE(SHA_CONTINUE_REG, 1); #if defined(DEBUG_WOLFSSL) this_block_num++; /* one-based counter */ ESP_LOGV(TAG, " continue block #%d", this_block_num); #endif } /* not first block */ /* end ESP32S3 */ #elif defined(CONFIG_IDF_TARGET_ESP32) if (sha->isfirstblock) { /* start registers for first message block * we don't make any relational memory position assumptions. */ switch (sha->sha_type) { case SHA1: DPORT_REG_WRITE(SHA_1_START_REG, 1); break; case SHA2_256: DPORT_REG_WRITE(SHA_256_START_REG, 1); break; #if defined(WOLFSSL_SHA384) case SHA2_384: DPORT_REG_WRITE(SHA_384_START_REG, 1); break; #endif #if defined(WOLFSSL_SHA512) case SHA2_512: DPORT_REG_WRITE(SHA_512_START_REG, 1); break; #endif default: sha->mode = ESP32_SHA_FAIL_NEED_UNROLL; ret = ESP_FAIL; break; } sha->isfirstblock = 0; ESP_LOGV(TAG, " set sha->isfirstblock = 0"); #if defined(DEBUG_WOLFSSL) this_block_num = 1; /* one-based counter, just for debug info */ #endif } else { /* continue registers for next message block. * we don't make any relational memory position assumptions * for future chip architecture changes. */ switch (sha->sha_type) { case SHA1: DPORT_REG_WRITE(SHA_1_CONTINUE_REG, 1); break; case SHA2_256: DPORT_REG_WRITE(SHA_256_CONTINUE_REG, 1); break; #if defined(WOLFSSL_SHA384) case SHA2_384: DPORT_REG_WRITE(SHA_384_CONTINUE_REG, 1); break; #endif #if defined(WOLFSSL_SHA512) case SHA2_512: DPORT_REG_WRITE(SHA_512_CONTINUE_REG, 1); break; #endif default: /* error for unsupported other values */ sha->mode = ESP32_SHA_FAIL_NEED_UNROLL; ret = ESP_FAIL; break; } } /* end standard ESP32 */ #else ESP_LOGE(TAG, "Unsupported hardware"); #endif #if defined(DEBUG_WOLFSSL) this_block_num++; /* one-based counter */ ESP_LOGV(TAG, " continue block #%d", this_block_num); #endif ESP_LOGV(TAG, " leave esp_sha_start_process"); CTX_STACK_CHECK(sha); return ret; } #endif /* esp_sha_start_process !CONFIG_IDF_TARGET_ESP32C3/C6 */ /* ** process message block */ static int wc_esp_process_block(WC_ESP32SHA* ctx, /* see ctx->sha_type */ const word32* data, word32 len) { int ret = ESP_OK; /* assume success */ word32 word32_to_save = (len) / (sizeof(word32)); #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) word32* MessageSource; word32* AcceleratorMessage; #define MAX_SHA_VALUE SHA_TYPE_MAX #elif CONFIG_IDF_TARGET_ESP32 int i; /* Only values 0 .. 3 are valid for ESP32; SHA_INVALID = -1 */ #define MAX_SHA_VALUE 4 #else /* Newer SoC devices have a different value: SHA_TYPE_MAX */ #define MAX_SHA_VALUE SHA_TYPE_MAX #endif ESP_LOGV(TAG, " enter esp_process_block"); if ((ctx->sha_type < 0) || (ctx->sha_type > MAX_SHA_VALUE)) { ESP_LOGE(TAG, "Unexpected sha_type: %d", ctx->sha_type); } CTX_STACK_CHECK(ctx); if (word32_to_save > 0x31) { word32_to_save = 0x31; ESP_LOGE(TAG, " ERROR esp_process_block length exceeds 0x31 words."); } /* wait until the engine is available */ ret = wc_esp_wait_until_idle(); #if defined(CONFIG_IDF_TARGET_ESP32) /* load [len] words of message data into HW */ for (i = 0; i < word32_to_save; i++) { /* By using DPORT_REG_WRITE, we avoid the need * to call __builtin_bswap32 to address endianness. * * A useful watch array cast to watch at runtime: * ((word32[32]) (*(volatile word32 *)(SHA_TEXT_BASE))) * * Write value to DPORT register (does not require protecting) */ DPORT_REG_WRITE(SHA_TEXT_BASE + (i*sizeof(word32)), *(data + i)); /* memw confirmed auto inserted by compiler here */ } /* Notify HW to start process * see ctx->sha_type * reg data does not change until we are ready to read */ ret = esp_sha_start_process(ctx); /***** END CONFIG_IDF_TARGET_ESP32 */ #elif defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /************* RISC-V Architecture ************* * * SHA_M_1_REG is not a macro: * DPORT_REG_WRITE(SHA_M_1_REG + (i*sizeof(word32)), *(data + i)); * * but we have this HAL: sha_ll_fill_text_block * * Note that unlike the plain ESP32 that has only 1 register, we can write * the entire block. * SHA_TEXT_BASE = 0x6003b080 * SHA_H_BASE = 0x6003b040 * see hash: (word32[08]) (*(volatile uint32_t *)(SHA_H_BASE)) * message: (word32[16]) (*(volatile uint32_t *)(SHA_TEXT_BASE)) * ((word32[16]) (*(volatile uint32_t *)(SHA_TEXT_BASE))) */ if (&data != _active_digest_address) { ESP_LOGV(TAG, "Moving alternate ctx->for_digest"); /* move last known digest into HW reg during interleave */ /* sha_ll_write_digest(ctx->sha_type, ctx->for_digest, WC_SHA256_BLOCK_SIZE); */ _active_digest_address = &data; } if (ctx->isfirstblock) { ets_sha_enable(); /* will clear initial digest */ #if ESP_IDF_VERSION_MAJOR >= 6 /* Beginning ESP-IDF v6, the mode needs to be explicitly set. */ sha_hal_wait_idle(); switch (ctx->sha_type) { case SHA1: sha_hal_set_mode(SHA1); break; case SHA2_224: esp_sha_set_mode(SHA2_224); break; case SHA2_256: esp_sha_set_mode(SHA2_256); break; default: /* Unsupported SHA mode. */ ESP_LOGW(TAG, "Unexpected sha_type", ctx->sha_type); } #endif #if defined(DEBUG_WOLFSSL) { this_block_num = 1; /* one-based counter, just for debug info */ } #endif } else { #if defined(DEBUG_WOLFSSL) { this_block_num++; } #endif } /* call Espressif HAL for this hash*/ sha_hal_hash_block(ctx->sha_type, (void *)(data), word32_to_save, ctx->isfirstblock); ctx->isfirstblock = 0; /* once we hash a block, * we're no longer at the first */ /***** END CONFIG_IDF_TARGET_ESP32C2 or * CONFIG_IDF_TARGET_ESP8684 or * CONFIG_IDF_TARGET_ESP32C3 or * CONFIG_IDF_TARGET_ESP32C6 */ #elif defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) MessageSource = (word32*)data; AcceleratorMessage = (word32*)(SHA_TEXT_BASE); while (word32_to_save--) { /* Must swap endianness of data loaded into hardware accelerator * to produce correct result. Using DPORT_REG_WRITE doesn't avoid this * for ESP32s3. * Note: data sheet claims we also need to swap endianness across * 64 byte words when doing SHA-512, but the SHA-512 result is not * correct if you do that. */ DPORT_REG_WRITE(AcceleratorMessage, __builtin_bswap32(*MessageSource)); ++AcceleratorMessage; ++MessageSource; } /* (word32_to_save--) */ /* notify HW to start process * see ctx->sha_type * reg data does not change until we are ready to read */ ret = esp_sha_start_process(ctx); /***** END CONFIG_IDF_TARGET_ESP32S2 or CONFIG_IDF_TARGET_ESP32S3 */ #else ret = ESP_FAIL; ESP_LOGE(TAG, "ERROR: (CONFIG_IDF_TARGET not supported"); #endif #ifdef WOLFSSL_HW_METRICS switch (ctx->sha_type) { case SHA1: esp_sha1_hw_hash_usage_ct++; break; #ifndef NO_WOLFSSL_ESP32_CRYPT_HASH_SHA224 case SHA2_224: esp_sha2_224_hw_hash_usage_ct++; break; #endif case SHA2_256: esp_sha2_256_hw_hash_usage_ct++; break; default: break; } #endif CTX_STACK_CHECK(ctx); ESP_LOGV(TAG, " leave esp_process_block"); return ret; } /* wc_esp_process_block */ /* ** retrieve SHA digest from memory */ int wc_esp_digest_state(WC_ESP32SHA* ctx, byte* hash) { word32 digestSz; #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) uint64_t* pHash64Buffer; uint32_t* pHashDestination; size_t szHashWords; size_t szHash64Words; #endif ESP_LOGV(TAG, "enter esp_digest_state"); CTX_STACK_CHECK(ctx); if (ctx == NULL) { return BAD_FUNC_ARG; } /* sanity check */ #if defined(CONFIG_IDF_TARGET_ESP32) if (ctx->sha_type == SHA_INVALID) { #elif defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32S2) || \ defined(CONFIG_IDF_TARGET_ESP32S3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) if (ctx->sha_type >= SHA_TYPE_MAX) { #else ESP_LOGE(TAG, "unexpected target for wc_esp_digest_state"); { #endif /* conditional sanity check on she_type */ ctx->mode = ESP32_SHA_FAIL_NEED_UNROLL; ESP_LOGE(TAG, "error. sha_type %d is invalid.", ctx->sha_type); return ESP_FAIL; } digestSz = wc_esp_sha_digest_size(ctx->sha_type); if (digestSz == 0) { ctx->mode = ESP32_SHA_FAIL_NEED_UNROLL; ESP_LOGE(TAG, "unexpected error. sha_type is invalid."); return ESP_FAIL; } #if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32S3) if (ctx->isfirstblock == 1) { /* no hardware use yet. Nothing to do yet */ return ESP_OK; } /* wait until idle */ wc_esp_wait_until_idle(); /* read hash result into buffer & flip endianness */ pHashDestination = (uint32_t*)hash; szHashWords = wc_esp_sha_digest_size(ctx->sha_type) / sizeof(word32); esp_dport_access_read_buffer(pHashDestination, SHA_H_BASE, szHashWords); if (ctx->sha_type == SHA2_512) { /* Although we don't have to swap endianness on 64-bit words ** at the input, we do for the output. */ szHash64Words = szHashWords / 2; pHash64Buffer = (uint64_t*)pHashDestination; while (szHash64Words--) { *pHash64Buffer = __builtin_bswap64(*pHash64Buffer); ++pHash64Buffer; } } /* (ctx->sha_type == SHA2_512) */ else { while (szHashWords--) { *pHashDestination = __builtin_bswap32(*pHashDestination); ++pHashDestination; } } /* not (ctx->sha_type == SHA2_512) */ /* end if CONFIG_IDF_TARGET_ESP32S3 */ #elif defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) wc_esp_wait_until_idle(); sha_ll_read_digest( ctx->sha_type, (void *)hash, wc_esp_sha_digest_size(ctx->sha_type) / sizeof(word32) ); #elif defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) wc_esp_wait_until_idle(); sha_ll_read_digest( ctx->sha_type, (void *)hash, wc_esp_sha_digest_size(ctx->sha_type) / sizeof(word32) ); #else /* Not CONFIG_IDF_TARGET_ESP32S3 */ /* wait until idle */ wc_esp_wait_until_idle(); /* each sha_type register is at a different location */ #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) #elif defined(CONFIG_IDF_TARGET_ESP32S2) #else switch (ctx->sha_type) { case SHA1: DPORT_REG_WRITE(SHA_1_LOAD_REG, 1); break; case SHA2_256: DPORT_REG_WRITE(SHA_256_LOAD_REG, 1); break; #if defined(WOLFSSL_SHA384) case SHA2_384: DPORT_REG_WRITE(SHA_384_LOAD_REG, 1); break; #endif #if defined(WOLFSSL_SHA512) case SHA2_512: DPORT_REG_WRITE(SHA_512_LOAD_REG, 1); break; #endif default: ctx->mode = ESP32_SHA_FAIL_NEED_UNROLL; return ESP_FAIL; } if (ctx->isfirstblock == 1) { /* no hardware use yet. Nothing to do yet */ return ESP_OK; } /* LOAD final digest */ wc_esp_wait_until_idle(); /* MEMW instructions before volatile memory references to guarantee * sequential consistency. At least one MEMW should be executed in * between every load or store to a volatile variable */ asm volatile("memw"); /* put result in hash variable. * * ALERT - hardware specific. See esp_hw_support\port\esp32\dport_access.c * * note we read 4-byte word32's here via DPORT_SEQUENCE_REG_READ * * example: * DPORT_SEQUENCE_REG_READ(address + i * 4); */ #ifdef WOLFSSL_ESP32_CRYPT_DEBUG ESP_LOGW(TAG, "SHA HW read..."); #endif esp_dport_access_read_buffer( #if ESP_IDF_VERSION_MAJOR >= 4 (uint32_t*)(hash), /* the result will be found in hash upon exit */ #else (word32*)(hash), /* the result will be found in hash upon exit */ #endif SHA_TEXT_BASE, /* there's a fixed reg addr for all SHA */ digestSz / sizeof(word32) /* # 4-byte */ ); #endif #if defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384) if (ctx->sha_type == SHA2_384 || ctx->sha_type == SHA2_512) { word32 i; word32* pwrd1 = (word32*)(hash); /* swap 32 bit words in 64 bit values */ for (i = 0; i < WC_SHA512_DIGEST_SIZE / 4; i += 2) { pwrd1[i] ^= pwrd1[i + 1]; pwrd1[i + 1] ^= pwrd1[i]; pwrd1[i] ^= pwrd1[i + 1]; } } #endif /* SHA512 or SHA384*/ #endif /* not CONFIG_IDF_TARGET_ESP32S3, C3, else... */ CTX_STACK_CHECK(ctx); ESP_LOGV(TAG, "leave esp_digest_state"); return ESP_OK; } /* wc_esp_digest_state */ #ifndef NO_SHA /* ** sha1 process */ int esp_sha_process(struct wc_Sha* sha, const byte* data) { int ret = 0; ESP_LOGV(TAG, "enter esp_sha_process"); wc_esp_process_block(&sha->ctx, (const word32*)data, WC_SHA_BLOCK_SIZE); ESP_LOGV(TAG, "leave esp_sha_process"); return ret; } /* esp_sha_process */ /* ** retrieve sha1 digest */ int esp_sha_digest_process(struct wc_Sha* sha, byte blockprocess) { int ret = 0; ESP_LOGV(TAG, "enter esp_sha_digest_process"); if (blockprocess) { wc_esp_process_block(&sha->ctx, sha->buffer, WC_SHA_BLOCK_SIZE); } ret = wc_esp_digest_state(&sha->ctx, (byte*)sha->digest); if (blockprocess) { ESP_LOGV(TAG, "esp_sha_digest_process NEW UNLOCK"); esp_sha_hw_unlock(&sha->ctx); /* also unlocks mutex */ ESP_LOGV(TAG, "sha blockprocess mutex_ctx_owner = NULLPTR"); mutex_ctx_owner = NULLPTR; } ESP_LOGV(TAG, "leave esp_sha_digest_process"); return ret; } /* esp_sha_digest_process */ #endif /* NO_SHA */ #if !defined(NO_SHA256) && !defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA256) /* ** sha256 process ** ** repeatedly call this for [N] blocks of [WC_SHA256_BLOCK_SIZE] bytes of data */ int esp_sha256_process(struct wc_Sha256* sha, const byte* data) { int ret = 0; switch ((&sha->ctx)->sha_type) { case SHA2_256: #if defined(DEBUG_WOLFSSL_VERBOSE) ESP_LOGV(TAG, " confirmed SHA256 type call match"); #endif wc_esp_process_block(&sha->ctx, (const word32*)data, WC_SHA256_BLOCK_SIZE); break; #if defined(WOLFSSL_SHA224) && !defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA224) case SHA2_224: #if defined(DEBUG_WOLFSSL_VERBOSE) ESP_LOGV(TAG, " confirmed SHA224 type call match"); #endif wc_esp_process_block(&sha->ctx, (const word32*)data, WC_SHA224_BLOCK_SIZE); break; #endif default: ret = ESP_FAIL; ESP_LOGE(TAG, " ERROR SHA type call mismatch"); break; } ESP_LOGV(TAG, " leave esp_sha256_process"); return ret; } /* esp_sha256_process */ /* ** retrieve sha256 digest ** ** note that wc_Sha256Final() in sha256.c expects to need to reverse byte ** order, even though we could have returned them in the right order. */ int esp_sha256_digest_process(struct wc_Sha256* sha, byte blockprocess) { int ret = ESP_OK; ESP_LOGV(TAG, "enter esp_sha256_digest_process"); #ifndef NO_WOLFSSL_ESP32_CRYPT_HASH_SHA256 if (blockprocess) { wc_esp_process_block(&sha->ctx, sha->buffer, WC_SHA256_BLOCK_SIZE); } wc_esp_digest_state(&sha->ctx, (byte*)sha->digest); if (blockprocess) { ESP_LOGV(TAG, "esp_sha256_digest_process blockprocess UNLOCK"); esp_sha_hw_unlock(&sha->ctx); /* also unlocks mutex */ ESP_LOGV(TAG, "blockprocess mutex_ctx_owner = NULLPTR"); mutex_ctx_owner = NULLPTR; } #else ESP_LOGE(TAG, "Call esp_sha256_digest_process with " "NO_WOLFSSL_ESP32_CRYPT_HASH_SHA256 "); #endif ESP_LOGV(TAG, "leave esp_sha256_digest_process"); return ret; } /* esp_sha256_digest_process */ #endif /* NO_SHA256 */ #if !(defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA384) && \ defined(NO_WOLFSSL_ESP32_CRYPT_HASH_SHA512) \ ) && \ (defined(WOLFSSL_SHA512) || defined(WOLFSSL_SHA384)) /* ** sha512 process. this is used for sha384 too. */ int esp_sha512_block(struct wc_Sha512* sha, const word32* data, byte isfinal) { int ret = 0; /* assume success */ ESP_LOGV(TAG, "enter esp_sha512_block"); /* start register offset */ #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) /* No SHA-512 HW on RISC-V SoC, so nothing to do. */ #else /* note that in SW mode, wolfSSL uses 64 bit words */ if (sha->ctx.mode == ESP32_SHA_SW) { ByteReverseWords64(sha->buffer, sha->buffer, WC_SHA512_BLOCK_SIZE); if (isfinal) { sha->buffer[WC_SHA512_BLOCK_SIZE / sizeof(word64) - 2] = sha->hiLen; sha->buffer[WC_SHA512_BLOCK_SIZE / sizeof(word64) - 1] = sha->loLen; } } else { /* when we are in HW mode, Espressif uses 32 bit words */ ByteReverseWords((word32*)sha->buffer, (word32*)sha->buffer, WC_SHA512_BLOCK_SIZE); if (isfinal) { sha->buffer[WC_SHA512_BLOCK_SIZE / sizeof(word64) - 2] = rotlFixed64(sha->hiLen, 32U); sha->buffer[WC_SHA512_BLOCK_SIZE / sizeof(word64) - 1] = rotlFixed64(sha->loLen, 32U); } ret = wc_esp_process_block(&sha->ctx, data, WC_SHA512_BLOCK_SIZE); } ESP_LOGV(TAG, "leave esp_sha512_block"); #endif return ret; } /* esp_sha512_block */ /* ** sha512 process. this is used for sha384 too. */ int esp_sha512_process(struct wc_Sha512* sha) { int ret = ESP_OK; /* assume success */ word32 *data = (word32*)sha->buffer; ESP_LOGV(TAG, "enter esp_sha512_process"); esp_sha512_block(sha, data, 0); ESP_LOGV(TAG, "leave esp_sha512_process"); return ret; } /* ** retrieve sha512 digest. this is used for sha384, sha512-224, sha512-256 too. */ int esp_sha512_digest_process(struct wc_Sha512* sha, byte blockproc) { int ret = 0; ESP_LOGV(TAG, "enter esp_sha512_digest_process"); #if defined(CONFIG_IDF_TARGET_ESP32C2) || \ defined(CONFIG_IDF_TARGET_ESP8684) || \ defined(CONFIG_IDF_TARGET_ESP32C3) || \ defined(CONFIG_IDF_TARGET_ESP32C6) { ESP_LOGW(TAG, "Warning: no SHA512 HW to digest on %s", CONFIG_IDF_TARGET); } #else if (blockproc) { word32* data = (word32*)sha->buffer; ret = esp_sha512_block(sha, data, 1); } if (sha->ctx.mode == ESP32_SHA_HW) { ret = wc_esp_digest_state(&sha->ctx, (byte*)sha->digest); } else { ESP_LOGW(TAG, "Call esp_sha512_digest_process in non-HW mode?"); } if (blockproc) { ESP_LOGV(TAG, "esp_sha512_digest_process NEW UNLOCK"); esp_sha_hw_unlock(&sha->ctx); /* also unlocks mutex */ ESP_LOGV(TAG, "mutex_ctx_owner = NULLPTR"); mutex_ctx_owner = NULLPTR; } ESP_LOGV(TAG, "leave esp_sha512_digest_process"); #endif return ret; } /* esp_sha512_digest_process */ #endif /* WOLFSSL_SHA512 || WOLFSSL_SHA384 */ #endif /* WOLFSSL_ESP32_CRYPT */ #endif /* !defined(NO_SHA) ||... */ #if defined(WOLFSSL_ESP32_CRYPT) && defined(WOLFSSL_HW_METRICS) int esp_sw_sha256_count_add(void) { int ret = 0; #if !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) esp_sha256_sw_fallback_usage_ct++; ret = esp_sha256_sw_fallback_usage_ct; #endif return ret; } int esp_hw_show_sha_metrics(void) { int ret = 0; #if defined(WOLFSSL_ESP32_CRYPT) && !defined(NO_WOLFSSL_ESP32_CRYPT_HASH) ESP_LOGI(TAG, "--------------------------------------------------------"); ESP_LOGI(TAG, "------------- wolfSSL ESP HW SHA Metrics -------------"); ESP_LOGI(TAG, "--------------------------------------------------------"); ESP_LOGI(TAG, "esp_sha_hw_copy_ct = %lu", esp_sha_hw_copy_ct); ESP_LOGI(TAG, "esp_sha1_hw_usage_ct = %lu", esp_sha1_hw_usage_ct); ESP_LOGI(TAG, "esp_sha1_sw_fallback_usage_ct = %lu", esp_sha1_sw_fallback_usage_ct); ESP_LOGI(TAG, "esp_sha_reverse_words_ct = %lu", esp_sha_reverse_words_ct); ESP_LOGI(TAG, "esp_sha1_hw_hash_usage_ct = %lu", esp_sha1_hw_hash_usage_ct); ESP_LOGI(TAG, "esp_sha2_224_hw_hash_usage_ct = %lu", esp_sha2_224_hw_hash_usage_ct); ESP_LOGI(TAG, "esp_sha2_256_hw_hash_usage_ct = %lu", esp_sha2_256_hw_hash_usage_ct); ESP_LOGI(TAG, "esp_byte_reversal_checks_ct = %lu", esp_byte_reversal_checks_ct); ESP_LOGI(TAG, "esp_byte_reversal_needed_ct = %lu", esp_byte_reversal_needed_ct); #else /* no HW math, no HW math metrics */ ret = 0; #endif /* HW_MATH_ENABLED */ return ret; } #endif /* WOLFSSL_ESP32_CRYPT and WOLFSSL_HW_METRICS */ #if defined(WOLFSSL_STACK_CHECK) int esp_sha_stack_check(WC_ESP32SHA* sha) { int ret = ESP_OK; if (sha == NULL) { ESP_LOGW(TAG, "esp_sha_stack_check; sha is NULL"); } else { if (sha->first_word != 0 || sha->last_word != 0) { ESP_LOGE(TAG, "esp_sha_stack_check warning"); ret = ESP_FAIL; } } return ret; } #endif /* WOLFSSL_STACK_CHECK */ #endif /* WOLFSSL_ESPIDF (exclude entire contents for non-Espressif projects. */