restored fingerprint functionality

WiFiClientSecure only supports checking a single fingerprint, so support
for multiple fingerprints was removed
This commit is contained in:
hermlon 2024-09-03 23:36:16 +02:00
parent b56eeec9d9
commit d7652bb18a
6 changed files with 48 additions and 10 deletions

View file

@ -1,3 +1,5 @@
import re
from esphome import automation
from esphome.automation import Condition
import esphome.codegen as cg
@ -36,6 +38,7 @@ from esphome.const import (
CONF_REBOOT_TIMEOUT,
CONF_RETAIN,
CONF_SHUTDOWN_MESSAGE,
CONF_SSL_FINGERPRINT,
CONF_STATE_TOPIC,
CONF_TOPIC,
CONF_TOPIC_PREFIX,
@ -196,6 +199,13 @@ def validate_config(value):
return out
def validate_fingerprint(value):
value = cv.string(value)
if re.match(r"^[0-9a-f]{40}$", value) is None:
raise cv.Invalid("fingerprint must be valid SHA1 hash")
return value
CONFIG_SCHEMA = cv.All(
cv.Schema(
{
@ -244,6 +254,9 @@ CONFIG_SCHEMA = cv.All(
),
validate_message_just_topic,
),
cv.Optional(CONF_SSL_FINGERPRINT): cv.All(
cv.only_on_esp8266, cv.string, validate_fingerprint
),
cv.Optional(CONF_KEEPALIVE, default="15s"): cv.positive_time_period_seconds,
cv.Optional(
CONF_REBOOT_TIMEOUT, default="15min"
@ -383,10 +396,19 @@ async def to_code(config):
cg.add(var.set_reboot_timeout(config[CONF_REBOOT_TIMEOUT]))
if CONF_SSL_FINGERPRINT in config:
cg.add_define("USE_MQTT_SECURE_CLIENT")
fingerprint = config[CONF_SSL_FINGERPRINT]
arr = [cg.RawExpression(f"0x{fingerprint[i:i + 2]}") for i in range(0, 40, 2)]
cg.add(var.set_ssl_fingerprint(arr))
if CONF_SKIP_CERT_CN_CHECK in config:
cg.add_define("USE_MQTT_SECURE_CLIENT")
cg.add(var.set_skip_cert_cn_check(config[CONF_SKIP_CERT_CN_CHECK]))
if CONF_CERTIFICATE_AUTHORITY in config:
cg.add_define("USE_MQTT_SECURE_CLIENT")
cg.add(var.set_ca_certificate(config[CONF_CERTIFICATE_AUTHORITY]))
cg.add(var.set_skip_cert_cn_check(config[CONF_SKIP_CERT_CN_CHECK]))
if CONF_CLIENT_CERTIFICATE in config:
cg.add(var.set_cl_certificate(config[CONF_CLIENT_CERTIFICATE]))
cg.add(var.set_cl_key(config[CONF_CLIENT_CERTIFICATE_KEY]))

View file

@ -22,9 +22,12 @@ void MQTTBackendESP8266::initialize_() {
if (this->ca_certificate_str_.has_value()) {
this->ca_certificate_.append(this->ca_certificate_str_.value().c_str());
this->wifi_client_.setTrustAnchors(&this->ca_certificate_);
if (this->skip_cert_cn_check_) {
this->wifi_client_.setInsecure();
}
}
if (this->ssl_fingerprint_.has_value()) {
this->wifi_client_.setFingerprint(this->ssl_fingerprint_.value().data());
}
if (this->skip_cert_cn_check_) {
this->wifi_client_.setInsecure();
}
#endif

View file

@ -93,6 +93,7 @@ class MQTTBackendESP8266 final : public MQTTBackend {
void set_ca_certificate(const std::string &cert) { this->ca_certificate_str_ = cert; }
void set_skip_cert_cn_check(bool skip_check) { this->skip_cert_cn_check_ = skip_check; }
void set_ssl_fingerprint(const std::array<uint8_t, 20> &fingerprint) { this->ssl_fingerprint_ = fingerprint; };
protected:
void initialize_();
@ -119,6 +120,7 @@ class MQTTBackendESP8266 final : public MQTTBackend {
std::string lwt_message_;
std::string client_id_;
optional<std::string> ca_certificate_str_;
optional<std::array<uint8_t, 20>> ssl_fingerprint_;
BearSSL::X509List ca_certificate_;
bool skip_cert_cn_check_{false};

View file

@ -132,14 +132,25 @@ class MQTTClientComponent : public Component {
bool is_discovery_enabled() const;
bool is_discovery_ip_enabled() const;
/** Add a SSL fingerprint to use for TCP SSL connections to the MQTT broker.
*
* @warning This is *not* secure and *not* how SSL is usually done. You'll have to add
* a separate fingerprint for every certificate you use. Additionally, the hashing
* algorithm used here due to the constraints of the MCU, SHA1, is known to be insecure.
*
* @param fingerprint The SSL fingerprint as a 20 value long std::array.
*/
#ifdef USE_ESP8266
void set_ssl_fingerprint(const std::array<uint8_t, 20> &fingerprint) {
this->mqtt_backend_.set_ssl_fingerprint(fingerprint);
};
#endif
void set_ca_certificate(const char *cert) { this->mqtt_backend_.set_ca_certificate(cert); }
void set_skip_cert_cn_check(bool skip_check) { this->mqtt_backend_.set_skip_cert_cn_check(skip_check); }
#ifdef USE_ESP32
void set_cl_certificate(const char *cert) { this->mqtt_backend_.set_cl_certificate(cert); }
void set_cl_key(const char *key) { this->mqtt_backend_.set_cl_key(key); }
#endif
const Availability &get_availability();
/** Set the topic prefix that will be prepended to all topics together with "/". This will, in most cases,

View file

@ -789,7 +789,7 @@ CONF_SPI = "spi"
CONF_SPI_ID = "spi_id"
CONF_SPIKE_REJECTION = "spike_rejection"
CONF_SSID = "ssid"
CONF_SSL_FINGERPRINTS = "ssl_fingerprints"
CONF_SSL_FINGERPRINT = "ssl_fingerprint"
CONF_STARTUP_DELAY = "startup_delay"
CONF_STATE = "state"
CONF_STATE_CLASS = "state_class"

View file

@ -17,7 +17,7 @@ from esphome.const import (
CONF_NAME,
CONF_PASSWORD,
CONF_PORT,
CONF_SSL_FINGERPRINTS,
CONF_SSL_FINGERPRINT,
CONF_TOPIC,
CONF_TOPIC_PREFIX,
CONF_USERNAME,
@ -99,7 +99,7 @@ def prepare(
elif username:
client.username_pw_set(username, password)
if config[CONF_MQTT].get(CONF_SSL_FINGERPRINTS) or config[CONF_MQTT].get(
if config[CONF_MQTT].get(CONF_SSL_FINGERPRINT) or config[CONF_MQTT].get(
CONF_CERTIFICATE_AUTHORITY
):
tls_version = ssl.PROTOCOL_TLS # pylint: disable=no-member
@ -267,6 +267,6 @@ def get_fingerprint(config):
safe_print(f"SHA1 Fingerprint: {color(Fore.CYAN, sha1)}")
safe_print(
f"Copy the string above into mqtt.ssl_fingerprints section of {CORE.config_path}"
f"Add the following line to the mqtt section of {CORE.config_path}:\n {CONF_SSL_FINGERPRINT}: {sha1}"
)
return 0