2#ifdef USE_OTA_ENCRYPTION_PROVISIONED
21#ifdef USE_LWIP_FAST_SELECT
35#ifdef USE_OTA_ENCRYPTION
37#ifdef USE_OTA_ENCRYPTION_PROVISIONED
46static constexpr uint16_t OTA_BLOCK_SIZE = 8192;
47static constexpr uint32_t OTA_SOCKET_TIMEOUT_HANDSHAKE = 60000;
51static constexpr uint32_t OTA_SOCKET_TIMEOUT_DATA = 105000;
54static constexpr uint32_t OTA_HANDSHAKE_PREEMPT = 10000;
55static constexpr uint32_t OTA_PROGRESS_INTERVAL_MS = 1000;
56static constexpr size_t OTA_SIZE_FIELD_BYTES = 4;
64 if (global_esphome_ota_component !=
nullptr) {
70#ifdef USE_OTA_ENCRYPTION_PROVISIONED
86 int err = this->
server_->
setsockopt(SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(
int));
118 global_esphome_ota_component =
this;
119#ifdef USE_LWIP_FAST_SELECT
125#ifdef USE_OTA_PARTITIONS
131 char addr_buf[network::USE_ADDRESS_BUFFER_SIZE];
133 "Over-The-Air updates:\n"
136#ifdef USE_OTA_ENCRYPTION
141#ifdef USE_OTA_ENCRYPTION_REQUIRED
143 LOG_STR_LITERAL(
"required")
144#elif defined(USE_OTA_ENCRYPTION_PROVISIONED)
147 this->
noise_context_().has_psk() ? LOG_STR_LITERAL(
"offered, plaintext accepted")
148 : LOG_STR_LITERAL(
"offered once the api key is provisioned")
149#elif defined(USE_OTA_ENCRYPTION)
151 LOG_STR_LITERAL(
"offered, plaintext accepted")
154#ifdef USE_OTA_PASSWORD
156 ESP_LOGCONFIG(TAG,
" Password configured");
159#ifdef USE_OTA_PARTITIONS
161 " Partition access allowed\n"
163 " Partition address: 0x%" PRIX32
"\n"
164 " Used size: %zu bytes (0x%zX)",
169 " Partition table:\n"
170 " %-12s %-4s %-8s %-10s %-10s",
171 "Name",
"Type",
"Subtype",
"Address",
"Size");
172 esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY,
nullptr);
173 while (it !=
nullptr) {
174 const esp_partition_t *partition = esp_partition_get(it);
175 ESP_LOGCONFIG(TAG,
" %-12s 0x%-2X 0x%-6X 0x%-8" PRIX32
" 0x%-8" PRIX32, partition->label, partition->type,
176 partition->subtype, partition->address, partition->size);
177 it = esp_partition_next(it);
179 esp_partition_iterator_release(it);
180 esp_bootloader_desc_t bootloader_desc;
181 esp_err_t err = esp_ota_get_bootloader_description(
nullptr, &bootloader_desc);
182 ESP_LOGCONFIG(TAG,
" Bootloader: ESP-IDF %s",
183 (err == ESP_OK) ? bootloader_desc.idf_ver : LOG_STR_LITERAL(
"version unknown"));
200static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_COMPRESSION = 0x01;
201static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
202static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL = 0x04;
203static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_NOISE = 0x08;
204static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_DEFLATE = 0x10;
205#ifdef ESPHOME_OTA_UDP
206static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_UDP = 0x20;
209static constexpr uint8_t CLIENT_NOISE_FEATURES =
210 CLIENT_FEATURE_SUPPORTS_NOISE | CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL;
211static constexpr uint8_t SERVER_FEATURE_SUPPORTS_COMPRESSION = 0x01;
212static constexpr uint8_t SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS = 0x02;
213static constexpr uint8_t SERVER_FEATURE_SUPPORTS_NOISE = 0x04;
216static constexpr uint8_t SERVER_FEATURE_SUPPORTS_DEFLATE = 0x08;
217#ifdef ESPHOME_OTA_UDP
218static constexpr uint8_t SERVER_FEATURE_SUPPORTS_UDP = 0x10;
219static constexpr uint8_t UDP_REQUEST_V1 = 0x01;
222#ifdef USE_OTA_ENCRYPTION
224 return (this->
handshake_buf_[1] & SERVER_FEATURE_SUPPORTS_NOISE) != 0 &&
225 (this->
ota_features_ & CLIENT_NOISE_FEATURES) == CLIENT_NOISE_FEATURES;
230#ifdef USE_OTA_ENCRYPTION_REQUIRED
234 return (this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL) != 0;
245 if (this->
client_ ==
nullptr) {
254 int err = this->
client_->setsockopt(IPPROTO_TCP, TCP_NODELAY, &enable,
sizeof(
int));
260 err = this->
client_->setblocking(
false);
275 ESP_LOGW(TAG,
"Handshake timeout");
281 ESP_LOGD(TAG,
"Newer connection waiting");
289 if (!this->
try_read_(5, LOG_STR(
"read magic"))) {
295 ESP_LOGW(TAG,
"Magic bytes mismatch! 0x%02X-0x%02X-0x%02X-0x%02X-0x%02X", this->
handshake_buf_[0],
310 if (!this->
try_write_(2, LOG_STR(
"ack magic"))) {
321 if (!this->
try_read_(1, LOG_STR(
"read feature"))) {
327#ifdef USE_OTA_ENCRYPTION_REQUIRED
329 if ((this->
ota_features_ & CLIENT_NOISE_FEATURES) != CLIENT_NOISE_FEATURES) {
330 ESP_LOGW(TAG,
"Client does not support encryption");
338 const bool supports_compression =
339 (this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_COMPRESSION) != 0 && ota::OTABackend::supports_compression();
345 static_assert(
HANDSHAKE_BUF_SIZE >= 2,
"handshake_buf_ must hold the 2-byte extended-protocol feature ack");
347 this->
handshake_buf_[1] = (supports_compression ? SERVER_FEATURE_SUPPORTS_COMPRESSION : 0);
348#ifdef ESPHOME_OTA_UDP
351#ifdef USE_OTA_PARTITIONS
352 this->
handshake_buf_[1] |= SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS;
354#ifdef USE_OTA_ENCRYPTION_PROVISIONED
359#elif defined(USE_OTA_ENCRYPTION)
363#ifdef USE_OTA_ENCRYPTION
370#ifdef USE_OTA_DEFLATE
372 if ((this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_DEFLATE) != 0) {
381 ESP_LOGW(TAG,
"No memory to inflate");
393 static constexpr size_t STANDARD_PROTO_ACK_SIZE = 1;
394 static constexpr size_t EXTENDED_PROTO_ACK_SIZE = 2;
395 const size_t ack_size = this->
extended_proto_() ? EXTENDED_PROTO_ACK_SIZE : STANDARD_PROTO_ACK_SIZE;
396 if (!this->
try_write_(ack_size, LOG_STR(
"ack feature"))) {
399#ifdef USE_OTA_ENCRYPTION
412#ifdef USE_OTA_PASSWORD
425#ifdef USE_OTA_PASSWORD
449#ifdef USE_OTA_ENCRYPTION
502 char *sbuf =
reinterpret_cast<char *
>(buf);
505#ifdef ESPHOME_OTA_UDP
507 this->
udp_ =
nullptr;
514 this->
client_->setsockopt(SOL_SOCKET, SO_RCVTIMEO, &tv,
sizeof(tv));
515 this->
client_->setsockopt(SOL_SOCKET, SO_SNDTIMEO, &tv,
sizeof(tv));
516 if (this->
client_->setblocking(
true) != 0) {
524#ifdef ESPHOME_OTA_UDP
535 if (buf[0] == UDP_REQUEST_V1) {
537 if (udp_link.
reply[0] == 0) {
538 ESP_LOGW(TAG,
"UDP unavailable, using TCP");
543 if (udp_link.
reply[0] != 0) {
544 this->
udp_ = &udp_link;
566 ESP_LOGV(TAG,
"OTA type is 0x%02x", ota_type);
577#ifdef USE_OTA_DEFLATE
578 if (this->
inflate_ !=
nullptr && !this->
read_size_(buf, image_size, LOG_STR(
"image size"))) {
584#ifndef USE_OTA_PARTITIONS
597#ifdef USE_OTA_STATE_LISTENER
602 error_code = this->
backend_->begin(image_size, ota_type);
616 ESP_LOGV(TAG,
"Update: Binary MD5 is %s", sbuf);
617 this->
backend_->set_update_md5(sbuf);
623#ifdef USE_OTA_DEFLATE
649 ESP_LOGW(TAG,
"End update err %d", error_code);
656#ifdef ESPHOME_OTA_UDP
658 if (this->
udp_ !=
nullptr) {
670 ESP_LOGI(TAG,
"Update complete");
672#ifdef USE_OTA_STATE_LISTENER
676#ifdef USE_OTA_PARTITIONS
687#ifdef ESPHOME_OTA_UDP
703#ifdef USE_OTA_STATE_LISTENER
711 while (
len - at > 0) {
713 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
714 ESP_LOGW(TAG,
"Timeout reading %zu bytes",
len);
720 const int err = errno;
722 ESP_LOGW(TAG,
"Read err %zu bytes, errno %d",
len, err);
725 }
else if (read == 0) {
729 ESP_LOGW(TAG,
"Remote closed after %u of %zu bytes", (
unsigned) at,
len);
743#ifdef ESPHOME_OTA_UDP
744 if (this->
udp_ !=
nullptr)
749 while (
len - at > 0) {
751 if (now - start > OTA_SOCKET_TIMEOUT_DATA) {
752 ESP_LOGW(TAG,
"Timeout writing %zu bytes",
len);
758 const int err = errno;
760 ESP_LOGW(TAG,
"Write err %zu bytes, errno %d",
len, err);
777 ESP_LOGW(TAG,
"Socket %s: errno %d", LOG_STR_ARG(msg), errno);
784 ESP_LOGD(TAG,
"Client left after the handshake");
794 ESP_LOGW(TAG,
"Read %s failed", LOG_STR_ARG(what));
798 char peername[socket::SOCKADDR_STR_LEN];
799#ifdef ESPHOME_OTA_UDP
800 if (this->
client_ ==
nullptr) {
802#ifdef ESPHOME_OTA_UDP_LWIP
803 ipaddr_ntoa_r(&this->
udp_->
peer_ip, peername,
sizeof(peername));
811 this->
client_->getpeername_to(peername);
813 ESP_LOGD(TAG,
"Starting %s from %s", LOG_STR_ARG(phase), peername);
817 ESP_LOGW(TAG,
"Remote closed at %s", LOG_STR_ARG(during));
863 this->handshake_buf_pos_ += read;
865 return this->handshake_buf_pos_ >= to_read;
877 this->handshake_buf_pos_ +=
written;
879 return this->handshake_buf_pos_ >= to_write;
886 ESP_LOGV(TAG,
"%s is %zu bytes", LOG_STR_ARG(desc),
size);
893 ESP_LOGW(TAG,
"Flash write err %d", result);
908 ESP_LOGW(TAG,
"No data received for %u ms", (
unsigned) OTA_SOCKET_TIMEOUT_DATA);
911#ifdef USE_OTA_ENCRYPTION
912 if (this->
noise_ !=
nullptr) {
946 ESP_LOGD(TAG,
"Progress: %0.1f%%", percentage);
947#ifdef USE_OTA_STATE_LISTENER
957#if USE_OTA_VERSION == 2
958#ifdef ESPHOME_OTA_UDP
960 if (this->
udp_ !=
nullptr)
972#ifdef ESPHOME_OTA_UDP
973 this->
udp_ =
nullptr;
985#ifdef USE_OTA_PASSWORD
988#ifdef USE_OTA_ENCRYPTION
991#ifdef USE_OTA_DEFLATE
1004#ifdef USE_OTA_PASSWORD
1008 bool client_supports_sha256 = (this->
ota_features_ & CLIENT_FEATURE_SUPPORTS_SHA256_AUTH) != 0;
1011 if (!client_supports_sha256) {
1045 const size_t hex_size = hasher.
get_size() * 2;
1046 const size_t nonce_len = hasher.
get_size() / 4;
1047 const size_t auth_buf_size = 1 + 3 * hex_size;
1058 char *buf =
reinterpret_cast<char *
>(this->
auth_buf_.get() + 1);
1059 if (!
random_bytes(
reinterpret_cast<uint8_t *
>(buf), nonce_len)) {
1066 hasher.
add(buf, nonce_len);
1071 ESP_LOGV(TAG,
"Auth: Nonce is %.*s", (
int) hex_size, buf);
1076 const size_t to_write = 1 + hex_size;
1084 this->auth_buf_pos_ +=
written;
1087 if (this->auth_buf_pos_ < to_write) {
1092 this->auth_buf_pos_ = 0;
1098 const size_t to_read = hex_size * 2;
1102 size_t cnonce_offset = 1 + hex_size;
1110 this->auth_buf_pos_ += read;
1113 if (this->auth_buf_pos_ < to_read) {
1118 const char *nonce =
reinterpret_cast<char *
>(this->
auth_buf_.get() + 1);
1119 const char *cnonce = nonce + hex_size;
1120 const char *response = cnonce + hex_size;
1127 hasher.
add(this->
password_.c_str(), this->password_.length());
1128 hasher.
add(nonce, hex_size * 2);
1131 ESP_LOGV(TAG,
"Auth: CNonce is %.*s", (
int) hex_size, cnonce);
1132#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
1134 hasher.
get_hex(computed_hash);
1135 ESP_LOGV(TAG,
"Auth: Result is %.*s", (
int) hex_size, computed_hash);
1137 ESP_LOGV(TAG,
"Auth: Response is %.*s", (
int) hex_size, response);
void feed_wdt()
Feed the task watchdog.
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void mark_failed()
Mark this component as failed.
void status_set_warning()
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void disable_loop()
Disable this component's loop.
void status_momentary_error(uint32_t length=5000)
Set error status flag and clear it after length ms.
void status_clear_warning()
ESPHomeOTAComponent provides a simple way to integrate Over-the-Air updates into your app using Ardui...
static constexpr size_t OTA_BUFFER_SIZE
bool handle_noise_handshake_()
Drive the non-blocking handshake from loop(); returns true once the transport ciphers are ready.
ssize_t receive_data_(uint8_t *buf, DataTransfer &xfer)
bool would_block_(int error_code) const
uint32_t running_app_offset_
uint8_t handshake_buf_pos_
static constexpr size_t SHA256_HEX_SIZE
static constexpr uint8_t MAGIC_BYTES[5]
bool udp_send_(const uint8_t *data, size_t len)
ota::OTAResponseTypes write_flash_(uint8_t *data, size_t len)
void yield_and_feed_watchdog_()
bool writeall_(const uint8_t *buf, size_t len)
bool try_read_(size_t to_read, const LogString *desc)
bool data_readall_(uint8_t *buf, size_t len)
noise::NoiseContext noise_ctx_
ssize_t transport_read_(uint8_t *buf, size_t len)
void ack_written_(DataTransfer &xfer)
bool noise_start_session_(uint8_t server_feature_flags)
Start the responder handshake, on the session reserved at offer time.
ota::OTABackendPtr backend_
bool try_write_(size_t to_write, const LogString *desc)
void dump_config() override
bool noise_offered_() const
RAMUniquePtr< NoiseSession > noise_
bool extended_proto_() const
bool handle_write_error_(ssize_t written, const LogString *desc)
void send_chunk_acks_(DataTransfer &xfer)
bool data_write_byte_(uint8_t byte)
void log_auth_warning_(const LogString *msg)
uint32_t client_connect_time_
RAMUniquePtr< InflateSession > inflate_
float get_setup_priority() const override
bool client_left_before_start_()
void send_error_and_cleanup_(ota::OTAResponseTypes error)
bool handle_read_error_(ssize_t read, const LogString *desc)
ssize_t noise_read_data_(uint8_t *buf, size_t capacity)
Blocking read of one data-phase frame, decrypted in place; returns the plaintext size,...
void log_read_error_(const LogString *what)
bool readall_(uint8_t *buf, size_t len)
bool data_write_(const uint8_t *data, size_t len)
RAMUniquePtr< uint8_t[]> auth_buf_
RAMUniquePtr< noise::psk_t > saved_psk_
void ack_received_(DataTransfer &xfer)
uint8_t handshake_buf_[HANDSHAKE_BUF_SIZE]
static constexpr size_t HANDSHAKE_BUF_SIZE
ota::OTAResponseTypes inflate_data_(uint8_t *in, size_t image_size, DataTransfer &xfer)
const noise::NoiseContext & noise_context_() const
void server_failed_(const LogString *msg)
void transition_ota_state_(OTAState next_state)
void cleanup_connection_()
bool read_size_(uint8_t *buf, size_t &size, const LogString *desc)
void noise_reserve_session_()
socket::ListenSocket * server_
void log_remote_closed_(const LogString *during)
std::unique_ptr< socket::Socket > client_
void udp_open_(UdpLink &link)
void log_start_(const LogString *phase)
void log_socket_error_(const LogString *msg)
void get_hex(char *output)
Retrieve the hash as hex characters. Output buffer must hold get_size() * 2 + 1 bytes.
bool equals_hex(const char *expected)
Compare the hash against a provided hex-encoded hash of get_size() * 2 characters,...
An STL allocator that uses SPI or internal RAM.
RAMUniquePtr< T > make_unique(Args &&...args)
Value initialize one T; empty on exhaustion.
RAMUniquePtr< T[]> make_unique_array_for_overwrite(size_t n)
n elements left uninitialized, as std::make_unique_for_overwrite does; empty on exhaustion,...
noise::NoiseContext & get_noise_ctx()
void set_psk(const uint8_t *psk)
psk points at 32 bytes that outlive the context (PROGMEM or caller owned RAM); nullptr means no key.
void notify_state_(OTAState state, float progress, uint8_t error)
SHA256 hash implementation.
void calculate() override
size_t get_size() const override
Get the size of the hash in bytes (32 for SHA256)
void add(const uint8_t *data, size_t len) override
int setblocking(bool blocking)
bool ready() const
Check if the socket has buffered data ready to read.
int bind(const struct sockaddr *addr, socklen_t addrlen)
int setsockopt(int level, int optname, const void *optval, socklen_t optlen)
std::unique_ptr< BSDSocketImpl > accept_loop_monitored(struct sockaddr *addr, socklen_t *addrlen)
struct lwip_sock * esphome_lwip_get_sock(int fd)
Look up a LwIP socket struct from a file descriptor.
void esphome_fast_select_set_ota_listener_sock(struct lwip_sock *sock)
Set the listener netconn that the fast-select callback filters OTA wakes against.
APIServer * global_api_server
bool load_saved_noise_psk(noise::psk_t &out)
One-shot read of the provisioned key for a boot without an api server (safe mode); false when there i...
ESPHOME_LOG_TAG(TAG, CRASH_LOG_TAG)
const char * get_use_address_to(std::span< char, USE_ADDRESS_BUFFER_SIZE > buf)
Get the active network address for logging.
std::array< uint8_t, 32 > psk_t
@ OTA_TYPE_UPDATE_PARTITION_TABLE
void get_running_app_position(uint32_t &offset, size_t &size)
@ OTA_RESPONSE_UPDATE_PREPARE_OK
@ OTA_RESPONSE_ERROR_ENCRYPTION_REQUIRED
@ OTA_RESPONSE_SUPPORTS_COMPRESSION
@ OTA_RESPONSE_BIN_MD5_OK
@ OTA_RESPONSE_UPDATE_END_OK
@ OTA_RESPONSE_RECEIVE_OK
@ OTA_RESPONSE_FEATURE_FLAGS
@ OTA_RESPONSE_ERROR_UNSUPPORTED_OTA_TYPE
@ OTA_RESPONSE_ERROR_AUTH_INVALID
@ OTA_RESPONSE_ERROR_UNKNOWN
@ OTA_RESPONSE_REQUEST_SHA256_AUTH
@ OTA_RESPONSE_ERROR_MAGIC
std::unique_ptr< ArduinoLibreTinyOTABackend > make_ota_backend()
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
size_t format_sockaddr_to(const struct sockaddr *addr_ptr, socklen_t len, std::span< char, SOCKADDR_STR_LEN > buf)
Format sockaddr into caller-provided buffer, returns length written (excluding null)
socklen_t set_sockaddr_any(struct sockaddr *addr, socklen_t addrlen, uint16_t port)
Set a sockaddr to the any address and specified port for the IP version used by socket_ip().
std::unique_ptr< ListenSocket > socket_ip_loop_monitored(int type, int protocol)
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
void esphome_wake_ota_component_any_context()
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
struct sockaddr_storage peer