mirror of
https://github.com/esphome/esphome.git
synced 2025-09-27 15:42:22 +01:00
Merge branch 'sha256_ota' into integration
This commit is contained in:
@@ -268,14 +268,14 @@ void ESPHomeOTAComponent::handle_data_() {
|
|||||||
// TODO: Remove this entire ifdef block in 2026.1.0
|
// TODO: Remove this entire ifdef block in 2026.1.0
|
||||||
if (client_supports_sha256) {
|
if (client_supports_sha256) {
|
||||||
sha256::SHA256 sha_hasher;
|
sha256::SHA256 sha_hasher;
|
||||||
auth_success = this->perform_hash_auth_(&sha_hasher, this->password_, 16, ota::OTA_RESPONSE_REQUEST_SHA256_AUTH,
|
auth_success = this->perform_hash_auth_(&sha_hasher, this->password_, ota::OTA_RESPONSE_REQUEST_SHA256_AUTH,
|
||||||
LOG_STR("SHA256"), sbuf);
|
LOG_STR("SHA256"), sbuf);
|
||||||
} else {
|
} else {
|
||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
ESP_LOGW(TAG, "Using MD5 auth for compatibility (deprecated)");
|
ESP_LOGW(TAG, "Using MD5 auth for compatibility (deprecated)");
|
||||||
md5::MD5Digest md5_hasher;
|
md5::MD5Digest md5_hasher;
|
||||||
auth_success = this->perform_hash_auth_(&md5_hasher, this->password_, 8, ota::OTA_RESPONSE_REQUEST_AUTH,
|
auth_success =
|
||||||
LOG_STR("MD5"), sbuf);
|
this->perform_hash_auth_(&md5_hasher, this->password_, ota::OTA_RESPONSE_REQUEST_AUTH, LOG_STR("MD5"), sbuf);
|
||||||
#endif // USE_OTA_MD5
|
#endif // USE_OTA_MD5
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@@ -286,7 +286,7 @@ void ESPHomeOTAComponent::handle_data_() {
|
|||||||
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
|
goto error; // NOLINT(cppcoreguidelines-avoid-goto)
|
||||||
}
|
}
|
||||||
sha256::SHA256 sha_hasher;
|
sha256::SHA256 sha_hasher;
|
||||||
auth_success = this->perform_hash_auth_(&sha_hasher, this->password_, 16, ota::OTA_RESPONSE_REQUEST_SHA256_AUTH,
|
auth_success = this->perform_hash_auth_(&sha_hasher, this->password_, ota::OTA_RESPONSE_REQUEST_SHA256_AUTH,
|
||||||
LOG_STR("SHA256"), sbuf);
|
LOG_STR("SHA256"), sbuf);
|
||||||
#endif // ALLOW_OTA_DOWNGRADE_MD5
|
#endif // ALLOW_OTA_DOWNGRADE_MD5
|
||||||
#else
|
#else
|
||||||
@@ -295,7 +295,7 @@ void ESPHomeOTAComponent::handle_data_() {
|
|||||||
#ifdef USE_OTA_MD5
|
#ifdef USE_OTA_MD5
|
||||||
md5::MD5Digest md5_hasher;
|
md5::MD5Digest md5_hasher;
|
||||||
auth_success =
|
auth_success =
|
||||||
this->perform_hash_auth_(&md5_hasher, this->password_, 8, ota::OTA_RESPONSE_REQUEST_AUTH, LOG_STR("MD5"), sbuf);
|
this->perform_hash_auth_(&md5_hasher, this->password_, ota::OTA_RESPONSE_REQUEST_AUTH, LOG_STR("MD5"), sbuf);
|
||||||
#endif // USE_OTA_MD5
|
#endif // USE_OTA_MD5
|
||||||
#endif // USE_OTA_SHA256
|
#endif // USE_OTA_SHA256
|
||||||
|
|
||||||
@@ -528,11 +528,20 @@ void ESPHomeOTAComponent::log_auth_warning_(const LogString *action, const LogSt
|
|||||||
ESP_LOGW(TAG, "Auth: %s %s failed", LOG_STR_ARG(action), LOG_STR_ARG(hash_name));
|
ESP_LOGW(TAG, "Auth: %s %s failed", LOG_STR_ARG(action), LOG_STR_ARG(hash_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper to convert uint32 to big-endian bytes
|
||||||
|
static inline void uint32_to_bytes(uint32_t value, uint8_t *bytes) {
|
||||||
|
bytes[0] = (value >> 24) & 0xFF;
|
||||||
|
bytes[1] = (value >> 16) & 0xFF;
|
||||||
|
bytes[2] = (value >> 8) & 0xFF;
|
||||||
|
bytes[3] = value & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
// Non-template function definition to reduce binary size
|
// Non-template function definition to reduce binary size
|
||||||
bool ESPHomeOTAComponent::perform_hash_auth_(HashBase *hasher, const std::string &password, size_t nonce_size,
|
bool ESPHomeOTAComponent::perform_hash_auth_(HashBase *hasher, const std::string &password, uint8_t auth_request,
|
||||||
uint8_t auth_request, const LogString *name, char *buf) {
|
const LogString *name, char *buf) {
|
||||||
// Get sizes from the hasher
|
// Get sizes from the hasher
|
||||||
const size_t hex_size = hasher->get_size() * 2; // Hex is twice the byte size
|
const size_t hex_size = hasher->get_size() * 2; // Hex is twice the byte size
|
||||||
|
const size_t nonce_len = hasher->get_size() / 4; // Nonce is 1/4 of hash size in bytes
|
||||||
|
|
||||||
// Use the provided buffer for all hex operations
|
// Use the provided buffer for all hex operations
|
||||||
|
|
||||||
@@ -545,25 +554,11 @@ bool ESPHomeOTAComponent::perform_hash_auth_(HashBase *hasher, const std::string
|
|||||||
hasher->init();
|
hasher->init();
|
||||||
|
|
||||||
// Generate nonce seed bytes
|
// Generate nonce seed bytes
|
||||||
uint32_t r1 = random_uint32();
|
uint32_to_bytes(random_uint32(), nonce_bytes);
|
||||||
// Convert first uint32 to bytes (always needed for MD5)
|
if (nonce_len > 4) {
|
||||||
nonce_bytes[0] = (r1 >> 24) & 0xFF;
|
uint32_to_bytes(random_uint32(), nonce_bytes + 4);
|
||||||
nonce_bytes[1] = (r1 >> 16) & 0xFF;
|
|
||||||
nonce_bytes[2] = (r1 >> 8) & 0xFF;
|
|
||||||
nonce_bytes[3] = r1 & 0xFF;
|
|
||||||
|
|
||||||
if (nonce_size == 8) {
|
|
||||||
// MD5: 8 chars = "%08x" format = 4 bytes from one random uint32
|
|
||||||
hasher->add(nonce_bytes, 4);
|
|
||||||
} else {
|
|
||||||
// SHA256: 16 chars = "%08x%08x" format = 8 bytes from two random uint32s
|
|
||||||
uint32_t r2 = random_uint32();
|
|
||||||
nonce_bytes[4] = (r2 >> 24) & 0xFF;
|
|
||||||
nonce_bytes[5] = (r2 >> 16) & 0xFF;
|
|
||||||
nonce_bytes[6] = (r2 >> 8) & 0xFF;
|
|
||||||
nonce_bytes[7] = r2 & 0xFF;
|
|
||||||
hasher->add(nonce_bytes, 8);
|
|
||||||
}
|
}
|
||||||
|
hasher->add(nonce_bytes, nonce_len);
|
||||||
hasher->calculate();
|
hasher->calculate();
|
||||||
|
|
||||||
// Generate and send nonce
|
// Generate and send nonce
|
||||||
|
@@ -32,8 +32,8 @@ class ESPHomeOTAComponent : public ota::OTAComponent {
|
|||||||
void handle_handshake_();
|
void handle_handshake_();
|
||||||
void handle_data_();
|
void handle_data_();
|
||||||
#ifdef USE_OTA_PASSWORD
|
#ifdef USE_OTA_PASSWORD
|
||||||
bool perform_hash_auth_(HashBase *hasher, const std::string &password, size_t nonce_size, uint8_t auth_request,
|
bool perform_hash_auth_(HashBase *hasher, const std::string &password, uint8_t auth_request, const LogString *name,
|
||||||
const LogString *name, char *buf);
|
char *buf);
|
||||||
void log_auth_warning_(const LogString *action, const LogString *hash_name);
|
void log_auth_warning_(const LogString *action, const LogString *hash_name);
|
||||||
#endif // USE_OTA_PASSWORD
|
#endif // USE_OTA_PASSWORD
|
||||||
bool readall_(uint8_t *buf, size_t len);
|
bool readall_(uint8_t *buf, size_t len);
|
||||||
|
@@ -166,7 +166,7 @@ def check_error(data: list[int] | bytes, expect: int | list[int] | None) -> None
|
|||||||
raise OTAError("Error: Authentication invalid. Is the password correct?")
|
raise OTAError("Error: Authentication invalid. Is the password correct?")
|
||||||
if dat == RESPONSE_ERROR_WRITING_FLASH:
|
if dat == RESPONSE_ERROR_WRITING_FLASH:
|
||||||
raise OTAError(
|
raise OTAError(
|
||||||
"Error: Wring OTA data to flash memory failed. See USB logs for more "
|
"Error: Writing OTA data to flash memory failed. See USB logs for more "
|
||||||
"information."
|
"information."
|
||||||
)
|
)
|
||||||
if dat == RESPONSE_ERROR_UPDATE_END:
|
if dat == RESPONSE_ERROR_UPDATE_END:
|
||||||
|
@@ -149,7 +149,7 @@ def test_receive_exactly_socket_error(mock_socket: Mock) -> None:
|
|||||||
(espota2.RESPONSE_ERROR_AUTH_INVALID, "Error: Authentication invalid"),
|
(espota2.RESPONSE_ERROR_AUTH_INVALID, "Error: Authentication invalid"),
|
||||||
(
|
(
|
||||||
espota2.RESPONSE_ERROR_WRITING_FLASH,
|
espota2.RESPONSE_ERROR_WRITING_FLASH,
|
||||||
"Error: Wring OTA data to flash memory failed",
|
"Error: Writing OTA data to flash memory failed",
|
||||||
),
|
),
|
||||||
(espota2.RESPONSE_ERROR_UPDATE_END, "Error: Finishing update failed"),
|
(espota2.RESPONSE_ERROR_UPDATE_END, "Error: Finishing update failed"),
|
||||||
(
|
(
|
||||||
|
Reference in New Issue
Block a user