#include "sha256.h" // Only compile SHA256 implementation on platforms that support it #if defined(USE_ESP32) || defined(USE_ESP8266) || defined(USE_RP2040) || defined(USE_LIBRETINY) || defined(USE_HOST) #include "esphome/core/helpers.h" #include namespace esphome::sha256 { #if defined(USE_SHA256_PSA) // ESP-IDF 6.0 ships mbedtls 4.0 which removed the legacy mbedtls_sha256_* API. // Use the PSA Crypto API instead. PSA crypto is auto-initialized by ESP-IDF // at startup, so no psa_crypto_init() call is needed. SHA256::~SHA256() { psa_hash_abort(&this->op_); } void SHA256::init() { psa_hash_abort(&this->op_); this->op_ = PSA_HASH_OPERATION_INIT; psa_hash_setup(&this->op_, PSA_ALG_SHA_256); } void SHA256::add(const uint8_t *data, size_t len) { psa_hash_update(&this->op_, data, len); } void SHA256::calculate() { size_t hash_length; psa_hash_finish(&this->op_, this->digest_, sizeof(this->digest_), &hash_length); } #elif defined(USE_SHA256_MBEDTLS) // CRITICAL ESP32 HARDWARE SHA ACCELERATION REQUIREMENTS (IDF 5.5.x): // // ESP32 variants (except original ESP32) use DMA-based hardware SHA acceleration that requires // 32-byte aligned digest buffers. This is handled automatically via HashBase::digest_ which has // alignas(32) on these platforms. Two additional constraints apply: // // 1. NO VARIABLE LENGTH ARRAYS (VLAs): VLAs corrupt the stack layout, causing the DMA engine to // write to incorrect memory locations. This results in null pointer dereferences and crashes. // ALWAYS use fixed-size arrays (e.g., char buf[65], not char buf[size+1]). // // 2. SAME STACK FRAME ONLY: The SHA256 object must be created and used entirely within the same // function. NEVER pass the SHA256 object or HashBase pointer to another function. When the stack // frame changes (function call/return), the DMA references become invalid and will produce // truncated hash output (20 bytes instead of 32) or corrupt memory. // // CORRECT USAGE: // void my_function() { // sha256::SHA256 hasher; // hasher.init(); // hasher.add(data, len); // Any size, no chunking needed // hasher.calculate(); // bool ok = hasher.equals_hex(expected); // // hasher destroyed when function returns // } // // INCORRECT USAGE (WILL FAIL): // void my_function() { // sha256::SHA256 hasher; // helper(&hasher); // WRONG: Passed to different stack frame // } // void helper(HashBase *h) { // h->init(); // WRONG: Will produce truncated/corrupted output // } SHA256::~SHA256() { mbedtls_sha256_free(&this->ctx_); } void SHA256::init() { mbedtls_sha256_init(&this->ctx_); mbedtls_sha256_starts(&this->ctx_, 0); // 0 = SHA256, not SHA224 } void SHA256::add(const uint8_t *data, size_t len) { mbedtls_sha256_update(&this->ctx_, data, len); } void SHA256::calculate() { mbedtls_sha256_finish(&this->ctx_, this->digest_); } #elif defined(USE_ESP8266) || defined(USE_RP2040) SHA256::~SHA256() = default; void SHA256::init() { br_sha256_init(&this->ctx_); this->calculated_ = false; } void SHA256::add(const uint8_t *data, size_t len) { br_sha256_update(&this->ctx_, data, len); } void SHA256::calculate() { if (!this->calculated_) { br_sha256_out(&this->ctx_, this->digest_); this->calculated_ = true; } } #elif defined(USE_HOST) SHA256::~SHA256() { if (this->ctx_) { EVP_MD_CTX_free(this->ctx_); } } void SHA256::init() { if (this->ctx_) { EVP_MD_CTX_free(this->ctx_); } this->ctx_ = EVP_MD_CTX_new(); EVP_DigestInit_ex(this->ctx_, EVP_sha256(), nullptr); this->calculated_ = false; } void SHA256::add(const uint8_t *data, size_t len) { if (!this->ctx_) { this->init(); } EVP_DigestUpdate(this->ctx_, data, len); } void SHA256::calculate() { if (!this->ctx_) { this->init(); } if (!this->calculated_) { unsigned int len = 32; EVP_DigestFinal_ex(this->ctx_, this->digest_, &len); this->calculated_ = true; } } #else #error "SHA256 not supported on this platform" #endif } // namespace esphome::sha256 #endif // Platform check