1
0
mirror of https://github.com/esphome/esphome.git synced 2025-09-22 13:12:22 +01:00
This commit is contained in:
J. Nick Koston
2025-09-18 14:46:49 -05:00
parent bff257258e
commit 853d3ae331
3 changed files with 44 additions and 23 deletions

View File

@@ -16,7 +16,7 @@ from esphome.const import (
CONF_SAFE_MODE,
CONF_VERSION,
)
from esphome.core import coroutine_with_priority
from esphome.core import CORE, coroutine_with_priority
from esphome.coroutine import CoroPriority
import esphome.final_validate as fv
@@ -24,9 +24,17 @@ _LOGGER = logging.getLogger(__name__)
CODEOWNERS = ["@esphome/core"]
AUTO_LOAD = ["md5", "sha256", "socket"]
DEPENDENCIES = ["network"]
def AUTO_LOAD():
"""Conditionally auto-load sha256 only on platforms that support it."""
base_components = ["md5", "socket"]
if CORE.is_esp32 or CORE.is_esp8266 or CORE.is_rp2040:
return base_components + ["sha256"]
return base_components
esphome = cg.esphome_ns.namespace("esphome")
ESPHomeOTAComponent = esphome.class_("ESPHomeOTAComponent", OTAComponent)
@@ -126,6 +134,12 @@ FINAL_VALIDATE_SCHEMA = ota_esphome_final_validate
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
cg.add(var.set_port(config[CONF_PORT]))
# Only include SHA256 support on platforms that have it
# This prevents including unnecessary SHA256 code on platforms like LibreTiny
if CORE.is_esp32 or CORE.is_esp8266 or CORE.is_rp2040:
cg.add_define("USE_OTA_SHA256")
if CONF_PASSWORD in config:
cg.add(var.set_auth_password(config[CONF_PASSWORD]))
cg.add_define("USE_OTA_PASSWORD")

View File

@@ -1,7 +1,7 @@
#include "ota_esphome.h"
#ifdef USE_OTA
#include "esphome/components/md5/md5.h"
#ifdef USE_SHA256
#ifdef USE_OTA_SHA256
#include "esphome/components/sha256/sha256.h"
#endif
#include "esphome/components/network/util.h"
@@ -98,7 +98,7 @@ void ESPHomeOTAComponent::loop() {
}
static const uint8_t FEATURE_SUPPORTS_COMPRESSION = 0x01;
#ifdef USE_SHA256
#ifdef USE_OTA_SHA256
static const uint8_t FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
#endif
@@ -112,7 +112,7 @@ template<> struct HashTraits<md5::MD5Digest> {
static constexpr ota::OTAResponseTypes auth_request = ota::OTA_RESPONSE_REQUEST_AUTH;
};
#ifdef USE_SHA256
#ifdef USE_OTA_SHA256
template<> struct HashTraits<sha256::SHA256> {
static constexpr int nonce_size = 16;
static constexpr int hex_size = 64;
@@ -133,9 +133,9 @@ template<typename HashClass> bool perform_hash_auth(ESPHomeOTAComponent *ota, co
char hex_buffer1[hex_buffer_size]; // Used for: nonce -> expected result
char hex_buffer2[hex_buffer_size]; // Used for: cnonce -> response
// Small stack buffer for auth request and nonce seed
// Small stack buffer for auth request and nonce seed bytes
uint8_t buf[1];
char nonce_seed[17]; // Max: "%08x%08x" = 16 chars + null
uint8_t nonce_bytes[8]; // Max 8 bytes (2 x uint32_t for SHA256)
// Send auth request type
buf[0] = Traits::auth_request;
@@ -144,13 +144,29 @@ template<typename HashClass> bool perform_hash_auth(ESPHomeOTAComponent *ota, co
HashClass hasher;
hasher.init();
// Generate nonce seed
// Generate nonce seed bytes
uint32_t r1 = random_uint32();
// Convert first uint32 to bytes (always needed for MD5)
nonce_bytes[0] = (r1 >> 24) & 0xFF;
nonce_bytes[1] = (r1 >> 16) & 0xFF;
nonce_bytes[2] = (r1 >> 8) & 0xFF;
nonce_bytes[3] = r1 & 0xFF;
if (Traits::nonce_size == 8) {
sprintf(nonce_seed, "%08" PRIx32, random_uint32());
} else {
sprintf(nonce_seed, "%08" PRIx32 "%08" PRIx32, random_uint32(), random_uint32());
// MD5: 8 chars = "%08x" format = 4 bytes from one random uint32
hasher.add(nonce_bytes, 4);
}
hasher.add(nonce_seed, Traits::nonce_size);
#ifdef USE_OTA_SHA256
else {
// SHA256: 16 chars = "%08x%08x" format = 8 bytes from two random uint32s
uint32_t r2 = random_uint32();
nonce_bytes[4] = (r2 >> 24) & 0xFF;
nonce_bytes[5] = (r2 >> 16) & 0xFF;
nonce_bytes[6] = (r2 >> 8) & 0xFF;
nonce_bytes[7] = r2 & 0xFF;
hasher.add(nonce_bytes, 8);
}
#endif
hasher.calculate();
// Use hex_buffer1 for nonce
@@ -335,7 +351,7 @@ void ESPHomeOTAComponent::handle_data_() {
if (!this->password_.empty()) {
bool auth_success = false;
#ifdef USE_SHA256
#ifdef USE_OTA_SHA256
// Check if client supports SHA256 auth
bool use_sha256 = (ota_features & FEATURE_SUPPORTS_SHA256_AUTH) != 0;
@@ -343,7 +359,7 @@ void ESPHomeOTAComponent::handle_data_() {
// Use SHA256 for authentication
auth_success = perform_hash_auth<sha256::SHA256>(this, this->password_);
} else
#endif // USE_SHA256
#endif // USE_OTA_SHA256
{
// Fall back to MD5 for backward compatibility (or when SHA256 is not available)
auth_success = perform_hash_auth<md5::MD5Digest>(this, this->password_);

View File

@@ -1,14 +1,5 @@
import esphome.codegen as cg
import esphome.config_validation as cv
from esphome.core import coroutine_with_priority
CODEOWNERS = ["@esphome/core"]
sha256_ns = cg.esphome_ns.namespace("sha256")
CONFIG_SCHEMA = cv.All(cv.Schema({}))
@coroutine_with_priority(1.0)
async def to_code(config):
cg.add_define("USE_SHA256")