mirror of
https://github.com/esphome/esphome.git
synced 2026-09-07 05:26:01 +00:00
[noise] Gate the refill on the noise handshake, cover its loop time on ESP8266
The refill now waits only for api clients still in their noise handshake, not for any client that has yet to send its hello, so a stale half open connection cannot keep the slot empty for a minute. On ESP8266 the api server raises its blocking warning threshold to 80 ms in setup(), since the refill takes about 60 ms there and used to run inside every handshake anyway; and prepare_spare_ephemeral() clears the ready flag before filling, so a random source failure can never leave a mismatched pair.
This commit is contained in:
@@ -319,6 +319,7 @@ class APIConnection final : public APIServerConnectionBase {
|
||||
bool is_authenticated() {
|
||||
return static_cast<ConnectionState>(this->flags_.connection_state) == ConnectionState::AUTHENTICATED;
|
||||
}
|
||||
bool is_handshake_complete() const { return this->helper_->is_handshake_complete(); }
|
||||
bool is_connection_setup() {
|
||||
return static_cast<ConnectionState>(this->flags_.connection_state) == ConnectionState::CONNECTED ||
|
||||
this->is_authenticated();
|
||||
|
||||
@@ -118,6 +118,9 @@ class APIFrameHelper {
|
||||
virtual APIError loop() = 0;
|
||||
virtual APIError read_packet(ReadPacketBuffer *buffer) = 0;
|
||||
bool can_write_without_blocking() { return this->state_ == State::DATA && this->overflow_buf_.empty(); }
|
||||
/// True once the transport handshake is done and the connection carries
|
||||
/// api messages (immediately for plaintext, after the noise handshake).
|
||||
bool is_handshake_complete() const { return this->state_ == State::DATA; }
|
||||
int getpeername(struct sockaddr *addr, socklen_t *addrlen) { return socket_->getpeername(addr, addrlen); }
|
||||
APIError close() {
|
||||
if (state_ == State::CLOSED)
|
||||
|
||||
@@ -41,6 +41,12 @@ void APIServer::setup() {
|
||||
ControllerRegistry::register_controller(this);
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
#ifdef USE_ESP8266
|
||||
// Refilling the spare ephemeral key blocks the loop for about 60 ms on
|
||||
// this core; the same generation used to run inside every handshake.
|
||||
// Cover it so the first refill does not log a blocking warning at boot.
|
||||
this->warn_if_blocking_over_ = 8; // centiseconds
|
||||
#endif
|
||||
// Always reserve the slot: flash preferences are positional on esp8266, so
|
||||
// a yaml key build must keep the layout of a runtime key build
|
||||
uint32_t hash = 88491486UL;
|
||||
@@ -193,19 +199,15 @@ void APIServer::loop() {
|
||||
|
||||
#ifdef USE_API_NOISE
|
||||
// Refill the spare ephemeral key while nobody is waiting for it: not before
|
||||
// the network is up, and not while an api client is still in its handshake
|
||||
// (an OTA handshake is not visible here; it took the previous spare and
|
||||
// pays the refill instead, no worse than generating its own key). The
|
||||
// generation blocks the loop for about 60 ms on ESP8266, over the 50 ms
|
||||
// blocking warning, so the api component's warning threshold ratchets up
|
||||
// on the first refill; the same generation used to run inside the
|
||||
// handshake, where it tripped the warning for longer.
|
||||
// the network is up, and not while an api client is still in its noise
|
||||
// handshake (an OTA handshake is not visible here; it took the previous
|
||||
// spare and pays the refill instead, no worse than generating its own key).
|
||||
void APIServer::prepare_spare_ephemeral_() {
|
||||
if (noise::has_spare_ephemeral() || !network::is_connected()) {
|
||||
return;
|
||||
}
|
||||
for (auto &client : this->active_clients()) {
|
||||
if (!client->is_connection_setup()) {
|
||||
if (!client->is_handshake_complete()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,8 @@ static bool spare_ephemeral_ready = false; // NOLINT(cppcoreguidel
|
||||
bool has_spare_ephemeral() { return spare_ephemeral_ready; }
|
||||
|
||||
void prepare_spare_ephemeral() {
|
||||
// A partial fill must never look ready
|
||||
spare_ephemeral_ready = false;
|
||||
uint8_t *private_key = spare_ephemeral;
|
||||
uint8_t *public_key = spare_ephemeral + EPHEMERAL_PRIVATE_KEY_SIZE;
|
||||
// Same generation as noise-c's curve25519 backend: random bytes, X25519
|
||||
|
||||
Reference in New Issue
Block a user