mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
Keep the OTA key in flash and derive the extended protocol flag
This commit is contained in:
@@ -25,7 +25,7 @@ from esphome.const import (
|
||||
CONF_VERSION,
|
||||
CONF_WEB_SERVER,
|
||||
)
|
||||
from esphome.core import CORE, coroutine_with_priority
|
||||
from esphome.core import CORE, ID, coroutine_with_priority
|
||||
from esphome.coroutine import CoroPriority
|
||||
import esphome.final_validate as fv
|
||||
from esphome.types import ConfigType
|
||||
@@ -309,7 +309,13 @@ async def to_code(config: ConfigType) -> None:
|
||||
key = _api_static_key(CORE.config.get(CONF_API) or {})
|
||||
if key is not None:
|
||||
cg.add_define("USE_OTA_ENCRYPTION")
|
||||
cg.add(var.set_noise_psk(list(decode_encryption_key(key))))
|
||||
# The key stays in flash; it is copied into the handshake only while
|
||||
# an encrypted session is open
|
||||
psk = cg.progmem_array(
|
||||
ID(f"{config[CONF_ID].id}_psk", is_declaration=True, type=cg.uint8),
|
||||
list(decode_encryption_key(key)),
|
||||
)
|
||||
cg.add(var.set_noise_psk(psk))
|
||||
|
||||
# Build flag so lwip_fast_select.c (a .c file that can't include defines.h) sees it.
|
||||
cg.add_build_flag("-DUSE_OTA_PLATFORM_ESPHOME")
|
||||
|
||||
@@ -151,18 +151,9 @@ void ESPHomeOTAComponent::loop() {
|
||||
this->handle_handshake_();
|
||||
}
|
||||
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL = 0x04;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_NOISE = 0x08;
|
||||
static constexpr uint8_t SERVER_FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
||||
static constexpr uint8_t SERVER_FEATURE_SUPPORTS_PARTITION_ACCESS = 0x02;
|
||||
static constexpr uint8_t SERVER_FEATURE_SUPPORTS_NOISE = 0x04;
|
||||
#ifdef USE_OTA_ENCRYPTION
|
||||
// Noise needs the extended protocol: the prologue binds the 2-byte feature ack
|
||||
static constexpr uint8_t CLIENT_NOISE_FEATURES =
|
||||
CLIENT_FEATURE_SUPPORTS_NOISE | CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL;
|
||||
#endif
|
||||
|
||||
void ESPHomeOTAComponent::handle_handshake_() {
|
||||
/// Handle the OTA handshake and authentication.
|
||||
@@ -265,8 +256,7 @@ void ESPHomeOTAComponent::handle_handshake_() {
|
||||
// Compose the feature-ack response. When the client negotiates the extended protocol we emit
|
||||
// a 2-byte response (marker + server feature flags); otherwise we emit the single-byte
|
||||
// legacy response.
|
||||
this->extended_proto_ = (this->ota_features_ & CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL) != 0;
|
||||
if (this->extended_proto_) {
|
||||
if (this->extended_proto_()) {
|
||||
static_assert(HANDSHAKE_BUF_SIZE >= 2, "handshake_buf_ must hold the 2-byte extended-protocol feature ack");
|
||||
this->handshake_buf_[0] = ota::OTA_RESPONSE_FEATURE_FLAGS;
|
||||
this->handshake_buf_[1] = (supports_compression ? SERVER_FEATURE_SUPPORTS_COMPRESSION : 0);
|
||||
@@ -286,7 +276,7 @@ void ESPHomeOTAComponent::handle_handshake_() {
|
||||
case OTAState::FEATURE_ACK: {
|
||||
static constexpr size_t STANDARD_PROTO_ACK_SIZE = 1;
|
||||
static constexpr size_t EXTENDED_PROTO_ACK_SIZE = 2;
|
||||
const size_t ack_size = this->extended_proto_ ? EXTENDED_PROTO_ACK_SIZE : STANDARD_PROTO_ACK_SIZE;
|
||||
const size_t ack_size = this->extended_proto_() ? EXTENDED_PROTO_ACK_SIZE : STANDARD_PROTO_ACK_SIZE;
|
||||
if (!this->try_write_(ack_size, LOG_STR("ack feature"))) {
|
||||
return;
|
||||
}
|
||||
@@ -414,7 +404,7 @@ void ESPHomeOTAComponent::handle_data_() {
|
||||
// Acknowledge auth OK - 1 byte
|
||||
this->data_write_byte_(ota::OTA_RESPONSE_AUTH_OK);
|
||||
|
||||
if (this->extended_proto_) {
|
||||
if (this->extended_proto_()) {
|
||||
// Read ota type, 1 byte
|
||||
if (!this->data_readall_(buf, 1)) {
|
||||
this->log_read_error_(LOG_STR("OTA type"));
|
||||
|
||||
@@ -45,7 +45,8 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
|
||||
#endif // USE_OTA_PASSWORD
|
||||
|
||||
#ifdef USE_OTA_ENCRYPTION
|
||||
void set_noise_psk(const noise::psk_t &psk) { this->noise_ctx_.set_psk(psk); }
|
||||
/// psk points at 32 bytes that live in flash for the life of the program
|
||||
void set_noise_psk(const uint8_t *psk) { this->noise_psk_ = psk; }
|
||||
#endif
|
||||
|
||||
/// Manually set the port OTA should listen on
|
||||
@@ -145,7 +146,7 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
|
||||
std::unique_ptr<uint8_t[]> auth_buf_;
|
||||
#endif // USE_OTA_PASSWORD
|
||||
#ifdef USE_OTA_ENCRYPTION
|
||||
noise::NoiseContext noise_ctx_;
|
||||
const uint8_t *noise_psk_{nullptr};
|
||||
std::unique_ptr<NoiseSession> noise_;
|
||||
#endif // USE_OTA_ENCRYPTION
|
||||
|
||||
@@ -167,6 +168,16 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
|
||||
"OTA_BUFFER_SIZE must fit a full encrypted data frame");
|
||||
#endif
|
||||
static constexpr uint8_t MAGIC_BYTES[5] = {0x6C, 0x26, 0xF7, 0x5C, 0x45};
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_COMPRESSION = 0x01;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_SHA256_AUTH = 0x02;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL = 0x04;
|
||||
static constexpr uint8_t CLIENT_FEATURE_SUPPORTS_NOISE = 0x08;
|
||||
// Noise needs the extended protocol: the prologue binds the 2-byte feature ack
|
||||
static constexpr uint8_t CLIENT_NOISE_FEATURES =
|
||||
CLIENT_FEATURE_SUPPORTS_NOISE | CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL;
|
||||
// Derived from the feature byte rather than stored, which keeps the
|
||||
// trailing byte members at a multiple of four
|
||||
inline bool extended_proto_() const { return (this->ota_features_ & CLIENT_FEATURE_SUPPORTS_EXTENDED_PROTOCOL) != 0; }
|
||||
#ifdef USE_OTA_PARTITIONS
|
||||
uint32_t running_app_offset_{0};
|
||||
size_t running_app_size_{0};
|
||||
@@ -180,7 +191,6 @@ class ESPHomeOTAComponent final : public ota::OTAComponent {
|
||||
uint8_t auth_buf_pos_{0};
|
||||
uint8_t auth_type_{0}; // Store auth type to know which hasher to use
|
||||
#endif // USE_OTA_PASSWORD
|
||||
bool extended_proto_{false};
|
||||
};
|
||||
|
||||
} // namespace esphome
|
||||
|
||||
@@ -65,9 +65,15 @@ bool ESPHomeOTAComponent::noise_start_session_(uint8_t server_feature_flags) {
|
||||
*p++ = ota::OTA_RESPONSE_FEATURE_FLAGS;
|
||||
*p++ = server_feature_flags;
|
||||
|
||||
int err = this->noise_ == nullptr
|
||||
? NOISE_ERROR_NO_MEMORY
|
||||
: this->noise_->handshake.init(this->noise_ctx_.get_psk(), prologue, sizeof(prologue));
|
||||
// noise-c keeps its own copy of the key, so the flash copy is only read here
|
||||
noise::psk_t psk;
|
||||
#ifdef USE_ESP8266
|
||||
memcpy_P(psk.data(), this->noise_psk_, psk.size());
|
||||
#else
|
||||
std::memcpy(psk.data(), this->noise_psk_, psk.size());
|
||||
#endif
|
||||
int err =
|
||||
this->noise_ == nullptr ? NOISE_ERROR_NO_MEMORY : this->noise_->handshake.init(psk, prologue, sizeof(prologue));
|
||||
if (err != 0) {
|
||||
ESP_LOGW(TAG, "Session init: %d", err);
|
||||
this->cleanup_connection_();
|
||||
|
||||
Reference in New Issue
Block a user