| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -614,24 +614,67 @@ bool ESPHomeOTAComponent::handle_auth_send_() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Generate nonce with appropriate hasher
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    bool success = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Generate nonce - hasher must be created and used in same stack frame
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // CRITICAL ESP32-S3 HARDWARE SHA ACCELERATION REQUIREMENTS:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // 1. Hash objects must NEVER be passed to another function (different stack frame)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // 2. NO Variable Length Arrays (VLAs) - they corrupt the stack with hardware DMA
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // 3. All hash operations (init/add/calculate) must happen in the SAME function where object is created
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Violating these causes truncated hash output (20 bytes instead of 32) or memory corruption.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Buffer layout after AUTH_READ completes:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   [0]: auth_type (1 byte)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   [1...hex_size]: nonce (hex_size bytes) - our random nonce sent in AUTH_SEND
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   [1+hex_size...1+2*hex_size-1]: cnonce (hex_size bytes) - client's nonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   [1+2*hex_size...1+3*hex_size-1]: response (hex_size bytes) - client's hash
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Declare both hash objects in same stack frame, use pointer to select.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // NOTE: Both objects are declared here even though only one is used. This is REQUIRED for ESP32-S3
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // hardware SHA acceleration - the object must exist in this stack frame for all operations.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // Do NOT try to "optimize" by creating the object inside the if block, as it would go out of scope.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_SHA256
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    sha256::SHA256 sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_MD5
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    md5::MD5Digest md5_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    HashBase *hasher = nullptr;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_SHA256
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      sha256::SHA256 sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      success = this->prepare_auth_nonce_(&sha_hasher);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      hasher = &sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#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);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      hasher = &md5_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!success) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const size_t hex_size = hasher->get_size() * 2;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const size_t nonce_len = hasher->get_size() / 4;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const size_t auth_buf_size = 1 + 3 * hex_size;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this->auth_buf_ = std::make_unique<uint8_t[]>(auth_buf_size);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this->auth_buf_pos_ = 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    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 failed"));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_UNKNOWN);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher->init();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher->add(buf, nonce_len);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher->calculate();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this->auth_buf_[0] = this->auth_type_;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher->get_hex(buf);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    char log_buf[65];  // Fixed size for SHA256 hex (64) + null, works for MD5 (32) too
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    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
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -678,89 +721,41 @@ bool ESPHomeOTAComponent::handle_auth_read_() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // We have all the data, verify it
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  bool matches = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *nonce = reinterpret_cast<char *>(this->auth_buf_.get() + 1);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *cnonce = nonce + hex_size;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *response = cnonce + hex_size;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // CRITICAL ESP32-S3: Hash objects must stay in same stack frame (no passing to other functions).
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Declare both hash objects in same stack frame, use pointer to select.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // NOTE: Both objects are declared here even though only one is used. This is REQUIRED for ESP32-S3
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // hardware SHA acceleration - the object must exist in this stack frame for all operations.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Do NOT try to "optimize" by creating the object inside the if block, as it would go out of scope.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_SHA256
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  sha256::SHA256 sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_MD5
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  md5::MD5Digest md5_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  HashBase *hasher = nullptr;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_SHA256
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_SHA256_AUTH) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    sha256::SHA256 sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    matches = this->verify_hash_auth_(&sha_hasher, hex_size);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher = &sha_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#ifdef USE_OTA_MD5
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (this->auth_type_ == ota::OTA_RESPONSE_REQUEST_AUTH) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    md5::MD5Digest md5_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    matches = this->verify_hash_auth_(&md5_hasher, hex_size);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    hasher = &md5_hasher;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  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 layout after AUTH_READ completes:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  //   [0]: auth_type (1 byte)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  //   [1...hex_size]: nonce (hex_size bytes) - our random nonce sent in AUTH_SEND
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  //   [1+hex_size...1+2*hex_size-1]: cnonce (hex_size bytes) - client's nonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  //   [1+2*hex_size...1+3*hex_size-1]: response (hex_size bytes) - client's hash
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Total: 1 + 3*hex_size
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const size_t auth_buf_size = 1 + 3 * hex_size;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  this->auth_buf_ = std::make_unique<uint8_t[]>(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 failed"));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    this->send_error_and_cleanup_(ota::OTA_RESPONSE_ERROR_UNKNOWN);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    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 in the buffer (see prepare_auth_nonce_ for buffer layout)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *nonce = reinterpret_cast<char *>(this->auth_buf_.get() + 1);  // Skip auth_type byte
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *cnonce = nonce + hex_size;                                    // CNonce immediately follows nonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const char *response = cnonce + hex_size;                                 // Response immediately follows cnonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Calculate expected hash: password + nonce + cnonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  hasher->init();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  hasher->add(this->password_.c_str(), this->password_.length());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  hasher->add(nonce, hex_size * 2);  // Add both nonce and cnonce (contiguous in buffer)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  hasher->calculate();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  char log_buf[hex_size + 1];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  char log_buf[65];  // Fixed size for SHA256 hex (64) + null, works for MD5 (32) too
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Log CNonce
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  memcpy(log_buf, cnonce, hex_size);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  log_buf[hex_size] = '\0';
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@@ -778,7 +773,18 @@ bool ESPHomeOTAComponent::verify_hash_auth_(HashBase *hasher, size_t hex_size) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				#endif
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Compare response
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return hasher->equals_hex(response);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  bool matches = 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
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  this->cleanup_auth_();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				size_t ESPHomeOTAComponent::get_auth_hex_size_() const {
 | 
			
		
		
	
	
		
			
				
					
					| 
						
					 | 
				
			
			 | 
			 | 
			
				 
 |