ESPHome 2026.8.0-dev
Loading...
Searching...
No Matches
api_frame_helper_noise.cpp
Go to the documentation of this file.
2#ifdef USE_API
3#ifdef USE_API_NOISE
4#include "api_connection.h" // For ClientInfo struct
7#include "esphome/core/hal.h"
9#include "esphome/core/log.h"
10#include "proto.h"
11#include <cstring>
12#include <cinttypes>
13
14#ifdef USE_ESP8266
15#include <pgmspace.h>
16#endif
17
18namespace esphome::api {
19
20static const char *const TAG = "api.noise";
21#ifdef USE_ESP8266
22static constexpr char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
23#else
24static const char *const PROLOGUE_INIT = "NoiseAPIInit";
25#endif
26static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
27
28// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
29static constexpr size_t API_MAX_LOG_BYTES = 168;
30
31#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
32#define HELPER_LOG(msg, ...) \
33 do { \
34 char peername_buf[socket::SOCKADDR_STR_LEN]; \
35 this->get_peername_to(peername_buf); \
36 ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
37 } while (0)
38#else
39#define HELPER_LOG(msg, ...) ((void) 0)
40#endif
41
42#ifdef HELPER_LOG_PACKETS
43#define LOG_PACKET_RECEIVED(buffer) \
44 do { \
45 char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
46 ESP_LOGVV(TAG, "Received frame: %s", \
47 format_hex_pretty_to(hex_buf_, (buffer).data(), \
48 (buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
49 } while (0)
50#else
51#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
52#endif
53
55const LogString *noise_err_to_logstr(int err) {
56 if (err == NOISE_ERROR_NO_MEMORY)
57 return LOG_STR("NO_MEMORY");
58 if (err == NOISE_ERROR_UNKNOWN_ID)
59 return LOG_STR("UNKNOWN_ID");
60 if (err == NOISE_ERROR_UNKNOWN_NAME)
61 return LOG_STR("UNKNOWN_NAME");
62 if (err == NOISE_ERROR_MAC_FAILURE)
63 return LOG_STR("MAC_FAILURE");
64 if (err == NOISE_ERROR_NOT_APPLICABLE)
65 return LOG_STR("NOT_APPLICABLE");
66 if (err == NOISE_ERROR_SYSTEM)
67 return LOG_STR("SYSTEM");
68 if (err == NOISE_ERROR_REMOTE_KEY_REQUIRED)
69 return LOG_STR("REMOTE_KEY_REQUIRED");
70 if (err == NOISE_ERROR_LOCAL_KEY_REQUIRED)
71 return LOG_STR("LOCAL_KEY_REQUIRED");
72 if (err == NOISE_ERROR_PSK_REQUIRED)
73 return LOG_STR("PSK_REQUIRED");
74 if (err == NOISE_ERROR_INVALID_LENGTH)
75 return LOG_STR("INVALID_LENGTH");
76 if (err == NOISE_ERROR_INVALID_PARAM)
77 return LOG_STR("INVALID_PARAM");
78 if (err == NOISE_ERROR_INVALID_STATE)
79 return LOG_STR("INVALID_STATE");
80 if (err == NOISE_ERROR_INVALID_NONCE)
81 return LOG_STR("INVALID_NONCE");
82 if (err == NOISE_ERROR_INVALID_PRIVATE_KEY)
83 return LOG_STR("INVALID_PRIVATE_KEY");
84 if (err == NOISE_ERROR_INVALID_PUBLIC_KEY)
85 return LOG_STR("INVALID_PUBLIC_KEY");
86 if (err == NOISE_ERROR_INVALID_FORMAT)
87 return LOG_STR("INVALID_FORMAT");
88 if (err == NOISE_ERROR_INVALID_SIGNATURE)
89 return LOG_STR("INVALID_SIGNATURE");
90 return LOG_STR("UNKNOWN");
91}
92
95 APIError err = init_common_();
96 if (err != APIError::OK) {
97 return err;
98 }
99
100 // init prologue
101 size_t old_size = prologue_.size();
102 prologue_.resize(old_size + PROLOGUE_INIT_LEN);
103#ifdef USE_ESP8266
104 memcpy_P(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
105#else
106 std::memcpy(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
107#endif
108
110 return APIError::OK;
111}
112#ifdef USE_API_PLAINTEXT
113APIError APINoiseFrameHelper::init_from_handoff(const uint8_t *header, uint8_t header_len) {
114 APIError err = this->init();
115 if (err != APIError::OK) {
116 return err;
117 }
118 // Seed the header bytes the plaintext helper consumed before detecting the
119 // Noise indicator; try_read_frame_ resumes from rx_header_buf_len_.
120 std::memcpy(this->rx_header_buf_, header, header_len);
121 this->rx_header_buf_len_ = header_len;
122 // Pump the handshake without gating on socket_->ready(): on LWIP the
123 // plaintext helper's partial read can drain rcvevent while the rest of the
124 // client hello sits in the lastdata cache, so ready() may report false even
125 // though data is available.
126 return this->pump_handshake_();
127}
128#endif // USE_API_PLAINTEXT
129
134 while (this->state_ != State::DATA) {
135 APIError err = this->state_action_();
136 if (err == APIError::WOULD_BLOCK) {
137 break;
138 }
139 if (err != APIError::OK) {
140 return err;
141 }
142 }
143 return APIError::OK;
144}
145
146// Helper for handling handshake frame errors
148 if (aerr == APIError::BAD_INDICATOR) {
149 send_explicit_handshake_reject_(LOG_STR("Bad indicator byte"));
150 } else if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
151 send_explicit_handshake_reject_(LOG_STR("Bad handshake packet len"));
152 }
153 return aerr;
154}
155
156// Helper for handling noise library errors
157APIError APINoiseFrameHelper::handle_noise_error_(int err, const LogString *func_name, APIError api_err) {
158 if (err != 0) {
160 HELPER_LOG("%s failed: %s", LOG_STR_ARG(func_name), LOG_STR_ARG(noise_err_to_logstr(err)));
161 return api_err;
162 }
163 return APIError::OK;
164}
165
168 // Check ready() once, not per state transition. On ESP8266 LWIP raw TCP,
169 // ready() returns false once the rx buffer is consumed. Re-checking each
170 // iteration would block handshake writes that must follow reads,
171 // deadlocking the handshake. pump_handshake_() stops on WOULD_BLOCK when
172 // no more data is available to read.
173 if (state_ != State::DATA && this->socket_->ready()) {
174 APIError err = this->pump_handshake_();
175 if (err != APIError::OK) {
176 return err;
177 }
178 }
179
180 if (!this->overflow_buf_.empty()) [[unlikely]] {
182 }
183 return APIError::OK;
184}
185
196 // read header
197 if (rx_header_buf_len_ < 3) {
198 // no header information yet
199 uint8_t to_read = 3 - rx_header_buf_len_;
200 ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
201 APIError err = handle_socket_read_result_(received);
202 if (err != APIError::OK) {
203 return err;
204 }
205 rx_header_buf_len_ += static_cast<uint8_t>(received);
206 if (static_cast<uint8_t>(received) != to_read) {
207 // not a full read
209 }
210
211 if (rx_header_buf_[0] != 0x01) {
213 HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
215 }
216 // header reading done
217 }
218
219 // read body
220 uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
221
222 // Check against size limits to prevent OOM: MAX_HANDSHAKE_SIZE for handshake, MAX_MESSAGE_SIZE for data
223 bool is_data = (state_ == State::DATA);
224 uint16_t limit = is_data ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE;
225 if (msg_size > limit) {
227 HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, limit);
229 }
230
231 // Reserve space for body (+ null terminator in DATA state so protobuf
232 // StringRef fields can be safely null-terminated in-place after decode.
233 // During handshake, rx_buf_.size() is used in prologue construction, so
234 // the buffer must be exactly msg_size to avoid prologue mismatch.)
235 uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0);
236 this->rx_buf_.resize(alloc_size);
237
238 if (rx_buf_len_ < msg_size) {
239 // more data to read
240 uint16_t to_read = msg_size - rx_buf_len_;
241 ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
242 APIError err = handle_socket_read_result_(received);
243 if (err != APIError::OK) {
244 return err;
245 }
246 rx_buf_len_ += static_cast<uint16_t>(received);
247 if (static_cast<uint16_t>(received) != to_read) {
248 // not all read
250 }
251 }
252
253 LOG_PACKET_RECEIVED(this->rx_buf_);
254
255 // Clear state for next frame (rx_buf_ still contains data for caller)
256 this->rx_buf_len_ = 0;
257 this->rx_header_buf_len_ = 0;
258
259 return APIError::OK;
260}
261
271// Split into per-state methods so the compiler doesn't allocate stack space
272// for all branches simultaneously. On RP2040 the core0 stack lives in a 4KB
273// scratch RAM bank; the Noise crypto path (curve25519) needs ~2KB+ of stack,
274// so every byte saved in the caller matters.
276 switch (this->state_) {
278 HELPER_LOG("Bad state for method: %d", (int) this->state_);
279 return APIError::BAD_STATE;
281 return this->state_action_client_hello_();
283 return this->state_action_server_hello_();
284 case State::HANDSHAKE:
285 return this->state_action_handshake_();
286 case State::CLOSED:
287 case State::FAILED:
288 return APIError::BAD_STATE;
289 default:
290 return APIError::OK;
291 }
292}
294 // waiting for client hello
295 APIError aerr = this->try_read_frame_();
296 if (aerr != APIError::OK) {
298 }
299 // ignore contents, may be used in future for flags
300 // Resize for: existing prologue + 2 size bytes + frame data
301 size_t old_size = this->prologue_.size();
302 size_t rx_size = this->rx_buf_.size();
303 this->prologue_.resize(old_size + 2 + rx_size);
304 this->prologue_[old_size] = (uint8_t) (rx_size >> 8);
305 this->prologue_[old_size + 1] = (uint8_t) rx_size;
306 if (rx_size > 0) {
307 std::memcpy(this->prologue_.data() + old_size + 2, this->rx_buf_.data(), rx_size);
308 }
309
311 return APIError::OK;
312}
314 // send server hello
315 const auto &name = App.get_name();
316 char mac[MAC_ADDRESS_BUFFER_SIZE];
318
319 // Calculate positions and sizes
320 size_t name_len = name.size() + 1; // including null terminator
321 size_t name_offset = 1;
322 size_t mac_offset = name_offset + name_len;
323 size_t total_size = 1 + name_len + MAC_ADDRESS_BUFFER_SIZE;
324
325 // 1 (proto) + name (max ESPHOME_DEVICE_NAME_MAX_LEN) + 1 (name null)
326 // + mac (MAC_ADDRESS_BUFFER_SIZE - 1) + 1 (mac null)
327 constexpr size_t max_msg_size = 1 + ESPHOME_DEVICE_NAME_MAX_LEN + 1 + MAC_ADDRESS_BUFFER_SIZE;
328 uint8_t msg[max_msg_size];
329
330 // chosen proto
331 msg[0] = 0x01;
332
333 // node name, terminated by null byte
334 std::memcpy(msg + name_offset, name.c_str(), name_len);
335 // node mac, terminated by null byte
336 std::memcpy(msg + mac_offset, mac, MAC_ADDRESS_BUFFER_SIZE);
337
338 APIError aerr = write_frame_(msg, total_size);
339 if (aerr != APIError::OK)
340 return aerr;
341
342 // start handshake
343 aerr = init_handshake_();
344 if (aerr != APIError::OK)
345 return aerr;
346
348 return APIError::OK;
349}
351 int action = noise_handshakestate_get_action(this->handshake_);
352 if (action == NOISE_ACTION_READ_MESSAGE) {
353 return this->state_action_handshake_read_();
354 } else if (action == NOISE_ACTION_WRITE_MESSAGE) {
355 return this->state_action_handshake_write_();
356 }
357 // bad state for action
358 this->state_ = State::FAILED;
359 HELPER_LOG("Bad action for handshake: %d", action);
361}
363 APIError aerr = this->try_read_frame_();
364 if (aerr != APIError::OK) {
365 return this->handle_handshake_frame_error_(aerr);
366 }
367
368 if (this->rx_buf_.empty()) {
369 this->send_explicit_handshake_reject_(LOG_STR("Empty handshake message"));
371 } else if (this->rx_buf_[0] != 0x00) {
372 HELPER_LOG("Bad handshake error byte: %u", this->rx_buf_[0]);
373 this->send_explicit_handshake_reject_(LOG_STR("Bad handshake error byte"));
375 }
376
377 NoiseBuffer mbuf;
378 noise_buffer_init(mbuf);
379 noise_buffer_set_input(mbuf, this->rx_buf_.data() + 1, this->rx_buf_.size() - 1);
380 int err = noise_handshakestate_read_message(this->handshake_, &mbuf, nullptr);
381 if (err != 0) {
382 // Special handling for MAC failure
383 this->send_explicit_handshake_reject_(err == NOISE_ERROR_MAC_FAILURE ? LOG_STR("Handshake MAC failure")
384 : LOG_STR("Handshake error"));
385 return this->handle_noise_error_(err, LOG_STR("noise_handshakestate_read_message"),
387 }
388
389 return this->check_handshake_finished_();
390}
392 uint8_t buffer[65];
393 NoiseBuffer mbuf;
394 noise_buffer_init(mbuf);
395 noise_buffer_set_output(mbuf, buffer + 1, sizeof(buffer) - 1);
396
397 int err = noise_handshakestate_write_message(this->handshake_, &mbuf, nullptr);
398 APIError aerr = this->handle_noise_error_(err, LOG_STR("noise_handshakestate_write_message"),
400 if (aerr != APIError::OK)
401 return aerr;
402 buffer[0] = 0x00; // success
403
404 aerr = this->write_frame_(buffer, mbuf.size + 1);
405 if (aerr != APIError::OK)
406 return aerr;
407 return this->check_handshake_finished_();
408}
410 // Max reject message: "Bad handshake packet len" (24) + 1 (failure byte) = 25 bytes
411 uint8_t data[32];
412 data[0] = 0x01; // failure
413
414#ifdef USE_STORE_LOG_STR_IN_FLASH
415 // On ESP8266 with flash strings, we need to use PROGMEM-aware functions
416 size_t reason_len = strlen_P(reinterpret_cast<PGM_P>(reason));
417 reason_len = std::min(reason_len, sizeof(data) - 1);
418 if (reason_len > 0) {
419 memcpy_P(data + 1, reinterpret_cast<PGM_P>(reason), reason_len);
420 }
421#else
422 // Normal memory access
423 const char *reason_str = LOG_STR_ARG(reason);
424 size_t reason_len = strlen(reason_str);
425 reason_len = std::min(reason_len, sizeof(data) - 1);
426 if (reason_len > 0) {
427 // NOLINTNEXTLINE(bugprone-not-null-terminated-result) - binary protocol, not a C string
428 std::memcpy(data + 1, reason_str, reason_len);
429 }
430#endif
431
432 size_t data_size = reason_len + 1;
433
434 // temporarily remove failed state
435 auto orig_state = state_;
437 write_frame_(data, data_size);
438 state_ = orig_state;
439}
441 APIError aerr = this->check_data_state_();
442 if (aerr != APIError::OK)
443 return aerr;
444
445 aerr = this->try_read_frame_();
446 if (aerr != APIError::OK)
447 return aerr;
448
449 NoiseBuffer mbuf;
450 noise_buffer_init(mbuf);
451 // read_packet() must only be called in DATA state; the extra
452 // RX_BUF_NULL_TERMINATOR byte is only allocated in DATA state
453 // (see try_read_frame_), so calling this during handshake would
454 // underflow the size calculation below.
455#ifdef ESPHOME_DEBUG_API
456 assert(this->state_ == State::DATA);
457#endif
458 // rx_buf_ has RX_BUF_NULL_TERMINATOR extra byte for null termination
459 // (only added in DATA state — see try_read_frame_), so subtract it
460 // to get the actual encrypted data size for decryption.
461 size_t encrypted_size = this->rx_buf_.size() - RX_BUF_NULL_TERMINATOR;
462 noise_buffer_set_inout(mbuf, this->rx_buf_.data(), encrypted_size, encrypted_size);
463 int err = noise_cipherstate_decrypt(this->recv_cipher_, &mbuf);
464 APIError decrypt_err =
465 handle_noise_error_(err, LOG_STR("noise_cipherstate_decrypt"), APIError::CIPHERSTATE_DECRYPT_FAILED);
466 if (decrypt_err != APIError::OK) {
467 return decrypt_err;
468 }
469
470 uint16_t msg_size = mbuf.size;
471 uint8_t *msg_data = this->rx_buf_.data();
472 if (msg_size < 4) {
473 this->state_ = State::FAILED;
474 HELPER_LOG("Bad data packet: size %d too short", msg_size);
476 }
477
478 uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
479 uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
480 if (data_len > msg_size - 4) {
481 this->state_ = State::FAILED;
482 HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
484 }
485
486 buffer->data = msg_data + 4; // Skip 4-byte header (type + length)
487 buffer->data_len = data_len;
488 buffer->type = type;
489 return APIError::OK;
490}
491// Encrypt a single noise message in place and return the encrypted frame length.
492// Returns APIError::OK on success.
493APIError APINoiseFrameHelper::encrypt_noise_message_(uint8_t *buf_start, uint16_t payload_size, uint8_t message_type,
494 uint16_t &encrypted_len_out) {
495 // Write noise header
496 buf_start[0] = 0x01; // indicator
497 // buf_start[1], buf_start[2] to be set after encryption
498
499 // Write message header (to be encrypted)
500 constexpr uint8_t msg_offset = 3;
501 buf_start[msg_offset] = static_cast<uint8_t>(message_type >> 8); // type high byte
502 buf_start[msg_offset + 1] = static_cast<uint8_t>(message_type); // type low byte
503 buf_start[msg_offset + 2] = static_cast<uint8_t>(payload_size >> 8); // data_len high byte
504 buf_start[msg_offset + 3] = static_cast<uint8_t>(payload_size); // data_len low byte
505 // payload data is already in the buffer starting at offset + 7
506
507 // Encrypt the message in place
508 NoiseBuffer mbuf;
509 noise_buffer_init(mbuf);
510 noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + payload_size, 4 + payload_size + this->frame_footer_size_);
511
512 int err = noise_cipherstate_encrypt(this->send_cipher_, &mbuf);
513 APIError aerr =
514 this->handle_noise_error_(err, LOG_STR("noise_cipherstate_encrypt"), APIError::CIPHERSTATE_ENCRYPT_FAILED);
515 if (aerr != APIError::OK)
516 return aerr;
517
518 // Fill in the encrypted size
519 buf_start[1] = static_cast<uint8_t>(mbuf.size >> 8);
520 buf_start[2] = static_cast<uint8_t>(mbuf.size);
521
522 encrypted_len_out = static_cast<uint16_t>(3 + mbuf.size); // indicator + size + encrypted data
523 return APIError::OK;
524}
525
527#ifdef ESPHOME_DEBUG_API
528 assert(this->state_ == State::DATA);
529#endif
530
531 // Resize buffer to include footer space for Noise MAC
532 if (this->frame_footer_size_)
533 buffer.get_buffer()->resize(buffer.get_buffer()->size() + this->frame_footer_size_);
534
535 uint16_t payload_size =
536 static_cast<uint16_t>(buffer.get_buffer()->size() - HEADER_PADDING - this->frame_footer_size_);
537 uint8_t *buf_start = buffer.get_buffer()->data();
538 uint16_t encrypted_len;
539 APIError aerr = this->encrypt_noise_message_(buf_start, payload_size, type, encrypted_len);
540 if (aerr != APIError::OK)
541 return aerr;
542 return this->write_raw_fast_buf_(buf_start, encrypted_len);
543}
544
545APIError APINoiseFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) {
546#ifdef ESPHOME_DEBUG_API
547 assert(this->state_ == State::DATA);
548 assert(!messages.empty());
549#endif
550
551 // Noise messages are already contiguous in the buffer:
552 // HEADER_PADDING (7) exactly matches the fixed header size, and
553 // footer space (16) is consumed by the encryption MAC.
554 uint8_t *buffer_data = buffer.get_buffer()->data();
555 uint8_t *write_start = buffer_data + messages[0].offset;
556 uint16_t total_write_len = 0;
557
558 for (const auto &msg : messages) {
559 uint8_t *buf_start = buffer_data + msg.offset;
560 uint16_t encrypted_len;
561 APIError aerr = this->encrypt_noise_message_(buf_start, msg.payload_size, msg.message_type, encrypted_len);
562 if (aerr != APIError::OK)
563 return aerr;
564 total_write_len += encrypted_len;
565 }
566
567 return this->write_raw_fast_buf_(write_start, total_write_len);
568}
569
570APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
571 uint8_t header[3];
572 header[0] = 0x01; // indicator
573 header[1] = (uint8_t) (len >> 8);
574 header[2] = (uint8_t) len;
575
576 if (len == 0) {
577 return this->write_raw_buf_(header, 3);
578 }
579 struct iovec iov[2];
580 iov[0].iov_base = header;
581 iov[0].iov_len = 3;
582 iov[1].iov_base = const_cast<uint8_t *>(data);
583 iov[1].iov_len = len;
584
585 return this->write_raw_iov_(iov, 2, 3 + len);
586}
587
593 int err;
594 memset(&nid_, 0, sizeof(nid_));
595 // const char *proto = "Noise_NNpsk0_25519_ChaChaPoly_SHA256";
596 // err = noise_protocol_name_to_id(&nid_, proto, strlen(proto));
597 nid_.pattern_id = NOISE_PATTERN_NN;
598 nid_.cipher_id = NOISE_CIPHER_CHACHAPOLY;
599 nid_.dh_id = NOISE_DH_CURVE25519;
600 nid_.prefix_id = NOISE_PREFIX_STANDARD;
601 nid_.hybrid_id = NOISE_DH_NONE;
602 nid_.hash_id = NOISE_HASH_SHA256;
603 nid_.modifier_ids[0] = NOISE_MODIFIER_PSK0;
604
605 err = noise_handshakestate_new_by_id(&handshake_, &nid_, NOISE_ROLE_RESPONDER);
606 APIError aerr =
607 handle_noise_error_(err, LOG_STR("noise_handshakestate_new_by_id"), APIError::HANDSHAKESTATE_SETUP_FAILED);
608 if (aerr != APIError::OK)
609 return aerr;
610
611 const auto &psk = this->ctx_.get_psk();
612 err = noise_handshakestate_set_pre_shared_key(handshake_, psk.data(), psk.size());
613 aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_set_pre_shared_key"),
615 if (aerr != APIError::OK)
616 return aerr;
617
618 err = noise_handshakestate_set_prologue(handshake_, prologue_.data(), prologue_.size());
619 aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_set_prologue"), APIError::HANDSHAKESTATE_SETUP_FAILED);
620 if (aerr != APIError::OK)
621 return aerr;
622 // set_prologue copies it into handshakestate, so we can get rid of it now
624
625 err = noise_handshakestate_start(handshake_);
626 aerr = handle_noise_error_(err, LOG_STR("noise_handshakestate_start"), APIError::HANDSHAKESTATE_SETUP_FAILED);
627 if (aerr != APIError::OK)
628 return aerr;
629 return APIError::OK;
630}
631
633#ifdef ESPHOME_DEBUG_API
634 assert(state_ == State::HANDSHAKE);
635#endif
636
637 int action = noise_handshakestate_get_action(handshake_);
638 if (action == NOISE_ACTION_READ_MESSAGE || action == NOISE_ACTION_WRITE_MESSAGE)
639 return APIError::OK;
640 if (action != NOISE_ACTION_SPLIT) {
642 HELPER_LOG("Bad action for handshake: %d", action);
644 }
645 int err = noise_handshakestate_split(handshake_, &send_cipher_, &recv_cipher_);
646 APIError aerr =
647 handle_noise_error_(err, LOG_STR("noise_handshakestate_split"), APIError::HANDSHAKESTATE_SPLIT_FAILED);
648 if (aerr != APIError::OK)
649 return aerr;
650
651 this->frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
652
653 HELPER_LOG("Handshake complete!");
654 noise_handshakestate_free(handshake_);
655 handshake_ = nullptr;
657 return APIError::OK;
658}
659
661 if (handshake_ != nullptr) {
662 noise_handshakestate_free(handshake_);
663 handshake_ = nullptr;
664 }
665 if (send_cipher_ != nullptr) {
666 noise_cipherstate_free(send_cipher_);
667 send_cipher_ = nullptr;
668 }
669 if (recv_cipher_ != nullptr) {
670 noise_cipherstate_free(recv_cipher_);
671 recv_cipher_ = nullptr;
672 }
673}
674
675extern "C" {
676// declare how noise generates random bytes (here with a good HWRNG based on the RF system)
677void noise_rand_bytes(void *output, size_t len) {
678 if (!esphome::random_bytes(reinterpret_cast<uint8_t *>(output), len)) {
679 ESP_LOGE(TAG, "Acquiring random bytes failed; rebooting");
680 arch_restart();
681 }
682}
683}
684
685} // namespace esphome::api
686#endif // USE_API_NOISE
687#endif // USE_API
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
void release()
Release all memory (equivalent to std::vector swap trick).
Definition api_buffer.h:60
size_t size() const
Definition api_buffer.h:55
void resize(size_t n) ESPHOME_ALWAYS_INLINE
Definition api_buffer.h:43
APIError handle_socket_read_result_(ssize_t received)
APIError ESPHOME_ALWAYS_INLINE write_raw_fast_buf_(const void *data, uint16_t len)
APIError write_raw_buf_(const void *data, uint16_t len, ssize_t sent=WRITE_NOT_ATTEMPTED)
APIError write_raw_iov_(const struct iovec *iov, int iovcnt, uint16_t total_write_len, ssize_t sent=WRITE_NOT_ATTEMPTED)
std::unique_ptr< socket::Socket > socket_
APIError ESPHOME_ALWAYS_INLINE check_data_state_() const
const psk_t & get_psk() const
APIError write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) override
APIError pump_handshake_()
Drive the handshake state machine until DATA, WOULD_BLOCK, or a fatal error.
APIError read_packet(ReadPacketBuffer *buffer) override
APIError handle_noise_error_(int err, const LogString *func_name, APIError api_err)
APIError write_protobuf_messages(ProtoWriteBuffer buffer, std::span< const MessageInfo > messages) override
APIError state_action_()
To be called from read/write methods.
APIError try_read_frame_()
Read a packet into the rx_buf_.
APIError loop() override
Run through handshake messages (if in that phase)
APIError encrypt_noise_message_(uint8_t *buf_start, uint16_t payload_size, uint8_t message_type, uint16_t &encrypted_len_out)
APIError handle_handshake_frame_error_(APIError aerr)
void send_explicit_handshake_reject_(const LogString *reason)
APIError write_frame_(const uint8_t *data, uint16_t len)
APIError init() override
Initialize the frame helper, returns OK if successful.
APIError init_handshake_()
Initiate the data structures for the handshake.
APIError init_from_handoff(const uint8_t *header, uint8_t header_len)
bool empty() const
True when no backlogged data is waiting.
APIBuffer * get_buffer() const
Definition proto.h:271
uint16_t type
__int64 ssize_t
Definition httplib.h:178
void noise_rand_bytes(void *output, size_t len)
const LogString * noise_err_to_logstr(int err)
Convert a noise error code to a readable error.
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
Definition helpers.cpp:20
const void size_t len
Definition hal.h:64
uint16_t size
Definition helpers.cpp:25
void get_mac_address_into_buffer(std::span< char, MAC_ADDRESS_BUFFER_SIZE > buf)
Get the device MAC address into the given buffer, in lowercase hex notation.
Definition helpers.cpp:744
void arch_restart()
Definition hal.cpp:39
Application App
Global storage of Application pointer - only one Application can exist.
void * iov_base
Definition headers.h:103
size_t iov_len
Definition headers.h:104
uint32_t payload_size()