mirror of
https://github.com/esp8266/Arduino.git
synced 2025-04-21 10:26:06 +03:00
1514 lines
54 KiB
C++
1514 lines
54 KiB
C++
/*
|
|
WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
|
|
- Mostly compatible with Arduino WiFi shield library and standard
|
|
WiFiClient/ServerSecure (except for certificate handling).
|
|
|
|
Copyright (c) 2018 Earle F. Philhower, III
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#define LWIP_INTERNAL
|
|
|
|
#include <list>
|
|
#include <errno.h>
|
|
#include <algorithm>
|
|
|
|
extern "C" {
|
|
#include "osapi.h"
|
|
#include "ets_sys.h"
|
|
}
|
|
#include "debug.h"
|
|
#include "ESP8266WiFi.h"
|
|
#include "WiFiClient.h"
|
|
#include "WiFiClientSecureBearSSL.h"
|
|
#include "StackThunk.h"
|
|
#include "lwip/opt.h"
|
|
#include "lwip/ip.h"
|
|
#include "lwip/tcp.h"
|
|
#include "lwip/inet.h"
|
|
#include "lwip/netif.h"
|
|
#include <include/ClientContext.h>
|
|
#include "c_types.h"
|
|
#include "coredecls.h"
|
|
|
|
#if !CORE_MOCK
|
|
|
|
// The BearSSL thunks in use for now
|
|
#define br_ssl_engine_recvapp_ack thunk_br_ssl_engine_recvapp_ack
|
|
#define br_ssl_engine_recvapp_buf thunk_br_ssl_engine_recvapp_buf
|
|
#define br_ssl_engine_recvrec_ack thunk_br_ssl_engine_recvrec_ack
|
|
#define br_ssl_engine_recvrec_buf thunk_br_ssl_engine_recvrec_buf
|
|
#define br_ssl_engine_sendapp_ack thunk_br_ssl_engine_sendapp_ack
|
|
#define br_ssl_engine_sendapp_buf thunk_br_ssl_engine_sendapp_buf
|
|
#define br_ssl_engine_sendrec_ack thunk_br_ssl_engine_sendrec_ack
|
|
#define br_ssl_engine_sendrec_buf thunk_br_ssl_engine_sendrec_buf
|
|
|
|
#endif
|
|
|
|
#ifdef DEBUG_ESP_SSL
|
|
#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__)
|
|
#else
|
|
#define DEBUG_BSSL(...)
|
|
#endif
|
|
|
|
namespace BearSSL {
|
|
|
|
void WiFiClientSecure::_clear() {
|
|
// TLS handshake may take more than the 5 second default timeout
|
|
_timeout = 15000;
|
|
|
|
_sc = nullptr;
|
|
_sc_svr = nullptr;
|
|
_eng = nullptr;
|
|
_x509_minimal = nullptr;
|
|
_x509_insecure = nullptr;
|
|
_x509_knownkey = nullptr;
|
|
_iobuf_in = nullptr;
|
|
_iobuf_out = nullptr;
|
|
_now = 0; // You can override or ensure time() is correct w/configTime
|
|
_ta = nullptr;
|
|
setBufferSizes(16384, 512); // Minimum safe
|
|
_handshake_done = false;
|
|
_recvapp_buf = nullptr;
|
|
_recvapp_len = 0;
|
|
_oom_err = false;
|
|
_session = nullptr;
|
|
_cipher_list = nullptr;
|
|
_cipher_cnt = 0;
|
|
}
|
|
|
|
void WiFiClientSecure::_clearAuthenticationSettings() {
|
|
_use_insecure = false;
|
|
_use_fingerprint = false;
|
|
_use_self_signed = false;
|
|
_knownkey = nullptr;
|
|
_sk = nullptr;
|
|
_ta = nullptr;
|
|
_axtls_ta = nullptr;
|
|
_axtls_chain = nullptr;
|
|
_axtls_sk = nullptr;
|
|
}
|
|
|
|
|
|
WiFiClientSecure::WiFiClientSecure() : WiFiClient() {
|
|
_clear();
|
|
_clearAuthenticationSettings();
|
|
_certStore = nullptr; // Don't want to remove cert store on a clear, should be long lived
|
|
stack_thunk_add_ref();
|
|
}
|
|
|
|
WiFiClientSecure::WiFiClientSecure(const WiFiClientSecure &rhs) : WiFiClient(rhs) {
|
|
*this = rhs;
|
|
stack_thunk_add_ref();
|
|
}
|
|
|
|
WiFiClientSecure::~WiFiClientSecure() {
|
|
if (_client) {
|
|
_client->unref();
|
|
_client = nullptr;
|
|
}
|
|
_cipher_list = nullptr; // std::shared will free if last reference
|
|
_freeSSL();
|
|
stack_thunk_del_ref();
|
|
// Clean up any dangling axtls compat structures, if needed
|
|
_axtls_ta = nullptr;
|
|
_axtls_chain = nullptr;
|
|
_axtls_sk = nullptr;
|
|
}
|
|
|
|
WiFiClientSecure::WiFiClientSecure(ClientContext* client,
|
|
const X509List *chain, const PrivateKey *sk,
|
|
int iobuf_in_size, int iobuf_out_size, const X509List *client_CA_ta) {
|
|
_clear();
|
|
_clearAuthenticationSettings();
|
|
stack_thunk_add_ref();
|
|
_iobuf_in_size = iobuf_in_size;
|
|
_iobuf_out_size = iobuf_out_size;
|
|
_client = client;
|
|
_client->ref();
|
|
if (!_connectSSLServerRSA(chain, sk, client_CA_ta)) {
|
|
_client->unref();
|
|
_client = nullptr;
|
|
_clear();
|
|
}
|
|
}
|
|
|
|
WiFiClientSecure::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) {
|
|
_clear();
|
|
_clearAuthenticationSettings();
|
|
stack_thunk_add_ref();
|
|
_iobuf_in_size = iobuf_in_size;
|
|
_iobuf_out_size = iobuf_out_size;
|
|
_client = client;
|
|
_client->ref();
|
|
if (!_connectSSLServerEC(chain, cert_issuer_key_type, sk, client_CA_ta)) {
|
|
_client->unref();
|
|
_client = nullptr;
|
|
_clear();
|
|
}
|
|
}
|
|
|
|
void WiFiClientSecure::setClientRSACert(const X509List *chain, const PrivateKey *sk) {
|
|
_chain = chain;
|
|
_sk = sk;
|
|
}
|
|
|
|
void WiFiClientSecure::setClientECCert(const X509List *chain,
|
|
const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
|
_chain = chain;
|
|
_sk = sk;
|
|
_allowed_usages = allowed_usages;
|
|
_cert_issuer_key_type = cert_issuer_key_type;
|
|
}
|
|
|
|
void WiFiClientSecure::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;
|
|
|
|
// The data buffers must be between 512B and 16KB
|
|
recv = std::max(512, std::min(16384, recv));
|
|
xmit = std::max(512, std::min(16384, xmit));
|
|
|
|
// Add in overhead for SSL protocol
|
|
recv += MAX_IN_OVERHEAD;
|
|
xmit += MAX_OUT_OVERHEAD;
|
|
_iobuf_in_size = recv;
|
|
_iobuf_out_size = xmit;
|
|
}
|
|
|
|
bool WiFiClientSecure::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) {
|
|
if (_session) {
|
|
br_ssl_engine_get_session_parameters(_eng, _session->getSession());
|
|
}
|
|
}
|
|
_freeSSL();
|
|
return ret;
|
|
}
|
|
|
|
bool WiFiClientSecure::flush(unsigned int maxWaitMs) {
|
|
(void) _run_until(BR_SSL_SENDAPP);
|
|
return WiFiClient::flush(maxWaitMs);
|
|
}
|
|
|
|
int WiFiClientSecure::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) {
|
|
IPAddress remote_addr;
|
|
if (!WiFi.hostByName(name, remote_addr)) {
|
|
DEBUG_BSSL("connect: Name loopup failure\n");
|
|
return 0;
|
|
}
|
|
if (!WiFiClient::connect(remote_addr, port)) {
|
|
DEBUG_BSSL("connect: Unable to connect TCP socket\n");
|
|
return 0;
|
|
}
|
|
return _connectSSL(name);
|
|
}
|
|
|
|
int WiFiClientSecure::connect(const String& host, uint16_t port) {
|
|
return connect(host.c_str(), port);
|
|
}
|
|
|
|
void WiFiClientSecure::_freeSSL() {
|
|
// These are smart pointers and will free if refcnt==0
|
|
_sc = nullptr;
|
|
_sc_svr = nullptr;
|
|
_x509_minimal = nullptr;
|
|
_x509_insecure = nullptr;
|
|
_x509_knownkey = nullptr;
|
|
_iobuf_in = nullptr;
|
|
_iobuf_out = nullptr;
|
|
// Reset non-allocated ptrs (pointing to bits potentially free'd above)
|
|
_recvapp_buf = nullptr;
|
|
_recvapp_len = 0;
|
|
// This connection is toast
|
|
_handshake_done = false;
|
|
}
|
|
|
|
bool WiFiClientSecure::_clientConnected() {
|
|
return (_client && _client->state() == ESTABLISHED);
|
|
}
|
|
|
|
uint8_t WiFiClientSecure::connected() {
|
|
if (available() || (_clientConnected() && _handshake_done)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
size_t WiFiClientSecure::_write(const uint8_t *buf, size_t size, bool pmem) {
|
|
size_t sent_bytes = 0;
|
|
|
|
if (!connected() || !size || !_handshake_done) {
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
// Ensure we yield if we need multiple fragments to avoid WDT
|
|
if (sent_bytes) {
|
|
optimistic_yield(1000);
|
|
}
|
|
|
|
// Get BearSSL to a state where we can send
|
|
if (_run_until(BR_SSL_SENDAPP) < 0) {
|
|
break;
|
|
}
|
|
|
|
if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
|
|
size_t sendapp_len;
|
|
unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len);
|
|
int to_send = size > sendapp_len ? sendapp_len : size;
|
|
if (pmem) {
|
|
memcpy_P(sendapp_buf, buf, to_send);
|
|
} else {
|
|
memcpy(sendapp_buf, buf, to_send);
|
|
}
|
|
br_ssl_engine_sendapp_ack(_eng, to_send);
|
|
br_ssl_engine_flush(_eng, 0);
|
|
flush();
|
|
buf += to_send;
|
|
sent_bytes += to_send;
|
|
size -= to_send;
|
|
} else {
|
|
break;
|
|
}
|
|
} while (size);
|
|
|
|
return sent_bytes;
|
|
}
|
|
|
|
size_t WiFiClientSecure::write(const uint8_t *buf, size_t size) {
|
|
return _write(buf, size, false);
|
|
}
|
|
|
|
size_t WiFiClientSecure::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 totalSent = 0;
|
|
size_t countRead;
|
|
size_t countSent;
|
|
|
|
if (!connected() || !_handshake_done) {
|
|
DEBUG_BSSL("write: Connect/handshake not completed yet\n");
|
|
return 0;
|
|
}
|
|
|
|
do {
|
|
uint8_t temp[256]; // Temporary chunk size same as ClientContext
|
|
countSent = 0;
|
|
countRead = stream.readBytes(temp, sizeof(temp));
|
|
if (countRead) {
|
|
countSent = _write((const uint8_t*)temp, countRead, true);
|
|
totalSent += countSent;
|
|
}
|
|
yield(); // Feed the WDT
|
|
} while ((countSent == countRead) && (countSent > 0));
|
|
return totalSent;
|
|
}
|
|
|
|
int WiFiClientSecure::read(uint8_t *buf, size_t size) {
|
|
if (!ctx_present() || !_handshake_done) {
|
|
return -1;
|
|
}
|
|
|
|
int avail = available();
|
|
bool conn = connected();
|
|
if (!avail && conn) {
|
|
return 0; // We're still connected, but nothing to read
|
|
}
|
|
if (!avail && !conn) {
|
|
DEBUG_BSSL("read: Not connected, none left available\n");
|
|
return -1;
|
|
}
|
|
|
|
if (avail) {
|
|
// Take data from the recvapp buffer
|
|
int to_copy = _recvapp_len < size ? _recvapp_len : size;
|
|
memcpy(buf, _recvapp_buf, to_copy);
|
|
br_ssl_engine_recvapp_ack(_eng, to_copy);
|
|
_recvapp_buf = nullptr;
|
|
_recvapp_len = 0;
|
|
return to_copy;
|
|
}
|
|
|
|
if (!conn) {
|
|
DEBUG_BSSL("read: Not connected\n");
|
|
return -1;
|
|
}
|
|
return 0; // If we're connected, no error but no read.
|
|
}
|
|
|
|
int WiFiClientSecure::read() {
|
|
uint8_t c;
|
|
if (1 == read(&c, 1)) {
|
|
return c;
|
|
}
|
|
DEBUG_BSSL("read: failed\n");
|
|
return -1;
|
|
}
|
|
|
|
int WiFiClientSecure::available() {
|
|
if (_recvapp_buf) {
|
|
return _recvapp_len; // Anything from last call?
|
|
}
|
|
_recvapp_buf = nullptr;
|
|
_recvapp_len = 0;
|
|
if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) {
|
|
return 0;
|
|
}
|
|
int st = br_ssl_engine_current_state(_eng);
|
|
if (st == BR_SSL_CLOSED) {
|
|
return 0; // Nothing leftover, SSL is closed
|
|
}
|
|
if (st & BR_SSL_RECVAPP) {
|
|
_recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len);
|
|
return _recvapp_len;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int WiFiClientSecure::peek() {
|
|
if (!ctx_present() || !available()) {
|
|
DEBUG_BSSL("peek: Not connected, none left available\n");
|
|
return -1;
|
|
}
|
|
if (_recvapp_buf && _recvapp_len) {
|
|
return _recvapp_buf[0];
|
|
}
|
|
DEBUG_BSSL("peek: No data left\n");
|
|
return -1;
|
|
}
|
|
|
|
size_t WiFiClientSecure::peekBytes(uint8_t *buffer, size_t length) {
|
|
size_t to_copy = 0;
|
|
if (!ctx_present()) {
|
|
DEBUG_BSSL("peekBytes: Not connected\n");
|
|
return 0;
|
|
}
|
|
|
|
_startMillis = millis();
|
|
while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) {
|
|
yield();
|
|
}
|
|
|
|
to_copy = _recvapp_len < length ? _recvapp_len : length;
|
|
memcpy(buffer, _recvapp_buf, to_copy);
|
|
return to_copy;
|
|
}
|
|
|
|
/* --- Copied almost verbatim from BEARSSL SSL_IO.C ---
|
|
Run the engine, until the specified target state is achieved, or
|
|
an error occurs. The target state is SENDAPP, RECVAPP, or the
|
|
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) {
|
|
if (!ctx_present()) {
|
|
DEBUG_BSSL("_run_until: Not connected\n");
|
|
return -1;
|
|
}
|
|
for (int no_work = 0; blocking || no_work < 2;) {
|
|
if (blocking) {
|
|
// Only for blocking operations can we afford to yield()
|
|
optimistic_yield(100);
|
|
}
|
|
|
|
int state;
|
|
state = br_ssl_engine_current_state(_eng);
|
|
if (state & BR_SSL_CLOSED) {
|
|
return -1;
|
|
}
|
|
|
|
if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) {
|
|
return (state & target) ? 0 : -1;
|
|
}
|
|
|
|
/*
|
|
If there is some record data to send, do it. This takes
|
|
precedence over everything else.
|
|
*/
|
|
if (state & BR_SSL_SENDREC) {
|
|
unsigned char *buf;
|
|
size_t len;
|
|
int wlen;
|
|
|
|
buf = br_ssl_engine_sendrec_buf(_eng, &len);
|
|
wlen = WiFiClient::write(buf, len);
|
|
if (wlen <= 0) {
|
|
/*
|
|
If we received a close_notify and we
|
|
still send something, then we have our
|
|
own response close_notify to send, and
|
|
the peer is allowed by RFC 5246 not to
|
|
wait for it.
|
|
*/
|
|
return -1;
|
|
}
|
|
if (wlen > 0) {
|
|
br_ssl_engine_sendrec_ack(_eng, wlen);
|
|
}
|
|
no_work = 0;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
If we reached our target, then we are finished.
|
|
*/
|
|
if (state & target) {
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
If some application data must be read, and we did not
|
|
exit, then this means that we are trying to write data,
|
|
and that's not possible until the application data is
|
|
read. This may happen if using a shared in/out buffer,
|
|
and the underlying protocol is not strictly half-duplex.
|
|
This is unrecoverable here, so we report an error.
|
|
*/
|
|
if (state & BR_SSL_RECVAPP) {
|
|
DEBUG_BSSL("_run_until: Fatal protocol state\n");
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
If we reached that point, then either we are trying
|
|
to read data and there is some, or the engine is stuck
|
|
until a new record is obtained.
|
|
*/
|
|
if (state & BR_SSL_RECVREC) {
|
|
if (WiFiClient::available()) {
|
|
unsigned char *buf;
|
|
size_t len;
|
|
int rlen;
|
|
|
|
buf = br_ssl_engine_recvrec_buf(_eng, &len);
|
|
rlen = WiFiClient::read(buf, len);
|
|
if (rlen < 0) {
|
|
return -1;
|
|
}
|
|
if (rlen > 0) {
|
|
br_ssl_engine_recvrec_ack(_eng, rlen);
|
|
}
|
|
no_work = 0;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
We can reach that point if the target RECVAPP, and
|
|
the state contains SENDAPP only. This may happen with
|
|
a shared in/out buffer. In that case, we must flush
|
|
the buffered data to "make room" for a new incoming
|
|
record.
|
|
*/
|
|
br_ssl_engine_flush(_eng, 0);
|
|
|
|
no_work++; // We didn't actually advance here
|
|
}
|
|
// We only get here if we ran through the loop without getting anything done
|
|
return -1;
|
|
}
|
|
|
|
bool WiFiClientSecure::_wait_for_handshake() {
|
|
_handshake_done = false;
|
|
while (!_handshake_done && _clientConnected()) {
|
|
int ret = _run_until(BR_SSL_SENDAPP);
|
|
if (ret < 0) {
|
|
DEBUG_BSSL("_wait_for_handshake: failed\n");
|
|
break;
|
|
}
|
|
if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
|
|
_handshake_done = true;
|
|
}
|
|
optimistic_yield(1000);
|
|
}
|
|
return _handshake_done;
|
|
}
|
|
|
|
static uint8_t htoi (unsigned char c)
|
|
{
|
|
if (c>='0' && c <='9') return c - '0';
|
|
else if (c>='A' && c<='F') return 10 + c - 'A';
|
|
else if (c>='a' && c<='f') return 10 + c - 'a';
|
|
else return 255;
|
|
}
|
|
|
|
// Set a fingerprint by parsing an ASCII string
|
|
bool WiFiClientSecure::setFingerprint(const char *fpStr) {
|
|
int idx = 0;
|
|
uint8_t c, d;
|
|
uint8_t fp[20];
|
|
|
|
while (idx < 20) {
|
|
c = pgm_read_byte(fpStr++);
|
|
if (!c) break; // String ended, done processing
|
|
d = pgm_read_byte(fpStr++);
|
|
if (!d) {
|
|
DEBUG_BSSL("setFingerprint: FP too short\n");
|
|
return false; // Only half of the last hex digit, error
|
|
}
|
|
c = htoi(c);
|
|
d = htoi(d);
|
|
if ((c>15) || (d>15)) {
|
|
DEBUG_BSSL("setFingerprint: Invalid char\n");
|
|
return false; // Error in one of the hex characters
|
|
}
|
|
fp[idx++] = (c<<4)|d;
|
|
|
|
// Skip 0 or more spaces or colons
|
|
while ( pgm_read_byte(fpStr) && (pgm_read_byte(fpStr)==' ' || pgm_read_byte(fpStr)==':') ) {
|
|
fpStr++;
|
|
}
|
|
}
|
|
if ((idx != 20) || pgm_read_byte(fpStr)) {
|
|
DEBUG_BSSL("setFingerprint: Garbage at end of fp\n");
|
|
return false; // Garbage at EOL or we didn't have enough hex digits
|
|
}
|
|
return setFingerprint(fp);
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
// BearSSL doesn't define a true insecure decoder, so we make one ourselves
|
|
// from the simple parser. It generates the issuer and subject hashes and
|
|
// the SHA1 fingerprint, only one (or none!) of which will be used to
|
|
// "verify" the certificate.
|
|
|
|
// Private x509 decoder state
|
|
struct br_x509_insecure_context {
|
|
const br_x509_class *vtable;
|
|
bool done_cert;
|
|
const uint8_t *match_fingerprint;
|
|
br_sha1_context sha1_cert;
|
|
bool allow_self_signed;
|
|
br_sha256_context sha256_subject;
|
|
br_sha256_context sha256_issuer;
|
|
br_x509_decoder_context ctx;
|
|
};
|
|
|
|
// Callback for the x509_minimal subject DN
|
|
static void insecure_subject_dn_append(void *ctx, const void *buf, size_t len) {
|
|
br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx;
|
|
br_sha256_update(&xc->sha256_subject, buf, len);
|
|
}
|
|
|
|
// Callback for the x509_minimal issuer DN
|
|
static void insecure_issuer_dn_append(void *ctx, const void *buf, size_t len) {
|
|
br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx;
|
|
br_sha256_update(&xc->sha256_issuer, buf, len);
|
|
}
|
|
|
|
// Callback on the first byte of any certificate
|
|
static void insecure_start_chain(const br_x509_class **ctx, const char *server_name) {
|
|
br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx;
|
|
br_x509_decoder_init(&xc->ctx, insecure_subject_dn_append, xc, insecure_issuer_dn_append, xc);
|
|
xc->done_cert = false;
|
|
br_sha1_init(&xc->sha1_cert);
|
|
br_sha256_init(&xc->sha256_subject);
|
|
br_sha256_init(&xc->sha256_issuer);
|
|
(void)server_name;
|
|
}
|
|
|
|
// Callback for each certificate present in the chain (but only operates
|
|
// on the first one by design).
|
|
static void insecure_start_cert(const br_x509_class **ctx, uint32_t length) {
|
|
(void) ctx;
|
|
(void) length;
|
|
}
|
|
|
|
// Callback for each byte stream in the chain. Only process first cert.
|
|
static void insecure_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) {
|
|
br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx;
|
|
// Don't process anything but the first certificate in the chain
|
|
if (!xc->done_cert) {
|
|
br_sha1_update(&xc->sha1_cert, buf, len);
|
|
br_x509_decoder_push(&xc->ctx, (const void*)buf, len);
|
|
}
|
|
}
|
|
|
|
// Callback on individual cert end.
|
|
static void insecure_end_cert(const br_x509_class **ctx) {
|
|
br_x509_insecure_context *xc = (br_x509_insecure_context *)ctx;
|
|
xc->done_cert = true;
|
|
}
|
|
|
|
// Callback when complete chain has been parsed.
|
|
// Return 0 on validation success, !0 on validation error
|
|
static unsigned insecure_end_chain(const br_x509_class **ctx) {
|
|
const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx;
|
|
if (!xc->done_cert) {
|
|
DEBUG_BSSL("insecure_end_chain: No cert seen\n");
|
|
return 1; // error
|
|
}
|
|
|
|
// Handle SHA1 fingerprint matching
|
|
char res[20];
|
|
br_sha1_out(&xc->sha1_cert, res);
|
|
if (xc->match_fingerprint && memcmp(res, xc->match_fingerprint, sizeof(res))) {
|
|
DEBUG_BSSL("insecure_end_chain: Received cert FP doesn't match\n");
|
|
return BR_ERR_X509_NOT_TRUSTED;
|
|
}
|
|
|
|
// Handle self-signer certificate acceptance
|
|
char res_issuer[32];
|
|
char res_subject[32];
|
|
br_sha256_out(&xc->sha256_issuer, res_issuer);
|
|
br_sha256_out(&xc->sha256_subject, res_subject);
|
|
if (xc->allow_self_signed && memcmp(res_subject, res_issuer, sizeof(res_issuer))) {
|
|
DEBUG_BSSL("insecure_end_chain: Didn't get self-signed cert\n");
|
|
return BR_ERR_X509_NOT_TRUSTED;
|
|
}
|
|
|
|
// Default (no validation at all) or no errors in prior checks = success.
|
|
return 0;
|
|
}
|
|
|
|
// Return the public key from the validator (set by x509_minimal)
|
|
static const br_x509_pkey *insecure_get_pkey(const br_x509_class *const *ctx, unsigned *usages) {
|
|
const br_x509_insecure_context *xc = (const br_x509_insecure_context *)ctx;
|
|
if (usages != NULL) {
|
|
*usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure!
|
|
}
|
|
return &xc->ctx.pkey;
|
|
}
|
|
|
|
// Set up the x509 insecure data structures for BearSSL core to use.
|
|
void br_x509_insecure_init(br_x509_insecure_context *ctx, int _use_fingerprint, const uint8_t _fingerprint[20], int _allow_self_signed) {
|
|
static const br_x509_class br_x509_insecure_vtable PROGMEM = {
|
|
sizeof(br_x509_insecure_context),
|
|
insecure_start_chain,
|
|
insecure_start_cert,
|
|
insecure_append,
|
|
insecure_end_cert,
|
|
insecure_end_chain,
|
|
insecure_get_pkey
|
|
};
|
|
|
|
memset(ctx, 0, sizeof * ctx);
|
|
ctx->vtable = &br_x509_insecure_vtable;
|
|
ctx->done_cert = false;
|
|
ctx->match_fingerprint = _use_fingerprint ? _fingerprint : nullptr;
|
|
ctx->allow_self_signed = _allow_self_signed ? 1 : 0;
|
|
}
|
|
|
|
// Some constants uses to init the server/client contexts
|
|
// Note that suites_P needs to be copied to RAM before use w/BearSSL!
|
|
// List copied verbatim from BearSSL/ssl_client_full.c
|
|
/*
|
|
* The "full" profile supports all implemented cipher suites.
|
|
*
|
|
* Rationale for suite order, from most important to least
|
|
* important rule:
|
|
*
|
|
* -- Don't use 3DES if AES or ChaCha20 is available.
|
|
* -- Try to have Forward Secrecy (ECDHE suite) if possible.
|
|
* -- When not using Forward Secrecy, ECDH key exchange is
|
|
* better than RSA key exchange (slightly more expensive on the
|
|
* client, but much cheaper on the server, and it implies smaller
|
|
* messages).
|
|
* -- ChaCha20+Poly1305 is better than AES/GCM (faster, smaller code).
|
|
* -- GCM is better than CCM and CBC. CCM is better than CBC.
|
|
* -- CCM is preferable over CCM_8 (with CCM_8, forgeries may succeed
|
|
* with probability 2^(-64)).
|
|
* -- AES-128 is preferred over AES-256 (AES-128 is already
|
|
* strong enough, and AES-256 is 40% more expensive).
|
|
*/
|
|
static const uint16_t suites_P[] PROGMEM = {
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
|
|
BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
|
|
BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256,
|
|
BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384,
|
|
BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384,
|
|
BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
|
|
BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
|
|
BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
|
|
BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
|
|
BR_TLS_RSA_WITH_AES_128_GCM_SHA256,
|
|
BR_TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
BR_TLS_RSA_WITH_AES_128_CCM,
|
|
BR_TLS_RSA_WITH_AES_256_CCM,
|
|
BR_TLS_RSA_WITH_AES_128_CCM_8,
|
|
BR_TLS_RSA_WITH_AES_256_CCM_8,
|
|
#endif
|
|
BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
|
BR_TLS_RSA_WITH_AES_128_CBC_SHA,
|
|
BR_TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
|
BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
|
|
BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
|
|
BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA
|
|
#endif
|
|
};
|
|
|
|
// For apps which want to use less secure but faster ciphers, only
|
|
static const uint16_t faster_suites_P[] PROGMEM = {
|
|
BR_TLS_RSA_WITH_AES_256_CBC_SHA256,
|
|
BR_TLS_RSA_WITH_AES_128_CBC_SHA256,
|
|
BR_TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
BR_TLS_RSA_WITH_AES_128_CBC_SHA };
|
|
|
|
// Install hashes into the SSL engine
|
|
static void br_ssl_client_install_hashes(br_ssl_engine_context *eng) {
|
|
br_ssl_engine_set_hash(eng, br_md5_ID, &br_md5_vtable);
|
|
br_ssl_engine_set_hash(eng, br_sha1_ID, &br_sha1_vtable);
|
|
br_ssl_engine_set_hash(eng, br_sha224_ID, &br_sha224_vtable);
|
|
br_ssl_engine_set_hash(eng, br_sha256_ID, &br_sha256_vtable);
|
|
br_ssl_engine_set_hash(eng, br_sha384_ID, &br_sha384_vtable);
|
|
br_ssl_engine_set_hash(eng, br_sha512_ID, &br_sha512_vtable);
|
|
}
|
|
|
|
static void br_x509_minimal_install_hashes(br_x509_minimal_context *x509) {
|
|
br_x509_minimal_set_hash(x509, br_md5_ID, &br_md5_vtable);
|
|
br_x509_minimal_set_hash(x509, br_sha1_ID, &br_sha1_vtable);
|
|
br_x509_minimal_set_hash(x509, br_sha224_ID, &br_sha224_vtable);
|
|
br_x509_minimal_set_hash(x509, br_sha256_ID, &br_sha256_vtable);
|
|
br_x509_minimal_set_hash(x509, br_sha384_ID, &br_sha384_vtable);
|
|
br_x509_minimal_set_hash(x509, br_sha512_ID, &br_sha512_vtable);
|
|
}
|
|
|
|
// Default initializion for our SSL clients
|
|
static void br_ssl_client_base_init(br_ssl_client_context *cc, const uint16_t *cipher_list, int cipher_cnt) {
|
|
uint16_t suites[cipher_cnt];
|
|
memcpy_P(suites, cipher_list, cipher_cnt * sizeof(cipher_list[0]));
|
|
br_ssl_client_zero(cc);
|
|
br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12);
|
|
br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0]));
|
|
br_ssl_client_set_default_rsapub(cc);
|
|
br_ssl_engine_set_default_rsavrfy(&cc->eng);
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_ssl_engine_set_default_ecdsa(&cc->eng);
|
|
#endif
|
|
br_ssl_client_install_hashes(&cc->eng);
|
|
br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf);
|
|
br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
|
|
br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf);
|
|
br_ssl_engine_set_default_aes_cbc(&cc->eng);
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_ssl_engine_set_default_aes_gcm(&cc->eng);
|
|
br_ssl_engine_set_default_aes_ccm(&cc->eng);
|
|
br_ssl_engine_set_default_des_cbc(&cc->eng);
|
|
br_ssl_engine_set_default_chapol(&cc->eng);
|
|
#endif
|
|
}
|
|
|
|
}
|
|
|
|
// Set custom list of ciphers
|
|
bool WiFiClientSecure::setCiphers(const uint16_t *cipherAry, int cipherCount) {
|
|
_cipher_list = nullptr;
|
|
_cipher_list = std::shared_ptr<uint16_t>(new uint16_t[cipherCount], std::default_delete<uint16_t[]>());
|
|
if (!_cipher_list.get()) {
|
|
DEBUG_BSSL("setCiphers: list empty\n");
|
|
return false;
|
|
}
|
|
memcpy_P(_cipher_list.get(), cipherAry, cipherCount * sizeof(uint16_t));
|
|
_cipher_cnt = cipherCount;
|
|
return true;
|
|
}
|
|
|
|
bool WiFiClientSecure::setCiphersLessSecure() {
|
|
return setCiphers(faster_suites_P, sizeof(faster_suites_P)/sizeof(faster_suites_P[0]));
|
|
}
|
|
|
|
bool WiFiClientSecure::setCiphers(std::vector<uint16_t> list) {
|
|
return setCiphers(&list[0], list.size());
|
|
}
|
|
|
|
// Installs the appropriate X509 cert validation method for a client connection
|
|
bool WiFiClientSecure::_installClientX509Validator() {
|
|
if (_use_insecure || _use_fingerprint || _use_self_signed) {
|
|
// Use common insecure x509 authenticator
|
|
_x509_insecure = std::make_shared<struct br_x509_insecure_context>();
|
|
if (!_x509_insecure) {
|
|
DEBUG_BSSL("_installClientX509Validator: OOM for _x509_insecure\n");
|
|
return false;
|
|
}
|
|
br_x509_insecure_init(_x509_insecure.get(), _use_fingerprint, _fingerprint, _use_self_signed);
|
|
br_ssl_engine_set_x509(_eng, &_x509_insecure->vtable);
|
|
} else if (_knownkey) {
|
|
// Simple, pre-known public key authenticator, ignores cert completely.
|
|
_x509_knownkey = std::make_shared<br_x509_knownkey_context>();
|
|
if (!_x509_knownkey) {
|
|
DEBUG_BSSL("_installClientX509Validator: OOM for _x509_knownkey\n");
|
|
return false;
|
|
}
|
|
if (_knownkey->isRSA()) {
|
|
br_x509_knownkey_init_rsa(_x509_knownkey.get(), _knownkey->getRSA(), _knownkey_usages);
|
|
} else if (_knownkey->isEC()) {
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_x509_knownkey_init_ec(_x509_knownkey.get(), _knownkey->getEC(), _knownkey_usages);
|
|
#else
|
|
(void) _knownkey;
|
|
(void) _knownkey_usages;
|
|
DEBUG_BSSL("_installClientX509Validator: Attempting to use EC keys in minimal cipher mode (no EC)\n");
|
|
return false;
|
|
#endif
|
|
}
|
|
br_ssl_engine_set_x509(_eng, &_x509_knownkey->vtable);
|
|
} else {
|
|
// X509 minimal validator. Checks dates, cert chain for trusted CA, etc.
|
|
_x509_minimal = std::make_shared<br_x509_minimal_context>();
|
|
if (!_x509_minimal) {
|
|
DEBUG_BSSL("_installClientX509Validator: OOM for _x509_minimal\n");
|
|
return false;
|
|
}
|
|
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta ? _ta->getTrustAnchors() : nullptr, _ta ? _ta->getCount() : 0);
|
|
br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng));
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng));
|
|
#endif
|
|
br_x509_minimal_install_hashes(_x509_minimal.get());
|
|
if (_now) {
|
|
// Magic constants convert to x509 times
|
|
br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400);
|
|
}
|
|
if (_certStore) {
|
|
_certStore->installCertStore(_x509_minimal.get());
|
|
}
|
|
br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Called by connect() to do the actual SSL setup and handshake.
|
|
// Returns if the SSL handshake succeeded.
|
|
bool WiFiClientSecure::_connectSSL(const char* hostName) {
|
|
DEBUG_BSSL("_connectSSL: start connection\n");
|
|
_freeSSL();
|
|
_oom_err = false;
|
|
|
|
#ifdef DEBUG_ESP_SSL
|
|
// BearSSL will reject all connections unless an authentication option is set, warn in DEBUG builds
|
|
if (!_use_insecure && !_use_fingerprint && !_use_self_signed && !_knownkey && !_certStore && !_ta) {
|
|
DEBUG_BSSL("Connection *will* fail, no authentication method is setup\n");
|
|
}
|
|
#endif
|
|
|
|
_sc = std::make_shared<br_ssl_client_context>();
|
|
_eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
|
|
_iobuf_in = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
|
|
_iobuf_out = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
|
|
|
|
if (!_sc || !_iobuf_in || !_iobuf_out) {
|
|
_freeSSL(); // Frees _sc, _iobuf*
|
|
_oom_err = true;
|
|
DEBUG_BSSL("_connectSSL: OOM error\n");
|
|
return false;
|
|
}
|
|
|
|
// If no cipher list yet set, use defaults
|
|
if (_cipher_list.get() == nullptr) {
|
|
br_ssl_client_base_init(_sc.get(), suites_P, sizeof(suites_P) / sizeof(suites_P[0]));
|
|
} else {
|
|
br_ssl_client_base_init(_sc.get(), _cipher_list.get(), _cipher_cnt);
|
|
}
|
|
// Only failure possible in the installation is OOM
|
|
if (!_installClientX509Validator()) {
|
|
_freeSSL();
|
|
_oom_err = true;
|
|
DEBUG_BSSL("_connectSSL: Can't install x509 validator\n");
|
|
return false;
|
|
}
|
|
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
|
|
|
|
// Apply any client certificates, if supplied.
|
|
if (_sk && _sk->isRSA()) {
|
|
br_ssl_client_set_single_rsa(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0,
|
|
_sk->getRSA(), br_rsa_pkcs1_sign_get_default());
|
|
} else if (_sk && _sk->isEC()) {
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_ssl_client_set_single_ec(_sc.get(), _chain ? _chain->getX509Certs() : nullptr, _chain ? _chain->getCount() : 0,
|
|
_sk->getEC(), _allowed_usages,
|
|
_cert_issuer_key_type, br_ec_get_default(), br_ecdsa_sign_asn1_get_default());
|
|
#else
|
|
_freeSSL();
|
|
DEBUG_BSSL("_connectSSL: Attempting to use EC cert in minimal cipher mode (no EC)\n");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// Restore session from the storage spot, if present
|
|
if (_session) {
|
|
br_ssl_engine_set_session_parameters(_eng, _session->getSession());
|
|
}
|
|
|
|
if (!br_ssl_client_reset(_sc.get(), hostName, _session?1:0)) {
|
|
_freeSSL();
|
|
DEBUG_BSSL("_connectSSL: Can't reset client\n");
|
|
return false;
|
|
}
|
|
|
|
auto ret = _wait_for_handshake();
|
|
#ifdef DEBUG_ESP_SSL
|
|
if (!ret) {
|
|
char err[256];
|
|
getLastSSLError(err, sizeof(err));
|
|
DEBUG_BSSL("Couldn't connect. Error = '%s'\n", err);
|
|
} else {
|
|
DEBUG_BSSL("Connected!\n");
|
|
}
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
// 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) {
|
|
if (client_CA_ta) {
|
|
_ta = client_CA_ta;
|
|
// X509 minimal validator. Checks dates, cert chain for trusted CA, etc.
|
|
_x509_minimal = std::make_shared<br_x509_minimal_context>();
|
|
if (!_x509_minimal) {
|
|
_freeSSL();
|
|
_oom_err = true;
|
|
DEBUG_BSSL("_installServerX509Validator: OOM for _x509_minimal\n");
|
|
return false;
|
|
}
|
|
br_x509_minimal_init(_x509_minimal.get(), &br_sha256_vtable, _ta->getTrustAnchors(), _ta->getCount());
|
|
br_ssl_engine_set_default_rsavrfy(_eng);
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_ssl_engine_set_default_ecdsa(_eng);
|
|
#endif
|
|
br_x509_minimal_set_rsa(_x509_minimal.get(), br_ssl_engine_get_rsavrfy(_eng));
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
br_x509_minimal_set_ecdsa(_x509_minimal.get(), br_ssl_engine_get_ec(_eng), br_ssl_engine_get_ecdsa(_eng));
|
|
#endif
|
|
br_x509_minimal_install_hashes(_x509_minimal.get());
|
|
if (_now) {
|
|
// Magic constants convert to x509 times
|
|
br_x509_minimal_set_time(_x509_minimal.get(), ((uint32_t)_now) / 86400 + 719528, ((uint32_t)_now) % 86400);
|
|
}
|
|
br_ssl_engine_set_x509(_eng, &_x509_minimal->vtable);
|
|
br_ssl_server_set_trust_anchor_names_alt(_sc_svr.get(), _ta->getTrustAnchors(), _ta->getCount());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Called by WiFiServerBearSSL when an RSA cert/key is specified.
|
|
bool WiFiClientSecure::_connectSSLServerRSA(const X509List *chain,
|
|
const PrivateKey *sk,
|
|
const X509List *client_CA_ta) {
|
|
_freeSSL();
|
|
_oom_err = false;
|
|
_sc_svr = std::make_shared<br_ssl_server_context>();
|
|
_eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
|
|
_iobuf_in = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
|
|
_iobuf_out = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
|
|
|
|
if (!_sc_svr || !_iobuf_in || !_iobuf_out) {
|
|
_freeSSL();
|
|
_oom_err = true;
|
|
DEBUG_BSSL("_connectSSLServerRSA: OOM error\n");
|
|
return false;
|
|
}
|
|
|
|
br_ssl_server_init_full_rsa(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0, sk ? sk->getRSA() : nullptr);
|
|
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
|
|
if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) {
|
|
DEBUG_BSSL("_connectSSLServerRSA: Can't install serverX509check\n");
|
|
return false;
|
|
}
|
|
if (!br_ssl_server_reset(_sc_svr.get())) {
|
|
_freeSSL();
|
|
DEBUG_BSSL("_connectSSLServerRSA: Can't reset server ctx\n");
|
|
return false;
|
|
}
|
|
|
|
return _wait_for_handshake();
|
|
}
|
|
|
|
// Called by WiFiServerBearSSL when an elliptic curve cert/key is specified.
|
|
bool WiFiClientSecure::_connectSSLServerEC(const X509List *chain,
|
|
unsigned cert_issuer_key_type, const PrivateKey *sk,
|
|
const X509List *client_CA_ta) {
|
|
#ifndef BEARSSL_SSL_BASIC
|
|
_freeSSL();
|
|
_oom_err = false;
|
|
_sc_svr = std::make_shared<br_ssl_server_context>();
|
|
_eng = &_sc_svr->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
|
|
_iobuf_in = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
|
|
_iobuf_out = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
|
|
|
|
if (!_sc_svr || !_iobuf_in || !_iobuf_out) {
|
|
_freeSSL();
|
|
_oom_err = true;
|
|
DEBUG_BSSL("_connectSSLServerEC: OOM error\n");
|
|
return false;
|
|
}
|
|
|
|
br_ssl_server_init_full_ec(_sc_svr.get(), chain ? chain->getX509Certs() : nullptr, chain ? chain->getCount() : 0,
|
|
cert_issuer_key_type, sk ? sk->getEC() : nullptr);
|
|
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
|
|
if (client_CA_ta && !_installServerX509Validator(client_CA_ta)) {
|
|
DEBUG_BSSL("_connectSSLServerEC: Can't install serverX509check\n");
|
|
return false;
|
|
}
|
|
if (!br_ssl_server_reset(_sc_svr.get())) {
|
|
_freeSSL();
|
|
DEBUG_BSSL("_connectSSLServerEC: Can't reset server ctx\n");
|
|
return false;
|
|
}
|
|
|
|
return _wait_for_handshake();
|
|
#else
|
|
(void) chain;
|
|
(void) cert_issuer_key_type;
|
|
(void) sk;
|
|
(void) client_CA_ta;
|
|
DEBUG_BSSL("_connectSSLServerEC: Attempting to use EC cert in minimal cipher mode (no EC)\n");
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
// 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 err = 0;
|
|
const char *t = PSTR("OK");
|
|
if (_sc || _sc_svr) {
|
|
err = br_ssl_engine_last_error(_eng);
|
|
}
|
|
if (_oom_err) {
|
|
err = -1000;
|
|
}
|
|
switch (err) {
|
|
case -1000: t = PSTR("Unable to allocate memory for SSL structures and buffers."); break;
|
|
case BR_ERR_BAD_PARAM: t = PSTR("Caller-provided parameter is incorrect."); break;
|
|
case BR_ERR_BAD_STATE: t = PSTR("Operation requested by the caller cannot be applied with the current context state (e.g. reading data while outgoing data is waiting to be sent)."); break;
|
|
case BR_ERR_UNSUPPORTED_VERSION: t = PSTR("Incoming protocol or record version is unsupported."); break;
|
|
case BR_ERR_BAD_VERSION: t = PSTR("Incoming record version does not match the expected version."); break;
|
|
case BR_ERR_BAD_LENGTH: t = PSTR("Incoming record length is invalid."); break;
|
|
case BR_ERR_TOO_LARGE: t = PSTR("Incoming record is too large to be processed, or buffer is too small for the handshake message to send."); break;
|
|
case BR_ERR_BAD_MAC: t = PSTR("Decryption found an invalid padding, or the record MAC is not correct."); break;
|
|
case BR_ERR_NO_RANDOM: t = PSTR("No initial entropy was provided, and none can be obtained from the OS."); break;
|
|
case BR_ERR_UNKNOWN_TYPE: t = PSTR("Incoming record type is unknown."); break;
|
|
case BR_ERR_UNEXPECTED: t = PSTR("Incoming record or message has wrong type with regards to the current engine state."); break;
|
|
case BR_ERR_BAD_CCS: t = PSTR("ChangeCipherSpec message from the peer has invalid contents."); break;
|
|
case BR_ERR_BAD_ALERT: t = PSTR("Alert message from the peer has invalid contents (odd length)."); break;
|
|
case BR_ERR_BAD_HANDSHAKE: t = PSTR("Incoming handshake message decoding failed."); break;
|
|
case BR_ERR_OVERSIZED_ID: t = PSTR("ServerHello contains a session ID which is larger than 32 bytes."); break;
|
|
case BR_ERR_BAD_CIPHER_SUITE: t = PSTR("Server wants to use a cipher suite that we did not claim to support. This is also reported if we tried to advertise a cipher suite that we do not support."); break;
|
|
case BR_ERR_BAD_COMPRESSION: t = PSTR("Server wants to use a compression that we did not claim to support."); break;
|
|
case BR_ERR_BAD_FRAGLEN: t = PSTR("Server's max fragment length does not match client's."); break;
|
|
case BR_ERR_BAD_SECRENEG: t = PSTR("Secure renegotiation failed."); break;
|
|
case BR_ERR_EXTRA_EXTENSION: t = PSTR("Server sent an extension type that we did not announce, or used the same extension type several times in a single ServerHello."); break;
|
|
case BR_ERR_BAD_SNI: t = PSTR("Invalid Server Name Indication contents (when used by the server, this extension shall be empty)."); break;
|
|
case BR_ERR_BAD_HELLO_DONE: t = PSTR("Invalid ServerHelloDone from the server (length is not 0)."); break;
|
|
case BR_ERR_LIMIT_EXCEEDED: t = PSTR("Internal limit exceeded (e.g. server's public key is too large)."); break;
|
|
case BR_ERR_BAD_FINISHED: t = PSTR("Finished message from peer does not match the expected value."); break;
|
|
case BR_ERR_RESUME_MISMATCH: t = PSTR("Session resumption attempt with distinct version or cipher suite."); break;
|
|
case BR_ERR_INVALID_ALGORITHM: t = PSTR("Unsupported or invalid algorithm (ECDHE curve, signature algorithm, hash function)."); break;
|
|
case BR_ERR_BAD_SIGNATURE: t = PSTR("Invalid signature in ServerKeyExchange or CertificateVerify message."); break;
|
|
case BR_ERR_WRONG_KEY_USAGE: t = PSTR("Peer's public key does not have the proper type or is not allowed for the requested operation."); break;
|
|
case BR_ERR_NO_CLIENT_AUTH: t = PSTR("Client did not send a certificate upon request, or the client certificate could not be validated."); break;
|
|
case BR_ERR_IO: t = PSTR("I/O error or premature close on transport stream."); break;
|
|
case BR_ERR_X509_INVALID_VALUE: t = PSTR("Invalid value in an ASN.1 structure."); break;
|
|
case BR_ERR_X509_TRUNCATED: t = PSTR("Truncated certificate or other ASN.1 object."); break;
|
|
case BR_ERR_X509_EMPTY_CHAIN: t = PSTR("Empty certificate chain (no certificate at all)."); break;
|
|
case BR_ERR_X509_INNER_TRUNC: t = PSTR("Decoding error: inner element extends beyond outer element size."); break;
|
|
case BR_ERR_X509_BAD_TAG_CLASS: t = PSTR("Decoding error: unsupported tag class (application or private)."); break;
|
|
case BR_ERR_X509_BAD_TAG_VALUE: t = PSTR("Decoding error: unsupported tag value."); break;
|
|
case BR_ERR_X509_INDEFINITE_LENGTH: t = PSTR("Decoding error: indefinite length."); break;
|
|
case BR_ERR_X509_EXTRA_ELEMENT: t = PSTR("Decoding error: extraneous element."); break;
|
|
case BR_ERR_X509_UNEXPECTED: t = PSTR("Decoding error: unexpected element."); break;
|
|
case BR_ERR_X509_NOT_CONSTRUCTED: t = PSTR("Decoding error: expected constructed element, but is primitive."); break;
|
|
case BR_ERR_X509_NOT_PRIMITIVE: t = PSTR("Decoding error: expected primitive element, but is constructed."); break;
|
|
case BR_ERR_X509_PARTIAL_BYTE: t = PSTR("Decoding error: BIT STRING length is not multiple of 8."); break;
|
|
case BR_ERR_X509_BAD_BOOLEAN: t = PSTR("Decoding error: BOOLEAN value has invalid length."); break;
|
|
case BR_ERR_X509_OVERFLOW: t = PSTR("Decoding error: value is off-limits."); break;
|
|
case BR_ERR_X509_BAD_DN: t = PSTR("Invalid distinguished name."); break;
|
|
case BR_ERR_X509_BAD_TIME: t = PSTR("Invalid date/time representation."); break;
|
|
case BR_ERR_X509_UNSUPPORTED: t = PSTR("Certificate contains unsupported features that cannot be ignored."); break;
|
|
case BR_ERR_X509_LIMIT_EXCEEDED: t = PSTR("Key or signature size exceeds internal limits."); break;
|
|
case BR_ERR_X509_WRONG_KEY_TYPE: t = PSTR("Key type does not match that which was expected."); break;
|
|
case BR_ERR_X509_BAD_SIGNATURE: t = PSTR("Signature is invalid."); break;
|
|
case BR_ERR_X509_TIME_UNKNOWN: t = PSTR("Validation time is unknown."); break;
|
|
case BR_ERR_X509_EXPIRED: t = PSTR("Certificate is expired or not yet valid."); break;
|
|
case BR_ERR_X509_DN_MISMATCH: t = PSTR("Issuer/Subject DN mismatch in the chain."); break;
|
|
case BR_ERR_X509_BAD_SERVER_NAME: t = PSTR("Expected server name was not found in the chain."); break;
|
|
case BR_ERR_X509_CRITICAL_EXTENSION: t = PSTR("Unknown critical extension in certificate."); break;
|
|
case BR_ERR_X509_NOT_CA: t = PSTR("Not a CA, or path length constraint violation."); break;
|
|
case BR_ERR_X509_FORBIDDEN_KEY_USAGE: t = PSTR("Key Usage extension prohibits intended usage."); break;
|
|
case BR_ERR_X509_WEAK_PUBLIC_KEY: t = PSTR("Public key found in certificate is too small."); break;
|
|
case BR_ERR_X509_NOT_TRUSTED: t = PSTR("Chain could not be linked to a trust anchor."); break;
|
|
default: t = PSTR("Unknown error code."); break;
|
|
}
|
|
if (dest) {
|
|
strncpy_P(dest, t, len);
|
|
dest[len - 1] = 0;
|
|
}
|
|
return err;
|
|
}
|
|
|
|
bool WiFiClientSecure::probeMaxFragmentLength(const char* name, uint16_t port, uint16_t len) {
|
|
IPAddress remote_addr;
|
|
if (!WiFi.hostByName(name, remote_addr)) {
|
|
DEBUG_BSSL("probeMaxFragmentLength: Can't resolve host\n");
|
|
return false;
|
|
}
|
|
return WiFiClientSecure::probeMaxFragmentLength(remote_addr, port, len);
|
|
}
|
|
|
|
bool WiFiClientSecure::probeMaxFragmentLength(const String& host, uint16_t port, uint16_t len) {
|
|
return WiFiClientSecure::probeMaxFragmentLength(host.c_str(), port, len);
|
|
}
|
|
|
|
|
|
// Helper function which aborts a TLS handshake by sending TLS
|
|
// ClientAbort and ClientClose messages.
|
|
static bool _SendAbort(WiFiClient& probe, bool supportsLen) {
|
|
// If we're still connected, send the appropriate notice that
|
|
// we're aborting the handshake per RFCs.
|
|
static const uint8_t clientAbort_P[] PROGMEM = {
|
|
0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02,
|
|
1, 90 /* warning: user_cancelled */
|
|
};
|
|
static const uint8_t clientClose_P[] PROGMEM = {
|
|
0x15 /*alert*/, 0x03, 0x03 /*TLS 1.2*/, 0x00, 0x02,
|
|
1, 0 /* warning: close_notify */
|
|
};
|
|
if (probe.connected()) {
|
|
uint8_t msg[sizeof(clientAbort_P)];
|
|
memcpy_P(msg, clientAbort_P, sizeof(clientAbort_P));
|
|
probe.write(msg, sizeof(clientAbort_P));
|
|
memcpy_P(msg, clientClose_P, sizeof(clientClose_P));
|
|
probe.write(msg, sizeof(clientClose_P));
|
|
}
|
|
return supportsLen;
|
|
}
|
|
|
|
// Checks for support of Maximum Frame Length Negotiation at the given
|
|
// blocksize. Note that, per spec, only 512, 1024, 2048, and 4096 are
|
|
// supported. Many servers today do not support this negotiation.
|
|
|
|
// TODO - Allow for fragmentation...but not very critical as the ServerHello
|
|
// we use comes to < 80 bytes which has no reason to ever be fragmented.
|
|
// TODO - Check the type of returned extensions and that the MFL is the exact
|
|
// same one we sent. Not critical as only horribly broken servers would
|
|
// return changed or add their own extensions.
|
|
bool WiFiClientSecure::probeMaxFragmentLength(IPAddress ip, uint16_t port, uint16_t len) {
|
|
// Hardcoded TLS 1.2 packets used throughout
|
|
static const uint8_t clientHelloHead_P[] PROGMEM = {
|
|
0x16, 0x03, 0x03, 0x00, 0, // TLS header, change last 2 bytes to len
|
|
0x01, 0x00, 0x00, 0, // Last 3 bytes == length
|
|
0x03, 0x03, // Proto version TLS 1.2
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Random (gmtime + rand[28])
|
|
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
|
|
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
|
|
0x00, // Session ID
|
|
};
|
|
// Followed by our cipher-suite, generated on-the-fly
|
|
// 0x00, 0x02, // cipher suite len
|
|
// 0xc0, 0x13, // BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA
|
|
static const uint8_t clientHelloTail_P[] PROGMEM = {
|
|
0x01, 0x00, // No compression
|
|
0x00, 26 + 14 + 6 + 5, // Extension length
|
|
0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x04, 0x03, 0x03, 0x03, 0x05, 0x03,
|
|
0x06, 0x03, 0x02, 0x03, 0x04, 0x01, 0x03, 0x01, 0x05, 0x01, 0x06,
|
|
0x01, 0x02, 0x01, // Supported signature algorithms
|
|
0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19,
|
|
0x00, 0x1d, // Supported groups
|
|
0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, // Supported EC formats
|
|
0x00, 0x01, // Max Frag Len
|
|
0x00, 0x01, // len of MaxFragLen
|
|
};
|
|
// Followed by a 1-byte MFLN size requesst
|
|
// 0x04 // 2^12 = 4K
|
|
uint8_t mfl;
|
|
|
|
switch (len) {
|
|
case 512: mfl = 1; break;
|
|
case 1024: mfl = 2; break;
|
|
case 2048: mfl = 3; break;
|
|
case 4096: mfl = 4; break;
|
|
default: return false; // Invalid size
|
|
}
|
|
int ttlLen = sizeof(clientHelloHead_P) + (2 + sizeof(suites_P)) + (sizeof(clientHelloTail_P) + 1);
|
|
uint8_t *clientHello = new uint8_t[ttlLen];
|
|
if (!clientHello) {
|
|
DEBUG_BSSL("probeMaxFragmentLength: OOM\n");
|
|
return false;
|
|
}
|
|
memcpy_P(clientHello, clientHelloHead_P, sizeof(clientHelloHead_P));
|
|
clientHello[sizeof(clientHelloHead_P) + 0] = sizeof(suites_P) >> 8; // MSB byte len
|
|
clientHello[sizeof(clientHelloHead_P) + 1] = sizeof(suites_P) & 0xff; // LSB byte len
|
|
for (size_t i = 0; i < sizeof(suites_P) / sizeof(suites_P[0]); i++) {
|
|
uint16_t flip = pgm_read_word(&suites_P[i]);
|
|
// Swap to network byte order
|
|
flip = ((flip >> 8) & 0xff) | ((flip & 0xff) << 8);
|
|
memcpy(clientHello + sizeof(clientHelloHead_P) + 2 + 2 * i, &flip, 2);
|
|
}
|
|
memcpy_P(clientHello + sizeof(clientHelloHead_P) + 2 + sizeof(suites_P), clientHelloTail_P, sizeof(clientHelloTail_P));
|
|
clientHello[sizeof(clientHelloHead_P) + 2 + sizeof(suites_P) + sizeof(clientHelloTail_P)] = mfl;
|
|
|
|
// Fix up TLS fragment length
|
|
clientHello[3] = (ttlLen - 5) >> 8;
|
|
clientHello[4] = (ttlLen - 5) & 0xff;
|
|
// Fix up ClientHello message length
|
|
clientHello[7] = (ttlLen - 5 - 4) >> 8;
|
|
clientHello[8] = (ttlLen - 5 - 4) & 0xff;
|
|
|
|
WiFiClient probe;
|
|
probe.connect(ip, port);
|
|
if (!probe.connected()) {
|
|
delete[] clientHello;
|
|
DEBUG_BSSL("probeMaxFragmentLength: Can't connect\n");
|
|
return false;
|
|
}
|
|
|
|
int ret = probe.write(clientHello, ttlLen);
|
|
delete[] clientHello; // We're done w/the hello message
|
|
if (!probe.connected() || (ret != ttlLen)) {
|
|
DEBUG_BSSL("probeMaxFragmentLength: Protocol error\n");
|
|
return false;
|
|
}
|
|
|
|
bool supportsLen = false;
|
|
uint8_t fragResp[5];
|
|
int fragLen;
|
|
uint8_t hand[4];
|
|
int handLen;
|
|
uint8_t protoVer[2];
|
|
uint8_t rand[32];
|
|
uint8_t sessionLen;
|
|
uint8_t cipher[2];
|
|
uint8_t comp;
|
|
uint8_t extBytes[2];
|
|
uint16_t extLen;
|
|
|
|
ret = probe.readBytes(fragResp, 5);
|
|
if (!probe.connected() || (ret != 5) || (fragResp[0] != 0x16) || (fragResp[1] != 0x03) || (fragResp[2] != 0x03)) {
|
|
// Short read, not a HANDSHAKE or not TLS 1.2, so it's not supported
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
fragLen = (fragResp[3] << 8) | fragResp[4];
|
|
if (fragLen < 4 + 2 + 32 + 1 + 2 + 1) {
|
|
// Too short to have an extension
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(hand, 4);
|
|
fragLen -= ret;
|
|
if ((ret != 4) || (hand[0] != 2)) {
|
|
// Short read or not server_hello
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
handLen = (hand[1] << 16) | (hand[2] << 8) | hand[3];
|
|
if (handLen != fragLen) {
|
|
// Got some weird mismatch, this is invalid
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(protoVer, 2);
|
|
handLen -= ret;
|
|
if ((ret != 2) || (protoVer[0] != 0x03) || (protoVer[1] != 0x03)) {
|
|
// Short read or not tls 1.2, so can't do MFLN
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(rand, 32);
|
|
handLen -= ret;
|
|
if (ret != 32) {
|
|
// short read of random data
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(&sessionLen, 1);
|
|
handLen -= ret;
|
|
if ((ret != 1) || (sessionLen > 32)) {
|
|
// short read of session len or invalid size
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
if (sessionLen) {
|
|
ret = probe.readBytes(rand, sessionLen);
|
|
handLen -= ret;
|
|
if (ret != sessionLen) {
|
|
// short session id read
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
}
|
|
|
|
ret = probe.readBytes(cipher, 2);
|
|
handLen -= ret;
|
|
if (ret != 2) {
|
|
// Short read...we don't check the cipher here
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(&comp, 1);
|
|
handLen -= ret;
|
|
if ((ret != 1) || comp != 0) {
|
|
// short read or invalid compression
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
ret = probe.readBytes(extBytes, 2);
|
|
handLen -= ret;
|
|
extLen = extBytes[1] || (extBytes[0]<<8);
|
|
if ((extLen == 0) || (ret != 2)) {
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
while (handLen > 0) {
|
|
// Parse each extension and look for MFLN
|
|
uint8_t typeBytes[2];
|
|
ret = probe.readBytes(typeBytes, 2);
|
|
handLen -= 2;
|
|
if ((ret != 2) || (handLen <= 0) ) {
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
uint8_t lenBytes[2];
|
|
ret = probe.readBytes(lenBytes, 2);
|
|
handLen -= 2;
|
|
uint16_t extLen = lenBytes[1] | (lenBytes[0]<<8);
|
|
if ((ret != 2) || (handLen <= 0) || (extLen > 32) || (extLen > handLen) ) {
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
if ((typeBytes[0]==0x00) && (typeBytes[1]==0x01)) { // MFLN extension!
|
|
// If present and 1-byte in length, it's supported
|
|
return _SendAbort(probe, extLen==1 ? true : false);
|
|
}
|
|
// Skip the extension, move to next one
|
|
uint8_t junk[32];
|
|
ret = probe.readBytes(junk, extLen);
|
|
handLen -= extLen;
|
|
if (ret != extLen) {
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
}
|
|
return _SendAbort(probe, supportsLen);
|
|
}
|
|
|
|
|
|
// AXTLS compatibility interfaces
|
|
bool WiFiClientSecure::setCACert(const uint8_t* pk, size_t size) {
|
|
_axtls_ta = nullptr;
|
|
_axtls_ta = std::shared_ptr<X509List>(new X509List(pk, size));
|
|
_ta = _axtls_ta.get();
|
|
return _ta ? true : false;
|
|
}
|
|
|
|
bool WiFiClientSecure::setCertificate(const uint8_t* pk, size_t size) {
|
|
_axtls_chain = nullptr;
|
|
_axtls_chain = std::shared_ptr<X509List>(new X509List(pk, size));
|
|
_chain = _axtls_chain.get();
|
|
return _chain ? true : false;
|
|
}
|
|
|
|
bool WiFiClientSecure::setPrivateKey(const uint8_t* pk, size_t size) {
|
|
_axtls_sk = nullptr;
|
|
_axtls_sk = std::shared_ptr<PrivateKey>(new PrivateKey(pk, size));
|
|
_sk = _axtls_sk.get();
|
|
return _sk ? true : false;
|
|
|
|
}
|
|
|
|
uint8_t *WiFiClientSecure::_streamLoad(Stream& stream, size_t size) {
|
|
uint8_t *dest = (uint8_t*)malloc(size);
|
|
if (!dest) {
|
|
return nullptr;
|
|
}
|
|
if (size != stream.readBytes(dest, size)) {
|
|
free(dest);
|
|
return nullptr;
|
|
}
|
|
return dest;
|
|
}
|
|
|
|
bool WiFiClientSecure::loadCACert(Stream& stream, size_t size) {
|
|
uint8_t *dest = _streamLoad(stream, size);
|
|
bool ret = false;
|
|
if (dest) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
ret = setCACert(dest, size);
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
free(dest);
|
|
return ret;
|
|
}
|
|
|
|
bool WiFiClientSecure::loadCertificate(Stream& stream, size_t size) {
|
|
uint8_t *dest = _streamLoad(stream, size);
|
|
bool ret = false;
|
|
if (dest) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
ret = setCertificate(dest, size);
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
free(dest);
|
|
return ret;
|
|
}
|
|
|
|
bool WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) {
|
|
uint8_t *dest = _streamLoad(stream, size);
|
|
bool ret = false;
|
|
if (dest) {
|
|
#pragma GCC diagnostic push
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
ret = setPrivateKey(dest, size);
|
|
#pragma GCC diagnostic pop
|
|
}
|
|
free(dest);
|
|
return ret;
|
|
}
|
|
|
|
};
|