mirror of
https://github.com/esphome/esphome.git
synced 2026-09-11 15:27:33 +00:00
Say what a provisioned key next to a password means, log a loaded key only when there is one, drop the wizard's dead password
This commit is contained in:
+3
-1
@@ -150,7 +150,9 @@ The following are **not** vulnerabilities, by design:
|
||||
- Plaintext OTA on a device with no `ota: encryption:` block, including one
|
||||
that offers encryption because it has an api key. That is the documented
|
||||
default, authenticated (if at all) by the OTA password. An uploader that
|
||||
takes the offer skips the password; the key authenticates it.
|
||||
takes the offer skips the password; the key authenticates it. With a
|
||||
runtime provisioned key and no `provisioning:` window, whoever provisions
|
||||
the key gains that upload path too; validation warns about the pair.
|
||||
- The CLI plaintext fallback until 2027.3.0: without `ota: encryption:` an
|
||||
active attacker who strips the offer or breaks the handshake can make a
|
||||
keyed CLI upload plaintext, with the pre-existing plaintext exposure. A
|
||||
|
||||
@@ -43,7 +43,8 @@ void APIServer::setup() {
|
||||
#if defined(USE_API_NOISE) && !defined(USE_API_NOISE_PSK_FROM_YAML)
|
||||
uint32_t hash = 88491486UL;
|
||||
this->noise_pref_ = global_preferences->make_preference<SavedNoisePsk>(hash, true);
|
||||
if (this->load_and_apply_noise_psk_()) {
|
||||
// A cleared record loads fine but holds no key
|
||||
if (this->load_and_apply_noise_psk_() && this->noise_ctx_.has_psk()) {
|
||||
ESP_LOGD(TAG, "Loaded saved Noise PSK");
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -155,11 +155,14 @@ def ota_esphome_final_validate(config: ConfigType) -> None:
|
||||
and not api_conf[CONF_ENCRYPTION].get(CONF_KEY)
|
||||
):
|
||||
# A runtime provisioned key: the CLI still needs the password, but
|
||||
# whoever holds the key Home Assistant provisions skips it
|
||||
# whoever holds the key skips it, and without a provisioning window
|
||||
# anyone on the network can be the one to provision it
|
||||
_LOGGER.warning(
|
||||
"The '%s' %s %s provisioned at runtime also authenticates OTA "
|
||||
"uploads once provisioned; '%s' %s then only guards plaintext "
|
||||
"uploads",
|
||||
"uploads. Whoever provisions the key can upload firmware "
|
||||
"without the password, so add a 'provisioning:' block to limit "
|
||||
"when that is possible",
|
||||
CONF_API,
|
||||
CONF_ENCRYPTION,
|
||||
CONF_KEY,
|
||||
|
||||
@@ -41,6 +41,15 @@ ESPHomeOTAComponent::NoiseSession::~NoiseSession() {
|
||||
* "NoiseOTAInit" | magic(5) | OK,version | client_features | FEATURE_FLAGS,server_flags
|
||||
*/
|
||||
bool ESPHomeOTAComponent::noise_start_session_(uint8_t server_feature_flags) {
|
||||
#ifdef USE_OTA_ENCRYPTION_PROVISIONED
|
||||
// The key can be cleared between the offer and the client taking it; a
|
||||
// missing key must not turn into a handshake on the all-zeros key
|
||||
if (!this->noise_context_().has_psk()) {
|
||||
ESP_LOGW(TAG, "Key cleared");
|
||||
this->cleanup_connection_();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// Default-init: the frame buffer is always written before it is read, so
|
||||
// skip zeroing its 132 bytes
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
|
||||
+1
-6
@@ -532,13 +532,9 @@ def wizard(path: Path) -> int:
|
||||
safe_print()
|
||||
safe_print("You'll need this key when adding the device to Home Assistant.")
|
||||
sleep(1)
|
||||
|
||||
# The api key also secures OTA updates; no password prompt
|
||||
ota_password = ""
|
||||
else:
|
||||
ssid, psk = "", ""
|
||||
api_encryption_key = None
|
||||
ota_password = ""
|
||||
|
||||
kwargs = {
|
||||
"path": path,
|
||||
@@ -549,10 +545,9 @@ def wizard(path: Path) -> int:
|
||||
"psk": psk,
|
||||
"type": "basic",
|
||||
}
|
||||
# The api key also secures OTA updates, so the wizard sets no OTA password
|
||||
if api_encryption_key:
|
||||
kwargs["api_encryption_key"] = api_encryption_key
|
||||
if ota_password:
|
||||
kwargs["ota_password"] = ota_password
|
||||
|
||||
if not wizard_write(**kwargs):
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user