From 594831d6058e8e204eb655b839564579b740c286 Mon Sep 17 00:00:00 2001 From: david gauchard Date: Thu, 3 Dec 2020 10:35:24 +0100 Subject: [PATCH] WiFiClientSecure: use context (#7680) * move WiFiClientSecure to WiFiClientSecureCtx and add WiFiClientSecure wrapper to handle the context * explicitely disable context copy constructor (similar to operator=) * move (static) probeMaxFragmentLength back from ctx to WiFiClientSecure * route sslclient::status() to context's ::status() --- libraries/ESP8266WiFi/src/BearSSLHelpers.h | 2 +- libraries/ESP8266WiFi/src/WiFiClient.h | 2 +- .../src/WiFiClientSecureBearSSL.cpp | 81 +++++----- .../ESP8266WiFi/src/WiFiClientSecureBearSSL.h | 138 +++++++++++++++--- 4 files changed, 156 insertions(+), 67 deletions(-) diff --git a/libraries/ESP8266WiFi/src/BearSSLHelpers.h b/libraries/ESP8266WiFi/src/BearSSLHelpers.h index c101e8a59..2e2251d8c 100644 --- a/libraries/ESP8266WiFi/src/BearSSLHelpers.h +++ b/libraries/ESP8266WiFi/src/BearSSLHelpers.h @@ -134,7 +134,7 @@ class X509List { class WiFiClientSecure; class Session { - friend class WiFiClientSecure; + friend class WiFiClientSecureCtx; public: Session() { memset(&_session, 0, sizeof(_session)); } diff --git a/libraries/ESP8266WiFi/src/WiFiClient.h b/libraries/ESP8266WiFi/src/WiFiClient.h index 50112c2f9..41c099bfc 100644 --- a/libraries/ESP8266WiFi/src/WiFiClient.h +++ b/libraries/ESP8266WiFi/src/WiFiClient.h @@ -52,7 +52,7 @@ public: WiFiClient(const WiFiClient&); WiFiClient& operator=(const WiFiClient&); - uint8_t status(); + virtual uint8_t status(); virtual int connect(IPAddress ip, uint16_t port) override; virtual int connect(const char *host, uint16_t port) override; virtual int connect(const String& host, uint16_t port); diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp index fde5f5048..c2161cdb4 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.cpp @@ -67,7 +67,7 @@ extern "C" { namespace BearSSL { -void WiFiClientSecure::_clear() { +void WiFiClientSecureCtx::_clear() { // TLS handshake may take more than the 5 second default timeout _timeout = 15000; @@ -91,7 +91,7 @@ void WiFiClientSecure::_clear() { _cipher_cnt = 0; } -void WiFiClientSecure::_clearAuthenticationSettings() { +void WiFiClientSecureCtx::_clearAuthenticationSettings() { _use_insecure = false; _use_fingerprint = false; _use_self_signed = false; @@ -100,7 +100,7 @@ void WiFiClientSecure::_clearAuthenticationSettings() { } -WiFiClientSecure::WiFiClientSecure() : WiFiClient() { +WiFiClientSecureCtx::WiFiClientSecureCtx() : WiFiClient() { _clear(); _clearAuthenticationSettings(); _certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived @@ -108,12 +108,7 @@ WiFiClientSecure::WiFiClientSecure() : WiFiClient() { stack_thunk_add_ref(); } -WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure &rhs) : WiFiClient(rhs) { - *this = rhs; - stack_thunk_add_ref(); -} - -WiFiClientSecure::~WiFiClientSecure() { +WiFiClientSecureCtx::~WiFiClientSecureCtx() { if (_client) { _client->unref(); _client = nullptr; @@ -123,7 +118,7 @@ WiFiClientSecure::~WiFiClientSecure() { stack_thunk_del_ref(); } -WiFiClientSecure::WiFiClientSecure(ClientContext* client, +WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) { _clear(); @@ -140,7 +135,7 @@ WiFiClientSecure::WiFiClientSecure(ClientContext* client, } } -WiFiClientSecure::WiFiClientSecure(ClientContext *client, +WiFiClientSecureCtx::WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) { @@ -158,12 +153,12 @@ WiFiClientSecure::WiFiClientSecure(ClientContext *client, } } -void WiFiClientSecure::setClientRSACert(const X509List *chain, const PrivateKey *sk) { +void WiFiClientSecureCtx::setClientRSACert(const X509List *chain, const PrivateKey *sk) { _chain = chain; _sk = sk; } -void WiFiClientSecure::setClientECCert(const X509List *chain, +void WiFiClientSecureCtx::setClientECCert(const X509List *chain, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) { _chain = chain; _sk = sk; @@ -171,7 +166,7 @@ void WiFiClientSecure::setClientECCert(const X509List *chain, _cert_issuer_key_type = cert_issuer_key_type; } -void WiFiClientSecure::setBufferSizes(int recv, int xmit) { +void WiFiClientSecureCtx::setBufferSizes(int recv, int xmit) { // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) const int MAX_OUT_OVERHEAD = 85; const int MAX_IN_OVERHEAD = 325; @@ -187,7 +182,7 @@ void WiFiClientSecure::setBufferSizes(int recv, int xmit) { _iobuf_out_size = xmit; } -bool WiFiClientSecure::stop(unsigned int maxWaitMs) { +bool WiFiClientSecureCtx::stop(unsigned int maxWaitMs) { bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() // Only if we've already connected, store session params and clear the connection options if (_handshake_done) { @@ -199,19 +194,19 @@ bool WiFiClientSecure::stop(unsigned int maxWaitMs) { return ret; } -bool WiFiClientSecure::flush(unsigned int maxWaitMs) { +bool WiFiClientSecureCtx::flush(unsigned int maxWaitMs) { (void) _run_until(BR_SSL_SENDAPP); return WiFiClient::flush(maxWaitMs); } -int WiFiClientSecure::connect(IPAddress ip, uint16_t port) { +int WiFiClientSecureCtx::connect(IPAddress ip, uint16_t port) { if (!WiFiClient::connect(ip, port)) { return 0; } return _connectSSL(nullptr); } -int WiFiClientSecure::connect(const char* name, uint16_t port) { +int WiFiClientSecureCtx::connect(const char* name, uint16_t port) { IPAddress remote_addr; if (!WiFi.hostByName(name, remote_addr)) { DEBUG_BSSL("connect: Name lookup failure\n"); @@ -224,11 +219,11 @@ int WiFiClientSecure::connect(const char* name, uint16_t port) { return _connectSSL(name); } -int WiFiClientSecure::connect(const String& host, uint16_t port) { +int WiFiClientSecureCtx::connect(const String& host, uint16_t port) { return connect(host.c_str(), port); } -void WiFiClientSecure::_freeSSL() { +void WiFiClientSecureCtx::_freeSSL() { // These are smart pointers and will free if refcnt==0 _sc = nullptr; _sc_svr = nullptr; @@ -245,18 +240,18 @@ void WiFiClientSecure::_freeSSL() { _timeout = 15000; } -bool WiFiClientSecure::_clientConnected() { +bool WiFiClientSecureCtx::_clientConnected() { return (_client && _client->state() == ESTABLISHED); } -uint8_t WiFiClientSecure::connected() { +uint8_t WiFiClientSecureCtx::connected() { if (available() || (_clientConnected() && _handshake_done && (br_ssl_engine_current_state(_eng) != BR_SSL_CLOSED))) { return true; } return false; } -size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) { +size_t WiFiClientSecureCtx::_write(const uint8_t *buf, size_t size, bool pmem) { size_t sent_bytes = 0; if (!connected() || !size || !_handshake_done) { @@ -297,16 +292,16 @@ size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) { return sent_bytes; } -size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) { +size_t WiFiClientSecureCtx::write(const uint8_t *buf, size_t size) { return _write(buf, size, false); } -size_t WiFiClientSecure::write_P(PGM_P buf, size_t size) { +size_t WiFiClientSecureCtx::write_P(PGM_P buf, size_t size) { return _write((const uint8_t *)buf, size, true); } // We have to manually read and send individual chunks. -size_t WiFiClientSecure::write(Stream& stream) { +size_t WiFiClientSecureCtx::write(Stream& stream) { size_t totalSent = 0; size_t countRead; size_t countSent; @@ -329,7 +324,7 @@ size_t WiFiClientSecure::write(Stream& stream) { return totalSent; } -int WiFiClientSecure::read(uint8_t *buf, size_t size) { +int WiFiClientSecureCtx::read(uint8_t *buf, size_t size) { if (!ctx_present() || !_handshake_done) { return -1; } @@ -361,7 +356,7 @@ int WiFiClientSecure::read(uint8_t *buf, size_t size) { return 0; // If we're connected, no error but no read. } -int WiFiClientSecure::read() { +int WiFiClientSecureCtx::read() { uint8_t c; if (1 == read(&c, 1)) { return c; @@ -370,7 +365,7 @@ int WiFiClientSecure::read() { return -1; } -int WiFiClientSecure::available() { +int WiFiClientSecureCtx::available() { if (_recvapp_buf) { return _recvapp_len; // Anything from last call? } @@ -391,7 +386,7 @@ int WiFiClientSecure::available() { return 0; } -int WiFiClientSecure::peek() { +int WiFiClientSecureCtx::peek() { if (!ctx_present() || !available()) { DEBUG_BSSL("peek: Not connected, none left available\n"); return -1; @@ -403,7 +398,7 @@ int WiFiClientSecure::peek() { return -1; } -size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) { +size_t WiFiClientSecureCtx::peekBytes(uint8_t *buffer, size_t length) { size_t to_copy = 0; if (!ctx_present()) { DEBUG_BSSL("peekBytes: Not connected\n"); @@ -426,7 +421,7 @@ size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) { combination of both (the combination matches either). When a match is achieved, this function returns 0. On error, it returns -1. */ -int WiFiClientSecure::_run_until(unsigned target, bool blocking) { +int WiFiClientSecureCtx::_run_until(unsigned target, bool blocking) { if (!ctx_present()) { DEBUG_BSSL("_run_until: Not connected\n"); return -1; @@ -550,7 +545,7 @@ int WiFiClientSecure::_run_until(unsigned target, bool blocking) { return -1; } -bool WiFiClientSecure::_wait_for_handshake() { +bool WiFiClientSecureCtx::_wait_for_handshake() { _handshake_done = false; while (!_handshake_done && _clientConnected()) { int ret = _run_until(BR_SSL_SENDAPP); @@ -575,7 +570,7 @@ static uint8_t htoi (unsigned char c) } // Set a fingerprint by parsing an ASCII string -bool WiFiClientSecure::setFingerprint(const char *fpStr) { +bool WiFiClientSecureCtx::setFingerprint(const char *fpStr) { int idx = 0; uint8_t c, d; uint8_t fp[20]; @@ -968,7 +963,7 @@ extern "C" { } // Set custom list of ciphers -bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) { +bool WiFiClientSecureCtx::setCiphers(const uint16_t *cipherAry, int cipherCount) { _cipher_list = nullptr; _cipher_list = std::shared_ptr(new (std::nothrow) uint16_t[cipherCount], std::default_delete()); if (!_cipher_list.get()) { @@ -980,16 +975,16 @@ bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) { return true; } -bool WiFiClientSecure::setCiphersLessSecure() { +bool WiFiClientSecureCtx::setCiphersLessSecure() { return setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0])); } -bool WiFiClientSecure::setCiphers(std::vector list) { +bool WiFiClientSecureCtx::setCiphers(const std::vector& list) { return setCiphers(&list[0], list.size()); } // Installs the appropriate X509 cert validation method for a client connection -bool WiFiClientSecure::_installClientX509Validator() { +bool WiFiClientSecureCtx::_installClientX509Validator() { if (_use_insecure || _use_fingerprint || _use_self_signed) { // Use common insecure x509 authenticator _x509_insecure = std::make_shared(); @@ -1046,7 +1041,7 @@ bool WiFiClientSecure::_installClientX509Validator() { // Called by connect() to do the actual SSL setup and handshake. // Returns if the SSL handshake succeeded. -bool WiFiClientSecure::_connectSSL(const char* hostName) { +bool WiFiClientSecureCtx::_connectSSL(const char* hostName) { DEBUG_BSSL("_connectSSL: start connection\n"); _freeSSL(); _oom_err = false; @@ -1136,7 +1131,7 @@ bool WiFiClientSecure::_connectSSL(const char* hostName) { // Slightly different X509 setup for servers who want to validate client // certificates, so factor it out as it's used in RSA and EC servers. -bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta) { +bool WiFiClientSecureCtx::_installServerX509Validator(const X509List *client_CA_ta) { if (client_CA_ta) { _ta = client_CA_ta; // X509 minimal validator. Checks dates, cert chain for trusted CA, etc. @@ -1169,7 +1164,7 @@ bool WiFiClientSecure::_installServerX509Validator(const X509List *client_CA_ta) // Called by WiFiServerBearSSL when an RSA cert/key is specified. -bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain, +bool WiFiClientSecureCtx::_connectSSLServerRSA(const X509List *chain, const PrivateKey *sk, const X509List *client_CA_ta) { _freeSSL(); @@ -1205,7 +1200,7 @@ bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain, } // Called by WiFiServerBearSSL when an elliptic curve cert/key is specified. -bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain, +bool WiFiClientSecureCtx::_connectSSLServerEC(const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, const X509List *client_CA_ta) { #ifndef BEARSSL_SSL_BASIC @@ -1251,7 +1246,7 @@ bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain, // Returns an error ID and possibly a string (if dest != null) of the last // BearSSL reported error. -int WiFiClientSecure::getLastSSLError(char *dest, size_t len) { +int WiFiClientSecureCtx::getLastSSLError(char *dest, size_t len) { int err = 0; const char *t = PSTR("OK"); const char *recv_fatal = ""; diff --git a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h index 7cc3af257..9d140b97a 100644 --- a/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h +++ b/libraries/ESP8266WiFi/src/WiFiClientSecureBearSSL.h @@ -31,13 +31,13 @@ namespace BearSSL { -class WiFiClientSecure : public WiFiClient { +class WiFiClientSecureCtx : public WiFiClient { public: - WiFiClientSecure(); - WiFiClientSecure(const WiFiClientSecure &rhs); - ~WiFiClientSecure() override; + WiFiClientSecureCtx(); + WiFiClientSecureCtx(const WiFiClientSecure &rhs) = delete; + ~WiFiClientSecureCtx() override; - WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically + WiFiClientSecureCtx& operator=(const WiFiClientSecureCtx&) = delete; int connect(IPAddress ip, uint16_t port) override; int connect(const String& host, uint16_t port) override; @@ -46,12 +46,6 @@ class WiFiClientSecure : public WiFiClient { uint8_t connected() override; size_t write(const uint8_t *buf, size_t size) override; size_t write_P(PGM_P buf, size_t size) override; - size_t write(const char *buf) { - return write((const uint8_t*)buf, strlen(buf)); - } - size_t write_P(const char *buf) { - return write_P((PGM_P)buf, strlen_P(buf)); - } size_t write(Stream& stream); // Note this is not virtual int read(uint8_t *buf, size_t size) override; int available() override; @@ -123,14 +117,9 @@ class WiFiClientSecure : public WiFiClient { // Select specific ciphers (i.e. optimize for speed over security) // These may be in PROGMEM or RAM, either will run properly bool setCiphers(const uint16_t *cipherAry, int cipherCount); - bool setCiphers(std::vector list); + bool setCiphers(const std::vector& list); bool setCiphersLessSecure(); // Only use the limited set of RSA ciphers without EC - // Check for Maximum Fragment Length support for given len before connection (possibly insecure) - static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len); - static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len); - static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len); - protected: bool _connectSSL(const char *hostName); // Do initial SSL handshake @@ -188,10 +177,10 @@ class WiFiClientSecure : public WiFiClient { unsigned _cert_issuer_key_type; // Methods for handling server.available() call which returns a client connection. - friend class WiFiServerSecure; // Server needs to access these constructors - WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, + friend class WiFiClientSecure; // access to private context constructors + WiFiClientSecureCtx(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); - WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk, + WiFiClientSecureCtx(ClientContext* client, const X509List *chain, const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta); // RSA keyed server @@ -205,8 +194,113 @@ class WiFiClientSecure : public WiFiClient { bool _installServerX509Validator(const X509List *client_CA_ta); // Setup X509 client cert validation, if supplied uint8_t *_streamLoad(Stream& stream, size_t size); -}; +}; // class WiFiClientSecureCtx -}; + +class WiFiClientSecure : public WiFiClient { + + // WiFiClient's "ClientContext* _client" is always nullptr in this class. + // Instead, all virtual functions call their counterpart in "WiFiClientecureCtx* _ctx" + // which also derives from WiFiClient (this parent is the one which is eventually used) + + public: + + WiFiClientSecure():_ctx(new WiFiClientSecureCtx()) { } + WiFiClientSecure(const WiFiClientSecure &rhs): WiFiClient(), _ctx(rhs._ctx) { } + ~WiFiClientSecure() override { _ctx = nullptr; } + + WiFiClientSecure& operator=(const WiFiClientSecure&) = default; // The shared-ptrs handle themselves automatically + + uint8_t status() override { return _ctx->status(); } + int connect(IPAddress ip, uint16_t port) override { return _ctx->connect(ip, port); } + int connect(const String& host, uint16_t port) override { return _ctx->connect(host, port); } + int connect(const char* name, uint16_t port) override { return _ctx->connect(name, port); } + + uint8_t connected() override { return _ctx->connected(); } + size_t write(const uint8_t *buf, size_t size) override { return _ctx->write(buf, size); } + size_t write_P(PGM_P buf, size_t size) override { return _ctx->write_P(buf, size); } + size_t write(const char *buf) { return write((const uint8_t*)buf, strlen(buf)); } + size_t write_P(const char *buf) { return write_P((PGM_P)buf, strlen_P(buf)); } + size_t write(Stream& stream) /* Note this is not virtual */ { return _ctx->write(stream); } + int read(uint8_t *buf, size_t size) override { return _ctx->read(buf, size); } + int available() override { return _ctx->available(); } + int read() override { return _ctx->read(); } + int peek() override { return _ctx->peek(); } + size_t peekBytes(uint8_t *buffer, size_t length) override { return _ctx->peekBytes(buffer, length); } + bool flush(unsigned int maxWaitMs) { return _ctx->flush(maxWaitMs); } + bool stop(unsigned int maxWaitMs) { return _ctx->stop(maxWaitMs); } + void flush() override { (void)flush(0); } + void stop() override { (void)stop(0); } + + // Allow sessions to be saved/restored automatically to a memory area + void setSession(Session *session) { _ctx->setSession(session); } + + // Don't validate the chain, just accept whatever is given. VERY INSECURE! + void setInsecure() { _ctx->setInsecure(); } + + // Assume a given public key, don't validate or use cert info at all + void setKnownKey(const PublicKey *pk, unsigned usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN) { + _ctx->setKnownKey(pk, usages); + } + // Only check SHA1 fingerprint of certificate + bool setFingerprint(const uint8_t fingerprint[20]) { + return _ctx->setFingerprint(fingerprint); + } + bool setFingerprint(const char *fpStr) { return _ctx->setFingerprint(fpStr); } + // Accept any certificate that's self-signed + void allowSelfSignedCerts() { _ctx->allowSelfSignedCerts(); } + + // Install certificates of trusted CAs or specific site + void setTrustAnchors(const X509List *ta) { _ctx->setTrustAnchors(ta); } + // In cases when NTP is not used, app must set a time manually to check cert validity + void setX509Time(time_t now) { _ctx->setX509Time(now); } + // Install a client certificate for this connection, in case the server requires it (i.e. MQTT) + void setClientRSACert(const X509List *cert, const PrivateKey *sk) { _ctx->setClientRSACert(cert, sk); } + void setClientECCert(const X509List *cert, const PrivateKey *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type) { + _ctx->setClientECCert(cert, sk, allowed_usages, cert_issuer_key_type); + } + + // Sets the requested buffer size for transmit and receive + void setBufferSizes(int recv, int xmit) { _ctx->setBufferSizes(recv, xmit); } + + // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) + int getMFLNStatus() { return _ctx->getMFLNStatus(); } + + // Return an error code and possibly a text string in a passed-in buffer with last SSL failure + int getLastSSLError(char *dest = NULL, size_t len = 0) { return _ctx->getLastSSLError(dest, len); } + + // Attach a preconfigured certificate store + void setCertStore(CertStore *certStore) { _ctx->setCertStore(certStore); } + + // Select specific ciphers (i.e. optimize for speed over security) + // These may be in PROGMEM or RAM, either will run properly + bool setCiphers(const uint16_t *cipherAry, int cipherCount) { return _ctx->setCiphers(cipherAry, cipherCount); } + bool setCiphers(const std::vector list) { return _ctx->setCiphers(list); } + bool setCiphersLessSecure() { return _ctx->setCiphersLessSecure(); } // Only use the limited set of RSA ciphers without EC + + // Check for Maximum Fragment Length support for given len before connection (possibly insecure) + static bool probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len); + static bool probeMaxFragmentLength(const char *hostname, uint16_t port, uint16_t len); + static bool probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len); + + private: + std::shared_ptr _ctx; + + // Methods for handling server.available() call which returns a client connection. + friend class WiFiServerSecure; // Server needs to access these constructors + WiFiClientSecure(ClientContext *client, const X509List *chain, unsigned cert_issuer_key_type, + const PrivateKey *sk, int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta): + _ctx(new WiFiClientSecureCtx(client, chain, cert_issuer_key_type, sk, iobuf_in_size, iobuf_out_size, client_CA_ta)) { + } + + WiFiClientSecure(ClientContext* client, const X509List *chain, const PrivateKey *sk, + int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta): + _ctx(new WiFiClientSecureCtx(client, chain, sk, iobuf_in_size, iobuf_out_size, client_CA_ta)) { + } + +}; // class WiFiClientSecure + +}; // namespace BearSSL #endif