|
|
- // -----------------------------------------------------------------------------
- // WiFiClientSecure validation helpers
- // -----------------------------------------------------------------------------
-
- #pragma once
-
- #include "../espurna.h"
-
- #if SECURE_CLIENT != SECURE_CLIENT_NONE
-
- #include "../ntp.h"
-
- #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
- #include "ntp_timelib.h"
- #include <WiFiClientSecureBearSSL.h>
- #elif SECURE_CLIENT == SECURE_CLIENT_AXTLS
- #include <WiFiClientSecureAxTLS.h>
- #endif
-
- namespace SecureClientHelpers {
-
- using host_callback_f = std::function<String()>;
- using check_callback_f = std::function<int()>;
- using fp_callback_f = std::function<String()>;
- using cert_callback_f = std::function<const char*()>;
- using mfln_callback_f = std::function<uint16_t()>;
-
- // TODO: workaround for `multiple definition of `SecureClientHelpers::_secureClientCheckAsString(int);'
- inline const char * _secureClientCheckAsString(int check) {
- switch (check) {
- case SECURE_CLIENT_CHECK_NONE: return "no validation";
- case SECURE_CLIENT_CHECK_FINGERPRINT: return "fingerprint validation";
- case SECURE_CLIENT_CHECK_CA: return "CA validation";
- default: return "unknown";
- }
- }
-
- #if SECURE_CLIENT == SECURE_CLIENT_AXTLS
- using SecureClientClass = axTLS::WiFiClientSecure;
-
- struct SecureClientConfig {
- SecureClientConfig(const char* tag, host_callback_f host_cb, check_callback_f check_cb, fp_callback_f fp_cb, bool debug = false) :
- tag(tag),
- on_host(host_cb),
- on_check(check_cb),
- on_fingerprint(fp_cb),
- debug(debug)
- {}
-
- String tag;
- host_callback_f on_host;
- check_callback_f on_check;
- fp_callback_f on_fingerprint;
- bool debug;
- };
-
- struct SecureClientChecks {
-
- SecureClientChecks(SecureClientConfig& config) :
- config(config)
- {}
-
- int getCheck() {
- return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK);
- }
-
- bool beforeConnected(SecureClientClass& client) {
- return true;
- }
-
- // Special condition for legacy client!
- // Otherwise, we are required to connect twice. And it is deemed broken & deprecated anyways...
- bool afterConnected(SecureClientClass& client) {
- bool result = false;
-
- int check = getCheck();
-
- if(config.debug) {
- DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check));
- }
-
- if (check == SECURE_CLIENT_CHECK_NONE) {
- if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str());
- result = true;
- } else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
- if (config.on_fingerprint) {
- char _buffer[60] = {0};
- if (config.on_fingerprint && config.on_host && sslFingerPrintChar(config.on_fingerprint().c_str(), _buffer)) {
- result = client.verify(_buffer, config.on_host().c_str());
- }
- if (!result) DEBUG_MSG_P(PSTR("[%s] Wrong fingerprint, cannot connect\n"), config.tag.c_str());
- }
- } else if (check == SECURE_CLIENT_CHECK_CA) {
- if (config.debug) DEBUG_MSG_P(PSTR("[%s] CA verification is not supported with axTLS client\n"), config.tag.c_str());
- }
-
- return result;
- }
-
- SecureClientConfig& config;
- bool debug;
-
- };
- #endif // SECURE_CLIENT_AXTLS
-
- #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
-
- using SecureClientClass = BearSSL::WiFiClientSecure;
-
- struct SecureClientConfig {
- SecureClientConfig(const char* tag, check_callback_f check_cb, cert_callback_f cert_cb, fp_callback_f fp_cb, mfln_callback_f mfln_cb, bool debug = false) :
- tag(tag),
- on_check(check_cb),
- on_certificate(cert_cb),
- on_fingerprint(fp_cb),
- on_mfln(mfln_cb),
- debug(debug)
- {}
-
- String tag;
- check_callback_f on_check;
- cert_callback_f on_certificate;
- fp_callback_f on_fingerprint;
- mfln_callback_f on_mfln;
- bool debug;
- };
-
- struct SecureClientChecks {
-
- SecureClientChecks(SecureClientConfig& config) :
- config(config)
- {}
-
- int getCheck() {
- return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK);
- }
-
- bool prepareMFLN(SecureClientClass& client) {
- const uint16_t requested_mfln = (config.on_mfln) ? config.on_mfln() : (SECURE_CLIENT_MFLN);
- bool result = false;
- switch (requested_mfln) {
- // default, do nothing
- case 0:
- result = true;
- break;
- // match valid sizes only
- case 512:
- case 1024:
- case 2048:
- case 4096:
- {
- client.setBufferSizes(requested_mfln, requested_mfln);
- result = true;
- if (config.debug) {
- DEBUG_MSG_P(PSTR("[%s] MFLN buffer size set to %u\n"), config.tag.c_str(), requested_mfln);
- }
- break;
- }
- default:
- {
- if (config.debug) {
- DEBUG_MSG_P(PSTR("[%s] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n"), config.tag.c_str());
- }
- }
- }
-
- return result;
- }
-
- bool beforeConnected(SecureClientClass& client) {
- int check = getCheck();
- bool settime = (check == SECURE_CLIENT_CHECK_CA);
-
- if(config.debug) {
- DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check));
- }
-
- if (!ntpSynced() && settime) {
- if (config.debug) DEBUG_MSG_P(PSTR("[%s] Time not synced! Cannot use CA validation\n"), config.tag.c_str());
- return false;
- }
-
- prepareMFLN(client);
-
- if (check == SECURE_CLIENT_CHECK_NONE) {
- if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str());
- client.setInsecure();
- } else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
- uint8_t _buffer[20] = {0};
- if (config.on_fingerprint && sslFingerPrintArray(config.on_fingerprint().c_str(), _buffer)) {
- client.setFingerprint(_buffer);
- }
- } else if (check == SECURE_CLIENT_CHECK_CA) {
- #if NTP_LEGACY_SUPPORT
- client.setX509Time(ntpLocal2UTC(now()));
- #else
- client.setX509Time(now());
- #endif
- if (!certs.getCount()) {
- if (config.on_certificate) certs.append(config.on_certificate());
- }
- client.setTrustAnchors(&certs);
- }
-
- return true;
- }
-
- bool afterConnected(SecureClientClass&) {
- return true;
- }
-
- bool debug;
-
- SecureClientConfig& config;
-
- BearSSL::X509List certs;
-
- };
- #endif // SECURE_CLIENT_BEARSSL
-
- class SecureClient {
-
- public:
-
- SecureClient(SecureClientConfig& config) :
- _config(config),
- _checks(_config),
- _client(std::make_unique<SecureClientClass>())
- {}
-
- bool afterConnected() {
- return _checks.afterConnected(get());
- }
-
- bool beforeConnected() {
- return _checks.beforeConnected(get());
- }
-
- SecureClientClass& get() {
- return *_client.get();
- }
-
- private:
-
- SecureClientConfig _config;
- SecureClientChecks _checks;
- std::unique_ptr<SecureClientClass> _client;
-
- };
-
- };
-
- using SecureClientConfig = SecureClientHelpers::SecureClientConfig;
- using SecureClientChecks = SecureClientHelpers::SecureClientChecks;
- using SecureClient = SecureClientHelpers::SecureClient;
-
- #endif // SECURE_CLIENT != SECURE_CLIENT_NONE
|