ESPHome 2026.10.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
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
21
22// api_frame_helper.h keeps its own MAX_HANDSHAKE_SIZE because that header is
23// also compiled in plaintext-only builds without the noise component; keep
24// the two definitions from drifting apart.
25static_assert(MAX_HANDSHAKE_SIZE == noise::MAX_HANDSHAKE_SIZE,
26 "api and noise component handshake size limits must match");
27
28static const char *const TAG = "api.noise";
29#ifdef USE_ESP8266
30static constexpr char PROLOGUE_INIT[] PROGMEM = "NoiseAPIInit";
31#else
32static const char *const PROLOGUE_INIT = "NoiseAPIInit";
33#endif
34static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit")
35
36// Maximum bytes to log in hex format (168 * 3 = 504, under TX buffer size of 512)
37static constexpr size_t API_MAX_LOG_BYTES = 168;
38
39#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERY_VERBOSE
40#define HELPER_LOG(msg, ...) \
41 do { \
42 char peername_buf[socket::SOCKADDR_STR_LEN]; \
43 this->get_peername_to(peername_buf); \
44 ESP_LOGVV(TAG, "%s (%s): " msg, this->client_name_, peername_buf, ##__VA_ARGS__); \
45 } while (0)
46#else
47#define HELPER_LOG(msg, ...) ((void) 0)
48#endif
49
50#ifdef HELPER_LOG_PACKETS
51#define LOG_PACKET_RECEIVED(buffer) \
52 do { \
53 char hex_buf_[format_hex_pretty_size(API_MAX_LOG_BYTES)]; \
54 ESP_LOGVV(TAG, "Received frame: %s", \
55 format_hex_pretty_to(hex_buf_, (buffer).data(), \
56 (buffer).size() < API_MAX_LOG_BYTES ? (buffer).size() : API_MAX_LOG_BYTES)); \
57 } while (0)
58#else
59#define LOG_PACKET_RECEIVED(buffer) ((void) 0)
60#endif
61
64 APIError err = init_common_();
65 if (err != APIError::OK) {
66 return err;
67 }
68
69 // init prologue
70 size_t old_size = prologue_.size();
71 if (!prologue_.resize(old_size + PROLOGUE_INIT_LEN)) [[unlikely]] {
74 }
75#ifdef USE_ESP8266
76 memcpy_P(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
77#else
78 std::memcpy(prologue_.data() + old_size, PROLOGUE_INIT, PROLOGUE_INIT_LEN);
79#endif
80
82 return APIError::OK;
83}
84#ifdef USE_API_PLAINTEXT
85APIError APINoiseFrameHelper::init_from_handoff(const uint8_t *header, uint8_t header_len) {
86 APIError err = this->init();
87 if (err != APIError::OK) {
88 return err;
89 }
90 // Seed the header bytes the plaintext helper consumed before detecting the
91 // Noise indicator; try_read_frame_ resumes from rx_header_buf_len_.
92 std::memcpy(this->rx_header_buf_, header, header_len);
93 this->rx_header_buf_len_ = header_len;
94 // Pump the handshake without gating on socket_->ready(): on LWIP the
95 // plaintext helper's partial read can drain rcvevent while the rest of the
96 // client hello sits in the lastdata cache, so ready() may report false even
97 // though data is available.
98 return this->pump_handshake_();
99}
100#endif // USE_API_PLAINTEXT
101
106 while (this->state_ != State::DATA) {
107 APIError err = this->state_action_();
108 if (err == APIError::WOULD_BLOCK) {
109 break;
110 }
111 if (err != APIError::OK) {
112 return err;
113 }
114 }
115 return APIError::OK;
116}
117
118// Helper for handling handshake frame errors
120 if (aerr == APIError::BAD_INDICATOR) {
121 send_explicit_handshake_reject_(LOG_STR("Bad indicator byte"));
122 } else if (aerr == APIError::BAD_HANDSHAKE_PACKET_LEN) {
123 send_explicit_handshake_reject_(LOG_STR("Bad handshake packet len"));
124 }
125 return aerr;
126}
127
128// Helper for handling noise library errors
129APIError APINoiseFrameHelper::handle_noise_error_(int err, const LogString *func_name, APIError api_err) {
130 if (err != 0) {
132 HELPER_LOG("%s failed: %s", LOG_STR_ARG(func_name), LOG_STR_ARG(noise_err_to_logstr(err)));
133 return api_err;
134 }
135 return APIError::OK;
136}
137
140 // Check ready() once, not per state transition. On ESP8266 LWIP raw TCP,
141 // ready() returns false once the rx buffer is consumed. Re-checking each
142 // iteration would block handshake writes that must follow reads,
143 // deadlocking the handshake. pump_handshake_() stops on WOULD_BLOCK when
144 // no more data is available to read.
145 if (state_ != State::DATA && this->socket_->ready()) {
146 APIError err = this->pump_handshake_();
147 if (err != APIError::OK) {
148 return err;
149 }
150 }
151
152 if (!this->overflow_buf_.empty()) [[unlikely]] {
154 }
155 return APIError::OK;
156}
157
168 // read header
169 if (rx_header_buf_len_ < noise::FRAME_HEADER_SIZE) {
170 // no header information yet
171 uint8_t to_read = static_cast<uint8_t>(noise::FRAME_HEADER_SIZE) - rx_header_buf_len_;
172 ssize_t received = this->socket_->read(&rx_header_buf_[rx_header_buf_len_], to_read);
173 APIError err = handle_socket_read_result_(received);
174 if (err != APIError::OK) {
175 return err;
176 }
177 rx_header_buf_len_ += static_cast<uint8_t>(received);
178 if (static_cast<uint8_t>(received) != to_read) {
179 // not a full read
181 }
182
183 if (rx_header_buf_[0] != noise::FRAME_INDICATOR) {
185 HELPER_LOG("Bad indicator byte %u", rx_header_buf_[0]);
187 }
188 // header reading done
189 }
190
191 // read body
192 uint16_t msg_size = (((uint16_t) rx_header_buf_[1]) << 8) | rx_header_buf_[2];
193
194 // Check against size limits to prevent OOM: MAX_HANDSHAKE_SIZE for handshake, MAX_MESSAGE_SIZE for data
195 bool is_data = (state_ == State::DATA);
196 uint16_t limit = is_data ? MAX_MESSAGE_SIZE : MAX_HANDSHAKE_SIZE;
197 if (msg_size > limit) {
199 HELPER_LOG("Bad packet: message size %u exceeds maximum %u", msg_size, limit);
201 }
202
203 // Reserve space for body (+ null terminator in DATA state so protobuf
204 // StringRef fields can be safely null-terminated in-place after decode.
205 // During handshake, rx_buf_.size() is used in prologue construction, so
206 // the buffer must be exactly msg_size to avoid prologue mismatch.)
207 uint16_t alloc_size = msg_size + (is_data ? RX_BUF_NULL_TERMINATOR : 0);
208 if (!this->rx_buf_.resize(alloc_size)) [[unlikely]] {
211 }
212
213 if (rx_buf_len_ < msg_size) {
214 // more data to read
215 uint16_t to_read = msg_size - rx_buf_len_;
216 ssize_t received = this->socket_->read(&rx_buf_[rx_buf_len_], to_read);
217 APIError err = handle_socket_read_result_(received);
218 if (err != APIError::OK) {
219 return err;
220 }
221 rx_buf_len_ += static_cast<uint16_t>(received);
222 if (static_cast<uint16_t>(received) != to_read) {
223 // not all read
225 }
226 }
227
228 LOG_PACKET_RECEIVED(this->rx_buf_);
229
230 // Clear state for next frame (rx_buf_ still contains data for caller)
231 this->rx_buf_len_ = 0;
232 this->rx_header_buf_len_ = 0;
233
234 return APIError::OK;
235}
236
246// Split into per-state methods so the compiler doesn't allocate stack space
247// for all branches simultaneously. On RP2040 the core0 stack lives in a 4KB
248// scratch RAM bank; the Noise crypto path (curve25519) needs ~2KB+ of stack,
249// so every byte saved in the caller matters.
251 switch (this->state_) {
253 HELPER_LOG("Bad state for method: %d", (int) this->state_);
254 return APIError::BAD_STATE;
256 return this->state_action_client_hello_();
258 return this->state_action_server_hello_();
259 case State::HANDSHAKE:
260 return this->state_action_handshake_();
261 case State::CLOSED:
262 case State::FAILED:
263 return APIError::BAD_STATE;
264 default:
265 return APIError::OK;
266 }
267}
269 // waiting for client hello
270 APIError aerr = this->try_read_frame_();
271 if (aerr != APIError::OK) {
273 }
274 // ignore contents, may be used in future for flags
275 // Resize for: existing prologue + 2 size bytes + frame data
276 size_t old_size = this->prologue_.size();
277 size_t rx_size = this->rx_buf_.size();
278 if (!this->prologue_.resize(old_size + 2 + rx_size)) [[unlikely]] {
281 }
282 this->prologue_[old_size] = (uint8_t) (rx_size >> 8);
283 this->prologue_[old_size + 1] = (uint8_t) rx_size;
284 if (rx_size > 0) {
285 std::memcpy(this->prologue_.data() + old_size + 2, this->rx_buf_.data(), rx_size);
286 }
287
289 return APIError::OK;
290}
292 // send server hello
293 const auto &name = App.get_name();
294 char mac[MAC_ADDRESS_BUFFER_SIZE];
296
297 // Calculate positions and sizes
298 size_t name_len = name.size() + 1; // including null terminator
299 size_t name_offset = 1;
300 size_t mac_offset = name_offset + name_len;
301 size_t total_size = 1 + name_len + MAC_ADDRESS_BUFFER_SIZE;
302
303 // 1 (proto) + name (max ESPHOME_DEVICE_NAME_MAX_LEN) + 1 (name null)
304 // + mac (MAC_ADDRESS_BUFFER_SIZE - 1) + 1 (mac null)
305 constexpr size_t max_msg_size = 1 + ESPHOME_DEVICE_NAME_MAX_LEN + 1 + MAC_ADDRESS_BUFFER_SIZE;
306 uint8_t msg[max_msg_size];
307
308 // chosen proto
309 msg[0] = 0x01;
310
311 // node name, terminated by null byte
312 std::memcpy(msg + name_offset, name.c_str(), name_len);
313 // node mac, terminated by null byte
314 std::memcpy(msg + mac_offset, mac, MAC_ADDRESS_BUFFER_SIZE);
315
316 APIError aerr = write_frame_(msg, total_size);
317 if (aerr != APIError::OK)
318 return aerr;
319
320 // start handshake
321 aerr = init_handshake_();
322 if (aerr != APIError::OK)
323 return aerr;
324
326 return APIError::OK;
327}
331 return this->state_action_handshake_read_();
333 return this->state_action_handshake_write_();
334 }
335 // bad state for action
336 this->state_ = State::FAILED;
337 HELPER_LOG("Bad action for handshake: %d", (int) action);
339}
341 APIError aerr = this->try_read_frame_();
342 if (aerr != APIError::OK) {
343 return this->handle_handshake_frame_error_(aerr);
344 }
345
346 if (this->rx_buf_.empty()) {
347 this->send_explicit_handshake_reject_(LOG_STR("Empty handshake message"));
349 } else if (this->rx_buf_[0] != noise::HANDSHAKE_STATUS_OK) {
350 HELPER_LOG("Bad handshake error byte: %u", this->rx_buf_[0]);
351 this->send_explicit_handshake_reject_(LOG_STR("Bad handshake error byte"));
353 }
354
355 int err = this->handshake_.read_message(this->rx_buf_.data() + 1, this->rx_buf_.size() - 1);
356 if (err != 0) {
357 // Special handling for MAC failure
359 return this->handle_noise_error_(err, LOG_STR("noise_handshakestate_read_message"),
361 }
362
363 return this->check_handshake_finished_();
364}
366 uint8_t buffer[65];
367 size_t msg_len = 0;
368
369 int err = this->handshake_.write_message(buffer + 1, sizeof(buffer) - 1, msg_len);
370 APIError aerr = this->handle_noise_error_(err, LOG_STR("noise_handshakestate_write_message"),
372 if (aerr != APIError::OK)
373 return aerr;
374 buffer[0] = noise::HANDSHAKE_STATUS_OK;
375
376 aerr = this->write_frame_(buffer, msg_len + 1);
377 if (aerr != APIError::OK)
378 return aerr;
379 return this->check_handshake_finished_();
380}
382 // Max reject message: "Bad handshake packet len" (24) + 1 (failure byte) = 25 bytes
383 uint8_t data[32];
384 static_assert(sizeof(data) >= noise::MAC_FAILURE_PAYLOAD_SIZE,
385 "reject buffer must fit the MAC failure wire contract");
386 size_t data_size = noise::format_reject_payload(data, sizeof(data), reason);
387
388 // temporarily remove failed state
389 auto orig_state = state_;
391 APIError aerr = write_frame_(data, data_size);
392 if (aerr != APIError::OK) {
393 // Best effort; the reject reason is a diagnosis aid, not a protocol step
394 ESP_LOGW(TAG, "Sending handshake reject failed: %d", (int) aerr);
395 }
397 // write_frame_ may have moved the state to FAILED; keep that decision
398 state_ = orig_state;
399 }
400}
402 APIError aerr = this->check_data_state_();
403 if (aerr != APIError::OK)
404 return aerr;
405
406 aerr = this->try_read_frame_();
407 if (aerr != APIError::OK)
408 return aerr;
409
410 NoiseBuffer mbuf;
411 noise_buffer_init(mbuf);
412 // read_packet() must only be called in DATA state; the extra
413 // RX_BUF_NULL_TERMINATOR byte is only allocated in DATA state
414 // (see try_read_frame_), so calling this during handshake would
415 // underflow the size calculation below.
416#ifdef ESPHOME_DEBUG_API
417 assert(this->state_ == State::DATA);
418#endif
419 // rx_buf_ has RX_BUF_NULL_TERMINATOR extra byte for null termination
420 // (only added in DATA state — see try_read_frame_), so subtract it
421 // to get the actual encrypted data size for decryption.
422 size_t encrypted_size = this->rx_buf_.size() - RX_BUF_NULL_TERMINATOR;
423 noise_buffer_set_inout(mbuf, this->rx_buf_.data(), encrypted_size, encrypted_size);
424 int err = noise_cipherstate_decrypt(this->recv_cipher_, &mbuf);
425 APIError decrypt_err =
426 handle_noise_error_(err, LOG_STR("noise_cipherstate_decrypt"), APIError::CIPHERSTATE_DECRYPT_FAILED);
427 if (decrypt_err != APIError::OK) {
428 return decrypt_err;
429 }
430
431 uint16_t msg_size = mbuf.size;
432 uint8_t *msg_data = this->rx_buf_.data();
433 if (msg_size < 4) {
434 this->state_ = State::FAILED;
435 HELPER_LOG("Bad data packet: size %d too short", msg_size);
437 }
438
439 uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
440 uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
441 if (data_len > msg_size - 4) {
442 this->state_ = State::FAILED;
443 HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
445 }
446
447 buffer->data = msg_data + 4; // Skip 4-byte header (type + length)
448 buffer->data_len = data_len;
449 buffer->type = type;
450 return APIError::OK;
451}
452// Encrypt a single noise message in place and return the encrypted frame length.
453// Returns APIError::OK on success.
454APIError APINoiseFrameHelper::encrypt_noise_message_(uint8_t *buf_start, uint16_t payload_size, uint16_t message_type,
455 uint16_t &encrypted_len_out) {
456 // The noise frame header is written after encryption, when the size is known
457
458 // Write message header (to be encrypted)
459 constexpr uint8_t msg_offset = noise::FRAME_HEADER_SIZE;
460 buf_start[msg_offset] = static_cast<uint8_t>(message_type >> 8); // type high byte
461 buf_start[msg_offset + 1] = static_cast<uint8_t>(message_type); // type low byte
462 buf_start[msg_offset + 2] = static_cast<uint8_t>(payload_size >> 8); // data_len high byte
463 buf_start[msg_offset + 3] = static_cast<uint8_t>(payload_size); // data_len low byte
464 // payload data is already in the buffer starting at offset + 7
465
466 // Encrypt the message in place
467 NoiseBuffer mbuf;
468 noise_buffer_init(mbuf);
469 noise_buffer_set_inout(mbuf, buf_start + msg_offset, 4 + payload_size, 4 + payload_size + this->frame_footer_size_);
470
471 int err = noise_cipherstate_encrypt(this->send_cipher_, &mbuf);
472 APIError aerr =
473 this->handle_noise_error_(err, LOG_STR("noise_cipherstate_encrypt"), APIError::CIPHERSTATE_ENCRYPT_FAILED);
474 if (aerr != APIError::OK)
475 return aerr;
476
477 // Fill in the frame header now that the encrypted size is known
478 noise::write_frame_header(buf_start, static_cast<uint16_t>(mbuf.size));
479
480 encrypted_len_out = static_cast<uint16_t>(noise::FRAME_HEADER_SIZE + mbuf.size);
481 return APIError::OK;
482}
483
485#ifdef ESPHOME_DEBUG_API
486 assert(this->state_ == State::DATA);
487#endif
488
489 APIBuffer *buf = buffer.get_buffer();
490 // Resize buffer to include footer space for Noise MAC
491 if (this->frame_footer_size_ && !buf->resize(buf->size() + this->frame_footer_size_)) [[unlikely]] {
494 }
495
496 uint16_t payload_size = static_cast<uint16_t>(buf->size() - HEADER_PADDING - this->frame_footer_size_);
497 uint8_t *buf_start = buf->data();
498 uint16_t encrypted_len;
499 APIError aerr = this->encrypt_noise_message_(buf_start, payload_size, type, encrypted_len);
500 if (aerr != APIError::OK)
501 return aerr;
502 return this->write_raw_fast_buf_(buf_start, encrypted_len);
503}
504
505APIError APINoiseFrameHelper::write_protobuf_messages(ProtoWriteBuffer buffer, std::span<const MessageInfo> messages) {
506#ifdef ESPHOME_DEBUG_API
507 assert(this->state_ == State::DATA);
508 assert(!messages.empty());
509#endif
510
511 // Noise messages are already contiguous in the buffer:
512 // HEADER_PADDING (7) exactly matches the fixed header size, and
513 // footer space (16) is consumed by the encryption MAC.
514 uint8_t *buffer_data = buffer.get_buffer()->data();
515 uint8_t *write_start = buffer_data + messages[0].offset;
516 uint16_t total_write_len = 0;
517
518 for (const auto &msg : messages) {
519 uint8_t *buf_start = buffer_data + msg.offset;
520 uint16_t encrypted_len;
521 APIError aerr = this->encrypt_noise_message_(buf_start, msg.payload_size, msg.message_type, encrypted_len);
522 if (aerr != APIError::OK)
523 return aerr;
524 total_write_len += encrypted_len;
525 }
526
527 return this->write_raw_fast_buf_(write_start, total_write_len);
528}
529
530APIError APINoiseFrameHelper::write_frame_(const uint8_t *data, uint16_t len) {
531 uint8_t header[noise::FRAME_HEADER_SIZE];
533
534 if (len == 0) {
535 return this->write_raw_buf_(header, noise::FRAME_HEADER_SIZE);
536 }
537 struct iovec iov[2];
538 iov[0].iov_base = header;
539 iov[0].iov_len = noise::FRAME_HEADER_SIZE;
540 iov[1].iov_base = const_cast<uint8_t *>(data);
541 iov[1].iov_len = len;
542
543 return this->write_raw_iov_(iov, 2, noise::FRAME_HEADER_SIZE + len);
544}
545
551 int err = this->handshake_.init(this->ctx_, prologue_.data(), prologue_.size());
552 APIError aerr = handle_noise_error_(err, LOG_STR("noise_handshake_init"), APIError::HANDSHAKESTATE_SETUP_FAILED);
553 if (aerr != APIError::OK)
554 return aerr;
555 // init copies the prologue into the handshakestate, so we can get rid of it now
557 return APIError::OK;
558}
559
561#ifdef ESPHOME_DEBUG_API
562 assert(state_ == State::HANDSHAKE);
563#endif
564
568 return APIError::OK;
571 HELPER_LOG("Bad action for handshake: %d", (int) action);
573 }
574 // split() also frees the handshake state
575 int err = this->handshake_.split(send_cipher_, recv_cipher_);
576 APIError aerr =
577 handle_noise_error_(err, LOG_STR("noise_handshakestate_split"), APIError::HANDSHAKESTATE_SPLIT_FAILED);
578 if (aerr != APIError::OK)
579 return aerr;
580
581 this->frame_footer_size_ = noise_cipherstate_get_mac_length(send_cipher_);
582
583 HELPER_LOG("Handshake complete!");
585 return APIError::OK;
586}
587
589 if (send_cipher_ != nullptr) {
590 noise_cipherstate_free(send_cipher_);
591 send_cipher_ = nullptr;
592 }
593 if (recv_cipher_ != nullptr) {
594 noise_cipherstate_free(recv_cipher_);
595 recv_cipher_ = nullptr;
596 }
597}
598
599} // namespace esphome::api
600#endif // USE_API_NOISE
601#endif // USE_API
const StringRef & get_name() const
Get the name of this Application set by pre_setup().
Byte buffer that skips zero-initialization on resize().
Definition api_buffer.h:26
void release()
Release all memory (equivalent to std::vector swap trick).
Definition api_buffer.h:50
size_t size() const
Definition api_buffer.h:44
bool resize(size_t n) ESPHOME_ALWAYS_INLINE
Returns false if allocation fails; the buffer is left unchanged. No zero-fill.
Definition api_buffer.h:32
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
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)
APIError pump_handshake_()
Drive the handshake state machine until DATA, WOULD_BLOCK, or a fatal error.
APIError read_packet(ReadPacketBuffer *buffer) 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 try_read_frame_()
Read a packet into the rx_buf_.
APIError loop() override
Run through handshake messages (if in that phase)
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)
noise::NoiseResponderHandshake handshake_
bool empty() const
True when no backlogged data is waiting.
APIBuffer * get_buffer() const
Definition proto.h:271
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.
uint16_t type
__int64 ssize_t
Definition httplib.h:178
void write_frame_header(uint8_t *buf, uint16_t payload_len)
Definition noise.h:52
const LogString * noise_err_to_logstr(int err)
Convert a noise error code to a readable error.
Definition noise.cpp:27
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...
Definition noise.cpp:69
const LogString * reject_reason_for(int err)
Reject reason for a failed handshake read.
Definition noise.cpp:65
const void size_t len
Definition hal.h:64
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:826
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()