mirror of
https://github.com/esphome/esphome.git
synced 2025-11-01 15:41:52 +00:00
stack it
This commit is contained in:
@@ -578,28 +578,17 @@ void ESPHomeOTAComponent::log_auth_warning_(const LogString *msg) { ESP_LOGW(TAG
|
|||||||
bool ESPHomeOTAComponent::handle_auth_send_() {
|
bool ESPHomeOTAComponent::handle_auth_send_() {
|
||||||
// Initialize auth buffer if not already done
|
// Initialize auth buffer if not already done
|
||||||
if (!this->auth_buf_) {
|
if (!this->auth_buf_) {
|
||||||
// Determine which auth type to use and create hasher on stack
|
// Determine which auth type to use
|
||||||
HashBase *hasher = nullptr;
|
|
||||||
|
|
||||||
#ifdef USE_OTA_SHA256
|
|
||||||
sha256::SHA256 sha256_hasher;
|
|
||||||
#endif
|
|
||||||
#ifdef USE_OTA_MD5
|
|
||||||
md5::MD5Digest md5_hasher;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef USE_OTA_SHA256
|
#ifdef USE_OTA_SHA256
|
||||||
bool client_supports_sha256 = (this->ota_features_ & FEATURE_SUPPORTS_SHA256_AUTH) != 0;
|
bool client_supports_sha256 = (this->ota_features_ & FEATURE_SUPPORTS_SHA256_AUTH) != 0;
|
||||||
|
|
||||||
#ifdef ALLOW_OTA_DOWNGRADE_MD5
|
#ifdef ALLOW_OTA_DOWNGRADE_MD5
|
||||||
if (client_supports_sha256) {
|
if (client_supports_sha256) {
|
||||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
||||||
hasher = &sha256_hasher;
|
|
||||||
} else {
|
} else {
|
||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
this->log_auth_warning_(LOG_STR("Using MD5 for compatibility (deprecated)"));
|
this->log_auth_warning_(LOG_STR("Using MD5 for compatibility (deprecated)"));
|
||||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
||||||
hasher = &md5_hasher;
|
|
||||||
#else
|
#else
|
||||||
this->log_auth_warning_(LOG_STR("Client doesn't support SHA256 and MD5 is disabled"));
|
this->log_auth_warning_(LOG_STR("Client doesn't support SHA256 and MD5 is disabled"));
|
||||||
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
||||||
@@ -613,12 +602,10 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_SHA256_AUTH;
|
||||||
hasher = &sha256_hasher;
|
|
||||||
#endif // ALLOW_OTA_DOWNGRADE_MD5
|
#endif // ALLOW_OTA_DOWNGRADE_MD5
|
||||||
#else // !USE_OTA_SHA256
|
#else // !USE_OTA_SHA256
|
||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
this->auth_type_ = ota::OTA_RESPONSE_REQUEST_AUTH;
|
||||||
hasher = &md5_hasher;
|
|
||||||
#else
|
#else
|
||||||
this->log_auth_warning_(LOG_STR("No auth methods available"));
|
this->log_auth_warning_(LOG_STR("No auth methods available"));
|
||||||
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
||||||
@@ -626,53 +613,28 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
|||||||
#endif // USE_OTA_MD5
|
#endif // USE_OTA_MD5
|
||||||
#endif // USE_OTA_SHA256
|
#endif // USE_OTA_SHA256
|
||||||
|
|
||||||
// Calculate required buffer size using the hasher
|
// Generate nonce with appropriate hasher
|
||||||
const size_t hex_size = hasher->get_size() * 2;
|
bool success = false;
|
||||||
const size_t nonce_len = hasher->get_size() / 4;
|
#ifdef USE_OTA_SHA256
|
||||||
// Buffer needs: 1 (auth_type) + hex_size (nonce) + hex_size*2 (cnonce+response)
|
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
||||||
this->auth_buf_size_ = 1 + hex_size + hex_size * 2;
|
sha256::SHA256 sha_hasher;
|
||||||
this->auth_buf_ = std::make_unique<uint8_t[]>(this->auth_buf_size_);
|
success = this->prepare_auth_nonce_(&sha_hasher);
|
||||||
this->auth_buf_pos_ = 0;
|
}
|
||||||
|
#endif
|
||||||
|
#ifdef USE_OTA_MD5
|
||||||
|
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
||||||
|
md5::MD5Digest md5_hasher;
|
||||||
|
success = this->prepare_auth_nonce_(&md5_hasher);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Generate nonce
|
if (!success) {
|
||||||
char *buf = reinterpret_cast<char *>(this->auth_buf_.get() + 1);
|
|
||||||
if (!random_bytes(reinterpret_cast<uint8_t *>(buf), nonce_len)) {
|
|
||||||
this->log_auth_warning_(LOG_STR("Random bytes generation failed"));
|
|
||||||
this->cleanup_connection_();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasher->init();
|
|
||||||
hasher->add(buf, nonce_len);
|
|
||||||
hasher->calculate();
|
|
||||||
|
|
||||||
// Prepare buffer: auth_type (1 byte) + nonce (hex_size bytes)
|
|
||||||
this->auth_buf_[0] = this->auth_type_;
|
|
||||||
hasher->get_hex(buf);
|
|
||||||
|
|
||||||
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
|
||||||
// Log nonce for debugging
|
|
||||||
char log_buf[hex_size + 1];
|
|
||||||
memcpy(log_buf, buf, hex_size);
|
|
||||||
log_buf[hex_size] = '\0';
|
|
||||||
ESP_LOGV(TAG, "Auth: Nonce is %s", log_buf);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to write auth_type + nonce
|
// Try to write auth_type + nonce
|
||||||
// Calculate hex_size based on auth_type
|
size_t hex_size = this->get_auth_hex_size_();
|
||||||
size_t hex_size;
|
|
||||||
#ifdef USE_OTA_SHA256
|
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
|
||||||
hex_size = 64; // SHA256 = 32 bytes * 2
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#ifdef USE_OTA_MD5
|
|
||||||
hex_size = 32; // MD5 = 16 bytes * 2
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t to_write = 1 + hex_size;
|
const size_t to_write = 1 + hex_size;
|
||||||
size_t remaining = to_write - this->auth_buf_pos_;
|
size_t remaining = to_write - this->auth_buf_pos_;
|
||||||
|
|
||||||
@@ -695,26 +657,11 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
|
|||||||
|
|
||||||
// All written, prepare for reading phase
|
// All written, prepare for reading phase
|
||||||
this->auth_buf_pos_ = 0;
|
this->auth_buf_pos_ = 0;
|
||||||
|
|
||||||
// We'll start the challenge hash in handle_auth_read_ when we have the cnonce
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ESPHomeOTAComponent::handle_auth_read_() {
|
bool ESPHomeOTAComponent::handle_auth_read_() {
|
||||||
// Calculate hex_size based on auth_type
|
size_t hex_size = this->get_auth_hex_size_();
|
||||||
size_t hex_size;
|
|
||||||
#ifdef USE_OTA_SHA256
|
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
|
||||||
hex_size = 64; // SHA256 = 32 bytes * 2
|
|
||||||
} else
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#ifdef USE_OTA_MD5
|
|
||||||
hex_size = 32; // MD5 = 16 bytes * 2
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t to_read = hex_size * 2; // CNonce + Response
|
const size_t to_read = hex_size * 2; // CNonce + Response
|
||||||
|
|
||||||
// Try to read remaining bytes (CNonce + Response)
|
// Try to read remaining bytes (CNonce + Response)
|
||||||
@@ -746,29 +693,72 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// We have all the data, verify it
|
// We have all the data, verify it
|
||||||
// Create hasher on stack based on auth_type
|
bool matches = false;
|
||||||
HashBase *hasher = nullptr;
|
|
||||||
|
|
||||||
#ifdef USE_OTA_SHA256
|
#ifdef USE_OTA_SHA256
|
||||||
sha256::SHA256 sha256_hasher;
|
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
||||||
hasher = &sha256_hasher;
|
sha256::SHA256 sha_hasher;
|
||||||
|
matches = this->verify_hash_auth_(&sha_hasher, hex_size);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
md5::MD5Digest md5_hasher;
|
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
||||||
hasher = &md5_hasher;
|
md5::MD5Digest md5_hasher;
|
||||||
|
matches = this->verify_hash_auth_(&md5_hasher, hex_size);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!hasher) {
|
if (!matches) {
|
||||||
this->log_auth_warning_(LOG_STR("Invalid auth type"));
|
this->log_auth_warning_(LOG_STR("Password mismatch"));
|
||||||
|
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authentication successful - clean up auth state
|
||||||
|
this->cleanup_auth_();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPHomeOTAComponent::prepare_auth_nonce_(HashBase *hasher) {
|
||||||
|
// Calculate required buffer size using the hasher
|
||||||
|
const size_t hex_size = hasher->get_size() * 2;
|
||||||
|
const size_t nonce_len = hasher->get_size() / 4;
|
||||||
|
// Buffer needs: 1 (auth_type) + hex_size (nonce) + hex_size*2 (cnonce+response)
|
||||||
|
this->auth_buf_size_ = 1 + hex_size + hex_size * 2;
|
||||||
|
this->auth_buf_ = std::make_unique<uint8_t[]>(this->auth_buf_size_);
|
||||||
|
this->auth_buf_pos_ = 0;
|
||||||
|
|
||||||
|
// Generate nonce
|
||||||
|
char *buf = reinterpret_cast<char *>(this->auth_buf_.get() + 1);
|
||||||
|
if (!random_bytes(reinterpret_cast<uint8_t *>(buf), nonce_len)) {
|
||||||
|
this->log_auth_warning_(LOG_STR("Random bytes generation failed"));
|
||||||
this->cleanup_connection_();
|
this->cleanup_connection_();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasher->init();
|
||||||
|
hasher->add(buf, nonce_len);
|
||||||
|
hasher->calculate();
|
||||||
|
|
||||||
|
// Prepare buffer: auth_type (1 byte) + nonce (hex_size bytes)
|
||||||
|
this->auth_buf_[0] = this->auth_type_;
|
||||||
|
hasher->get_hex(buf);
|
||||||
|
|
||||||
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
||||||
|
char log_buf[hex_size + 1];
|
||||||
|
// Log nonce for debugging
|
||||||
|
memcpy(log_buf, buf, hex_size);
|
||||||
|
log_buf[hex_size] = '\0';
|
||||||
|
ESP_LOGV(TAG, "Auth: Nonce is %s", log_buf);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESPHomeOTAComponent::verify_hash_auth_(HashBase *hasher, size_t hex_size) {
|
||||||
// Get pointers to the data
|
// Get pointers to the data
|
||||||
|
size_t offset = 1 + hex_size; // Skip auth_type byte and nonce
|
||||||
char *nonce = reinterpret_cast<char *>(this->auth_buf_.get() + 1); // Skip auth_type byte
|
char *nonce = reinterpret_cast<char *>(this->auth_buf_.get() + 1); // Skip auth_type byte
|
||||||
char *cnonce = reinterpret_cast<char *>(this->auth_buf_.get() + offset);
|
char *cnonce = reinterpret_cast<char *>(this->auth_buf_.get() + offset);
|
||||||
const char *response = cnonce + hex_size;
|
const char *response = cnonce + hex_size;
|
||||||
@@ -789,12 +779,12 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
|
|||||||
#endif
|
#endif
|
||||||
char *log_buf = nullptr;
|
char *log_buf = nullptr;
|
||||||
#ifdef USE_OTA_SHA256
|
#ifdef USE_OTA_SHA256
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
|
if (hex_size == 64) {
|
||||||
log_buf = log_buf_sha;
|
log_buf = log_buf_sha;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
|
if (hex_size == 32) {
|
||||||
log_buf = log_buf_md5;
|
log_buf = log_buf_md5;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -818,18 +808,11 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Compare response
|
// Compare response
|
||||||
bool matches = hasher->equals_hex(response);
|
return hasher->equals_hex(response);
|
||||||
|
|
||||||
if (!matches) {
|
|
||||||
this->log_auth_warning_(LOG_STR("Password mismatch"));
|
|
||||||
this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_AUTH_INVALID);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authentication successful - clean up auth state
|
size_t ESPHomeOTAComponent::get_auth_hex_size_() const {
|
||||||
this->cleanup_auth_();
|
return this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH ? 64 : 32;
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESPHomeOTAComponent::cleanup_auth_() {
|
void ESPHomeOTAComponent::cleanup_auth_() {
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
|
|||||||
#ifdef USE_OTA_PASSWORD
|
#ifdef USE_OTA_PASSWORD
|
||||||
bool handle_auth_send_();
|
bool handle_auth_send_();
|
||||||
bool handle_auth_read_();
|
bool handle_auth_read_();
|
||||||
|
bool prepare_auth_nonce_(HashBase *hasher);
|
||||||
|
bool verify_hash_auth_(HashBase *hasher, size_t hex_size);
|
||||||
|
size_t get_auth_hex_size_() const;
|
||||||
void cleanup_auth_();
|
void cleanup_auth_();
|
||||||
void log_auth_warning_(const LogString *msg);
|
void log_auth_warning_(const LogString *msg);
|
||||||
#endif // USE_OTA_PASSWORD
|
#endif // USE_OTA_PASSWORD
|
||||||
|
|||||||
Reference in New Issue
Block a user