22static_assert(MAX_HANDSHAKE_SIZE == noise::MAX_HANDSHAKE_SIZE,
23 "api and noise component handshake size limits must match");
26static constexpr char PROLOGUE_INIT[] PROGMEM =
"NoiseAPIInit";
27static constexpr size_t PROLOGUE_INIT_LEN = 12;
30static constexpr size_t API_MAX_LOG_BYTES = 168;
32#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
33#define HELPER_LOG(msg, ...) \
35 char peername_buf[socket::SOCKADDR_STR_LEN]; \
36 this->get_peername_to(peername_buf); \
37 ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
40#define HELPER_LOG(msg, ...) ((void) 0)
43#ifdef HELPER_LOG_PACKETS
44#define LOG_PACKET_RECEIVED(buffer) \
46 char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
47 ESP_LOGVV(TAG, "Received frame: %s", \
48 format_hex_pretty_to(hex_buf_, (buffer).data(), \
49 (buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
52#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
64 if (dst ==
nullptr) [[unlikely]] {
73#ifdef USE_API_OUTGOING_CONNECTION
80#ifdef USE_API_PLAINTEXT
128 HELPER_LOG(
"%s failed: %s", LOG_STR_ARG(func_name), LOG_STR_ARG(noise_err_to_logstr(err)));
167 uint8_t to_read =
static_cast<uint8_t
>(noise::FRAME_HEADER_SIZE) -
rx_header_buf_len_;
174 if (
static_cast<uint8_t
>(received) != to_read) {
192 uint16_t limit = is_data ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE;
193 if (msg_size > limit) {
195 HELPER_LOG(
"Bad packet: message size %u exceeds maximum %u", msg_size, limit);
203 uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0);
218 if (
static_cast<uint16_t
>(received) != to_read) {
224 LOG_PACKET_RECEIVED(this->
rx_buf_);
249 HELPER_LOG(
"Bad state for method: %d", (
int) this->
state_);
252#ifdef USE_API_OUTGOING_CONNECTION
277 if (dst ==
nullptr) [[unlikely]] {
281 dst[0] = (uint8_t) (rx_size >> 8);
282 dst[1] = (uint8_t) rx_size;
284 std::memcpy(dst + 2, this->
rx_buf_.
data(), rx_size);
287#ifdef USE_API_OUTGOING_CONNECTION
298 char mac[MAC_ADDRESS_BUFFER_SIZE];
302 size_t name_len = name.size() + 1;
303 size_t name_offset = 1;
304 size_t mac_offset = name_offset + name_len;
305 size_t total_size = 1 + name_len + MAC_ADDRESS_BUFFER_SIZE;
309 constexpr size_t max_msg_size = 1 + ESPHOME_DEVICE_NAME_MAX_LEN + 1 + MAC_ADDRESS_BUFFER_SIZE;
310 uint8_t msg[max_msg_size];
316 std::memcpy(msg + name_offset, name.c_str(), name_len);
318 std::memcpy(msg + mac_offset, mac, MAC_ADDRESS_BUFFER_SIZE);
344 HELPER_LOG(
"Bad action for handshake: %d", (
int) action);
356 }
else if (this->
rx_buf_[0] != noise::HANDSHAKE_STATUS_OK) {
357 HELPER_LOG(
"Bad handshake error byte: %u", this->
rx_buf_[0]);
381 buffer[0] = noise::HANDSHAKE_STATUS_OK;
391 static_assert(
sizeof(data) >= noise::MAC_FAILURE_PAYLOAD_SIZE,
392 "reject buffer must fit the MAC failure wire contract");
401 ESP_LOGW(TAG,
"Sending handshake reject failed: %d", (
int) aerr);
418 noise_buffer_init(mbuf);
423#ifdef ESPHOME_DEBUG_API
429 size_t encrypted_size = this->
rx_buf_.
size() - RX_BUF_NULL_TERMINATOR;
430 noise_buffer_set_inout(mbuf, this->
rx_buf_.
data(), encrypted_size, encrypted_size);
431 int err = noise_cipherstate_decrypt(this->
recv_cipher_, &mbuf);
438 uint16_t msg_size = mbuf.size;
442 HELPER_LOG(
"Bad data packet: size %d too short", msg_size);
446 uint16_t
type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
447 uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
448 if (data_len > msg_size - 4) {
450 HELPER_LOG(
"Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
454 buffer->data = msg_data + 4;
455 buffer->data_len = data_len;
462 uint16_t &encrypted_len_out) {
466 constexpr uint8_t msg_offset = noise::FRAME_HEADER_SIZE;
467 buf_start[msg_offset] =
static_cast<uint8_t
>(message_type >> 8);
468 buf_start[msg_offset + 1] =
static_cast<uint8_t
>(message_type);
469 buf_start[msg_offset + 2] =
static_cast<uint8_t
>(
payload_size >> 8);
470 buf_start[msg_offset + 3] =
static_cast<uint8_t
>(
payload_size);
475 noise_buffer_init(mbuf);
478 int err = noise_cipherstate_encrypt(this->
send_cipher_, &mbuf);
487 encrypted_len_out =
static_cast<uint16_t
>(noise::FRAME_HEADER_SIZE + mbuf.size);
492#ifdef ESPHOME_DEBUG_API
504 uint8_t *buf_start = buf->
data();
505 uint16_t encrypted_len;
513#ifdef ESPHOME_DEBUG_API
515 assert(!messages.empty());
521 uint8_t *buffer_data =
buffer.get_buffer()->data();
522 uint8_t *write_start = buffer_data + messages[0].offset;
523 uint16_t total_write_len = 0;
525 for (
const auto &msg : messages) {
526 uint8_t *buf_start = buffer_data + msg.offset;
527 uint16_t encrypted_len;
531 total_write_len += encrypted_len;
538 uint8_t header[noise::FRAME_HEADER_SIZE];
546 iov[0].
iov_len = noise::FRAME_HEADER_SIZE;
547 iov[1].
iov_base =
const_cast<uint8_t *
>(data);
568#ifdef ESPHOME_DEBUG_API
578 HELPER_LOG(
"Bad action for handshake: %d", (
int) action);
590 HELPER_LOG(
"Handshake complete!");
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
Byte buffer that skips zero-initialization on resize().
void release()
Release all memory (equivalent to std::vector swap trick).
uint8_t * append(size_t n)
Grow by n bytes; returns the new bytes, or nullptr on allocation failure.
bool resize(size_t n) ESPHOME_ALWAYS_INLINE
Returns false if allocation fails; the buffer is left unchanged. No zero-fill.
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)
APIOverflowBuffer overflow_buf_
std::unique_ptr< socket::Socket > socket_
uint8_t frame_footer_size_
APIError ESPHOME_ALWAYS_INLINE check_data_state_() const
APIError drain_overflow_and_handle_errors_()
uint8_t rx_header_buf_len_
APIError write_protobuf_packet(uint16_t type, ProtoWriteBuffer buffer) override
APIError encrypt_noise_message_(uint8_t *buf_start, uint16_t payload_size, uint16_t message_type, uint16_t &encrypted_len_out)
NoiseCipherState * send_cipher_
noise::NoiseContext & ctx_
APIError pump_handshake_()
Drive the handshake state machine until DATA, WOULD_BLOCK, or a fatal error.
APIError state_action_handshake_()
APIError read_packet(ReadPacketBuffer *buffer) override
NoiseCipherState * recv_cipher_
~APINoiseFrameHelper() override
uint8_t rx_header_buf_[noise::FRAME_HEADER_SIZE]
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 send_server_hello_frame_()
APIError state_action_handshake_write_()
APIError try_read_frame_()
Read a packet into the rx_buf_.
APIError loop() override
Run through handshake messages (if in that phase)
APIError start_handshake_()
static constexpr uint8_t HEADER_PADDING
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 state_action_handshake_read_()
APIError state_action_server_hello_()
APIError init() override
Initialize the frame helper, returns OK if successful.
APIError send_server_hello_first()
APIError state_action_client_hello_()
APIError check_handshake_finished_()
APIError init_handshake_()
Initiate the data structures for the handshake.
APIError init_from_handoff(const uint8_t *header, uint8_t header_len)
noise::NoiseResponderHandshake handshake_
bool empty() const
True when no backlogged data is waiting.
int write_message(uint8_t *out, size_t capacity, size_t &out_len)
Produce the next handshake message into out; out_len receives its size and is zero on error.
Action action() const
ACTION_FAILED is the catch-all: returned before init(), after split() has released the state,...
int init(const NoiseContext &ctx, const uint8_t *prologue, size_t prologue_len)
Create and start the handshake with the context's PSK and the prologue.
int read_message(uint8_t *data, size_t len)
Process one received handshake message.
int split(NoiseCipherState *&send_cipher, NoiseCipherState *&recv_cipher)
Hand out the transport ciphers and free the handshake state.
ESPHOME_LOG_TAG(TAG, "api.connection")
@ HANDSHAKESTATE_READ_FAILED
@ HANDSHAKESTATE_BAD_STATE
@ HANDSHAKESTATE_SPLIT_FAILED
@ BAD_HANDSHAKE_PACKET_LEN
@ BAD_HANDSHAKE_ERROR_BYTE
@ HANDSHAKESTATE_SETUP_FAILED
@ CIPHERSTATE_ENCRYPT_FAILED
@ CIPHERSTATE_DECRYPT_FAILED
@ HANDSHAKESTATE_WRITE_FAILED
void write_frame_header(uint8_t *buf, uint16_t payload_len)
const LogString * noise_err_to_logstr(int err)
Convert a noise error code to a readable error.
size_t format_reject_payload(uint8_t *buf, size_t capacity, const LogString *reason)
Fill buf with a handshake reject payload (status byte plus the reason text, PROGMEM aware); returns t...
const LogString * reject_reason_for(int err)
Reject reason for a failed handshake read.
void progmem_memcpy(void *dst, const void *src, size_t len)
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.
Application App
Global storage of Application pointer - only one Application can exist.