3#ifdef USE_OTA_ENCRYPTION
18static const char *
const TAG =
"esphome.ota";
21static constexpr char OTA_NOISE_PROLOGUE_INIT[]
PROGMEM =
"NoiseOTAInit";
23static constexpr char OTA_NOISE_PROLOGUE_INIT[] =
"NoiseOTAInit";
25static constexpr size_t OTA_NOISE_PROLOGUE_INIT_LEN =
sizeof(OTA_NOISE_PROLOGUE_INIT) - 1;
49 static constexpr size_t PROLOGUE_ACK_LEN = 2;
50 static constexpr size_t PROLOGUE_CLIENT_FEATURES_LEN = 1;
51 static constexpr size_t PROLOGUE_FEATURE_ACK_LEN = 2;
52 uint8_t prologue[OTA_NOISE_PROLOGUE_INIT_LEN +
sizeof(
MAGIC_BYTES) + PROLOGUE_ACK_LEN + PROLOGUE_CLIENT_FEATURES_LEN +
53 PROLOGUE_FEATURE_ACK_LEN];
54 progmem_memcpy(prologue, OTA_NOISE_PROLOGUE_INIT, OTA_NOISE_PROLOGUE_INIT_LEN);
55 uint8_t *p = prologue + OTA_NOISE_PROLOGUE_INIT_LEN;
61 *p++ = USE_OTA_VERSION;
66 *p++ = server_feature_flags;
69 int err = this->
noise_ ==
nullptr ? NOISE_ERROR_NO_MEMORY
74 ESP_LOGW(TAG,
"Session init: %d", err);
102 const uint16_t payload_len = s.
frame_len - noise::FRAME_HEADER_SIZE;
105 if (s.
frame_buf[noise::FRAME_HEADER_SIZE] != noise::HANDSHAKE_STATUS_OK) {
106 ESP_LOGW(TAG,
"Client rejected the handshake: %u", s.
frame_buf[noise::FRAME_HEADER_SIZE]);
114 ESP_LOGW(TAG,
"Handshake read: %s (%d)", LOG_STR_ARG(reason), err);
126 ESP_LOGW(TAG,
"Handshake write: %d", err);
130 const uint16_t payload_len = msg_len + 1;
132 s.
frame_buf[noise::FRAME_HEADER_SIZE] = noise::HANDSHAKE_STATUS_OK;
133 s.
frame_len = noise::FRAME_HEADER_SIZE + payload_len;
141 ESP_LOGW(TAG,
"Handshake split: %d", err);
145 ESP_LOGD(TAG,
"Noise handshake complete");
149 ESP_LOGW(TAG,
"Bad handshake state");
160 const size_t payload_len =
encode_uint16(header[1], header[2]);
161 if (header[0] != noise::FRAME_INDICATOR || payload_len < min_len || payload_len > max_len) {
162 ESP_LOGW(TAG,
"Bad frame: 0x%02X, %zu bytes", header[0], payload_len);
186 if (payload_len == 0) {
190 s.
frame_len = noise::FRAME_HEADER_SIZE + payload_len;
211 uint8_t data[noise::FRAME_HEADER_SIZE + noise::MAC_FAILURE_PAYLOAD_SIZE];
212 const size_t payload_len =
215 this->
client_->write(data, noise::FRAME_HEADER_SIZE + payload_len);
221 noise_buffer_init(mbuf);
222 noise_buffer_set_inout(mbuf, buf,
len,
len);
223 int err = noise_cipherstate_decrypt(this->
noise_->recv_cipher, &mbuf);
225 ESP_LOGW(TAG,
"Decrypt: %d", err);
236 uint8_t header[noise::FRAME_HEADER_SIZE];
237 if (!this->
readall_(header,
sizeof(header))) {
241 if (ciphertext_len == 0) {
244 if (!this->
readall_(buf, ciphertext_len)) {
270 uint8_t frame[noise::FRAME_HEADER_SIZE + 1 + noise::MAC_SIZE];
271 frame[noise::FRAME_HEADER_SIZE] = byte;
273 noise_buffer_init(mbuf);
274 noise_buffer_set_inout(mbuf, frame + noise::FRAME_HEADER_SIZE, 1, 1 + noise::MAC_SIZE);
275 int err = noise_cipherstate_encrypt(this->
noise_->send_cipher, &mbuf);
277 ESP_LOGW(TAG,
"Encrypt: %d", err);
281 return this->
writeall_(frame, noise::FRAME_HEADER_SIZE + mbuf.size);
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.
static constexpr uint8_t MAGIC_BYTES[5]
bool writeall_(const uint8_t *buf, size_t len)
ssize_t noise_decrypt_(uint8_t *buf, size_t len)
Decrypt a ciphertext in place; returns the plaintext size or -1.
bool noise_readall_(uint8_t *buf, size_t len)
Blocking read of one frame whose plaintext must be exactly len bytes (control units are one unit per ...
bool noise_start_session_(uint8_t server_feature_flags)
Allocate the session and start the responder handshake.
bool noise_try_read_frame_()
Non-blocking read of one handshake frame into the session buffer.
bool handle_write_error_(ssize_t written, const LogString *desc)
ssize_t noise_read_frame_blocking_(uint8_t *buf, size_t min_ciphertext, size_t max_ciphertext)
Blocking read of one frame whose ciphertext size must be within the given bounds, decrypted in place;...
size_t noise_frame_payload_len_(const uint8_t *header, size_t min_len, size_t max_len)
Payload length from a frame header, or 0 (logged) when the indicator or the length is out of range.
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,...
bool readall_(uint8_t *buf, size_t len)
std::unique_ptr< NoiseSession > noise_
void noise_send_reject_(const LogString *reason)
Best-effort explicit reject frame so the client can log a readable reason.
bool noise_write_byte_(uint8_t byte)
Blocking write of one response byte as an encrypted frame.
const noise::NoiseContext & noise_context_() const
bool noise_try_write_frame_()
Non-blocking write of the pending session-buffer frame.
void cleanup_connection_()
std::unique_ptr< socket::Socket > client_
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 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.
constexpr float BME680_GAS_LOOKUP_TABLE_1[16] PROGMEM
void write_frame_header(uint8_t *buf, uint16_t payload_len)
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.
@ OTA_RESPONSE_FEATURE_FLAGS
void progmem_memcpy(void *dst, const void *src, size_t len)
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
uint16_t uint16_t & capacity
NoiseCipherState * recv_cipher
uint8_t frame_buf[noise::FRAME_HEADER_SIZE+1+noise::MAX_HANDSHAKE_SIZE]
NoiseCipherState * send_cipher
noise::NoiseResponderHandshake handshake