Files
esphome/esphome/components/sha256/sha256.cpp
J. Nick Koston 9003844eda [core] Fix ESP32-S2/S3 hardware SHA crash by aligning HashBase digest buffer (#13234)
Co-authored-by: Jonathan Swoboda <154711427+swoboda1337@users.noreply.github.com>
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
2026-01-15 18:29:11 +00:00

118 lines
3.4 KiB
C++

#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 <cstring>
namespace esphome::sha256 {
#if defined(USE_ESP32) || defined(USE_LIBRETINY)
// 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