3#ifdef USE_OTA_ENCRYPTION
17static const char *
const TAG =
"esphome.ota";
20static constexpr char OTA_NOISE_PROLOGUE_INIT[]
PROGMEM =
"NoiseOTAInit";
22static constexpr char OTA_NOISE_PROLOGUE_INIT[] =
"NoiseOTAInit";
24static constexpr size_t OTA_NOISE_PROLOGUE_INIT_LEN =
sizeof(OTA_NOISE_PROLOGUE_INIT) - 1;
47 static constexpr size_t PROLOGUE_ACK_LEN = 2;
48 static constexpr size_t PROLOGUE_CLIENT_FEATURES_LEN = 1;
49 static constexpr size_t PROLOGUE_FEATURE_ACK_LEN = 2;
50 uint8_t prologue[OTA_NOISE_PROLOGUE_INIT_LEN +
sizeof(
MAGIC_BYTES) + PROLOGUE_ACK_LEN + PROLOGUE_CLIENT_FEATURES_LEN +
51 PROLOGUE_FEATURE_ACK_LEN];
52 progmem_memcpy(prologue, OTA_NOISE_PROLOGUE_INIT, OTA_NOISE_PROLOGUE_INIT_LEN);
53 uint8_t *p = prologue + OTA_NOISE_PROLOGUE_INIT_LEN;
59 *p++ = USE_OTA_VERSION;
64 *p++ = server_feature_flags;
67 int err = this->
noise_ ==
nullptr ? NOISE_ERROR_NO_MEMORY
72 ESP_LOGW(TAG,
"Session init: %d", err);
100 const uint16_t payload_len = s.
frame_len - noise::FRAME_HEADER_SIZE;
103 if (s.
frame_buf[noise::FRAME_HEADER_SIZE] != noise::HANDSHAKE_STATUS_OK) {
104 ESP_LOGW(TAG,
"Client rejected the handshake: %u", s.
frame_buf[noise::FRAME_HEADER_SIZE]);
112 ESP_LOGW(TAG,
"Handshake read: %s (%d)", LOG_STR_ARG(reason), err);
124 ESP_LOGW(TAG,
"Handshake write: %d", err);
128 const uint16_t payload_len = msg_len + 1;
130 s.
frame_buf[noise::FRAME_HEADER_SIZE] = noise::HANDSHAKE_STATUS_OK;
131 s.
frame_len = noise::FRAME_HEADER_SIZE + payload_len;
139 ESP_LOGW(TAG,
"Handshake split: %d", err);
143 ESP_LOGD(TAG,
"Noise handshake complete");
147 ESP_LOGW(TAG,
"Bad handshake state");
158 const size_t payload_len =
encode_uint16(header[1], header[2]);
159 if (header[0] != noise::FRAME_INDICATOR || payload_len < min_len || payload_len > max_len) {
160 ESP_LOGW(TAG,
"Bad frame: 0x%02X, %zu bytes", header[0], payload_len);
184 if (payload_len == 0) {
188 s.
frame_len = noise::FRAME_HEADER_SIZE + payload_len;
209 uint8_t data[noise::FRAME_HEADER_SIZE + noise::MAC_FAILURE_PAYLOAD_SIZE];
210 const size_t payload_len =
213 this->
client_->write(data, noise::FRAME_HEADER_SIZE + payload_len);
219 noise_buffer_init(mbuf);
220 noise_buffer_set_inout(mbuf, buf,
len,
len);
221 int err = noise_cipherstate_decrypt(this->
noise_->recv_cipher, &mbuf);
223 ESP_LOGW(TAG,
"Decrypt: %d", err);
234 uint8_t header[noise::FRAME_HEADER_SIZE];
235 if (!this->
readall_(header,
sizeof(header))) {
239 if (ciphertext_len == 0) {
242 if (!this->
readall_(buf, ciphertext_len)) {
268 uint8_t frame[noise::FRAME_HEADER_SIZE + 1 + noise::MAC_SIZE];
269 frame[noise::FRAME_HEADER_SIZE] = byte;
271 noise_buffer_init(mbuf);
272 noise_buffer_set_inout(mbuf, frame + noise::FRAME_HEADER_SIZE, 1, 1 + noise::MAC_SIZE);
273 int err = noise_cipherstate_encrypt(this->
noise_->send_cipher, &mbuf);
275 ESP_LOGW(TAG,
"Encrypt: %d", err);
279 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.
RAMUniquePtr< NoiseSession > noise_
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)
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_
An STL allocator that uses SPI or internal RAM.
RAMUniquePtr< T > make_unique(Args &&...args)
Value initialize one T; empty on exhaustion.
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