added lots and lots of Serial debugging framework, need to test and make sure it works
This commit is contained in:
parent
2375ec0339
commit
c212e355a4
3 changed files with 246 additions and 101 deletions
|
@ -42,19 +42,6 @@
|
|||
#ifndef SSLClient_H_
|
||||
#define SSLClient_H_
|
||||
|
||||
/** error enums
|
||||
* Static constants defining the possible errors encountered
|
||||
* Read from getWriteError();
|
||||
*/
|
||||
enum Error {
|
||||
SSL_OK = 0,
|
||||
SSL_CLIENT_CONNECT_FAIL,
|
||||
SSL_BR_CONNECT_FAIL,
|
||||
SSL_CLIENT_WRTIE_ERROR,
|
||||
SSL_BR_WRITE_ERROR,
|
||||
SSL_INTERNAL_ERROR
|
||||
};
|
||||
|
||||
/**
|
||||
* \brief This class serves as a templating proxy class for the SSLClientImpl to do the real work.
|
||||
*
|
||||
|
@ -100,7 +87,7 @@ public:
|
|||
* @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG
|
||||
* @param debug whether to enable or disable debug logging, must be constexpr
|
||||
*/
|
||||
explicit SSLClient(const C& client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const bool debug = true)
|
||||
explicit SSLClient(const C& client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug = SSL_ERROR)
|
||||
: SSLClientImpl(NULL, trust_anchors, trust_anchors_num, analog_pin, debug)
|
||||
, m_client(client)
|
||||
, m_sessions{}
|
||||
|
@ -153,8 +140,8 @@ public:
|
|||
// increment m_index so the session cache is a circular buffer
|
||||
if (temp_index == m_index && ++m_index >= SessionCache) m_index = 0;
|
||||
// return the pointed to value
|
||||
m_print("Using index: ");
|
||||
m_print(temp_index);
|
||||
m_info("Using session index: ", __func__);
|
||||
Serial.println(temp_index);
|
||||
return m_sessions[temp_index];
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
/** see SSLClientImpl.h */
|
||||
SSLClientImpl::SSLClientImpl(Client *client, const br_x509_trust_anchor *trust_anchors,
|
||||
const size_t trust_anchors_num, const int analog_pin, const bool debug)
|
||||
const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug)
|
||||
: m_client(client)
|
||||
, m_trust_anchors(trust_anchors)
|
||||
, m_trust_anchors_num(trust_anchors_num)
|
||||
|
@ -43,31 +43,33 @@ SSLClientImpl::SSLClientImpl(Client *client, const br_x509_trust_anchor *trust_a
|
|||
|
||||
/* see SSLClientImpl.h*/
|
||||
int SSLClientImpl::connect(IPAddress ip, uint16_t port) {
|
||||
const char* func_name = __func__;
|
||||
// connection check
|
||||
if (connected()) {
|
||||
m_print("Error: cannot have two connections at the same time! Please create another SSLClient instance.");
|
||||
m_error("Cannot have two connections at the same time! Please create another SSLClient instance.", func_name);
|
||||
return -1;
|
||||
}
|
||||
// reset indexs for saftey
|
||||
m_write_idx = 0;
|
||||
// Warning for security
|
||||
m_print("Warning! Using a raw IP Address for an SSL connection bypasses some important verification steps\nYou should use a domain name (www.google.com) whenever possible.");
|
||||
m_warn("Using a raw IP Address for an SSL connection bypasses some important verification steps. You should use a domain name (www.google.com) whenever possible.", func_name);
|
||||
// first we need our hidden client member to negotiate the socket for us,
|
||||
// since most times socket functionality is implemented in hardeware.
|
||||
if (!m_client->connect(ip, port)) {
|
||||
m_print("Error: Failed to connect using m_client");
|
||||
m_error("Failed to connect using m_client. Are you connected to the internet?", func_name);
|
||||
setWriteError(SSL_CLIENT_CONNECT_FAIL);
|
||||
return 0;
|
||||
}
|
||||
m_print("Base ethernet client connected!");
|
||||
m_info("Base client connected!", func_name);
|
||||
return m_start_ssl(NULL, getSession(NULL, ip));
|
||||
}
|
||||
|
||||
/* see SSLClientImpl.h*/
|
||||
int SSLClientImpl::connect(const char *host, uint16_t port) {
|
||||
const char* func_name = __func__;
|
||||
// connection check
|
||||
if (connected()) {
|
||||
m_print("Error: cannot have two connections at the same time! Please create another SSLClient instance.");
|
||||
m_error("Cannot have two connections at the same time! Please create another SSLClient instance.", func_name);
|
||||
return -1;
|
||||
}
|
||||
// reset indexs for saftey
|
||||
|
@ -78,7 +80,7 @@ int SSLClientImpl::connect(const char *host, uint16_t port) {
|
|||
SSLSession& ses = getSession(host, INADDR_NONE);
|
||||
if (ses.is_valid_session()) {
|
||||
// if so, then connect using the stored session
|
||||
m_print("Connecting using a cached IP");
|
||||
m_info("Connecting using a cached IP", func_name);
|
||||
connect_ok = m_client->connect(ses.get_ip(), port);
|
||||
}
|
||||
// else connect with the provided hostname
|
||||
|
@ -86,22 +88,20 @@ int SSLClientImpl::connect(const char *host, uint16_t port) {
|
|||
// first we need our hidden client member to negotiate the socket for us,
|
||||
// since most times socket functionality is implemented in hardeware.
|
||||
if (!connect_ok) {
|
||||
m_print("Error: Failed to connect using m_client");
|
||||
m_error("Failed to connect using m_client. Are you connected to the internet?", func_name);
|
||||
setWriteError(SSL_CLIENT_CONNECT_FAIL);
|
||||
return 0;
|
||||
}
|
||||
m_print("Base ethernet client connected!");
|
||||
m_info("Base client connected!", func_name);
|
||||
// start ssl!
|
||||
return m_start_ssl(host, ses);
|
||||
}
|
||||
|
||||
/** see SSLClientImpl.h*/
|
||||
size_t SSLClientImpl::write(const uint8_t *buf, size_t size) {
|
||||
const char* func_name = __func__;
|
||||
// check if the socket is still open and such
|
||||
if(br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED || getWriteError()) {
|
||||
m_print("Client is not connected! Perhaps something has happened?");
|
||||
return 0;
|
||||
}
|
||||
if (!m_soft_connected(func_name)) return 0;
|
||||
// add to the bearssl io buffer, simply appending whatever we want to write
|
||||
size_t alen;
|
||||
unsigned char *br_buf = br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen);
|
||||
|
@ -112,21 +112,15 @@ size_t SSLClientImpl::write(const uint8_t *buf, size_t size) {
|
|||
// to the buffer in which we conclude it's already safe to write
|
||||
if(m_write_idx == 0) {
|
||||
if (m_run_until(BR_SSL_SENDAPP) < 0) {
|
||||
m_print("Error: could not run until sendapp");
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
m_error("Failed while waiting for the engine to enter BR_SSL_SENDAPP", func_name);
|
||||
return 0;
|
||||
}
|
||||
// reset the buffer pointer
|
||||
br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen);
|
||||
}
|
||||
// sanity check
|
||||
if(br_buf == NULL || alen == 0) {
|
||||
m_print("Error: recieved null buffer or zero alen in write");
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
return 0;
|
||||
}
|
||||
// if we're about to fill the buffer, we need to send the data and then wait
|
||||
// for another oppurtinity to send
|
||||
// so we only send the smallest of the buffer size or our data size - how much we've already sent
|
||||
const size_t cpamount = m_write_idx + (size - cur_idx) > alen ? alen : size - cur_idx;
|
||||
memcpy(br_buf + m_write_idx, buf + cur_idx, cpamount);
|
||||
// if we filled the buffer, reset m_write_idx
|
||||
|
@ -142,25 +136,19 @@ size_t SSLClientImpl::write(const uint8_t *buf, size_t size) {
|
|||
|
||||
/** see SSLClientImpl.h*/
|
||||
int SSLClientImpl::available() {
|
||||
const char* func_name = __func__;
|
||||
// connection check
|
||||
if (br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED || getWriteError()) {
|
||||
m_print("Warn: Cannot check available of disconnected client");
|
||||
return 0;
|
||||
}
|
||||
if (!m_soft_connected(func_name)) return 0;
|
||||
// run the SSL engine until we are waiting for either user input or a server response
|
||||
unsigned state = m_update_engine();
|
||||
if (state == 0) {
|
||||
m_print("Error: SSL engine failed: ");
|
||||
m_print(br_ssl_engine_last_error(&m_sslctx.eng));
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
}
|
||||
if (state == 0) m_error("SSL engine failed to update.", func_name);
|
||||
else if(state & BR_SSL_RECVAPP) {
|
||||
// return how many received bytes we have
|
||||
size_t alen;
|
||||
br_ssl_engine_recvapp_buf(&m_sslctx.eng, &alen);
|
||||
return (int)(alen);
|
||||
}
|
||||
else if (state == BR_SSL_CLOSED) m_print("Error: Tried to check available when engine is closed");
|
||||
else if (state == BR_SSL_CLOSED) m_warn("Engine closed after update", func_name);
|
||||
// flush the buffer if it's stuck in the SENDAPP state
|
||||
else if (state & BR_SSL_SENDAPP) br_ssl_engine_flush(&m_sslctx.eng, 0);
|
||||
// other state, or client is closed
|
||||
|
@ -199,13 +187,15 @@ void SSLClientImpl::flush() {
|
|||
// trigger a flush, incase there's any leftover data
|
||||
br_ssl_engine_flush(&m_sslctx.eng, 0);
|
||||
// run until application data is ready for pickup
|
||||
if(m_run_until(BR_SSL_RECVAPP) < 0) m_print("Error: could not flush write buffer!");
|
||||
if(m_run_until(BR_SSL_RECVAPP) < 0) m_error("Could not flush write buffer!", __func__);
|
||||
}
|
||||
|
||||
/** see SSLClientImpl.h*/
|
||||
void SSLClientImpl::stop() {
|
||||
// tell the SSL connection to gracefully close
|
||||
br_ssl_engine_close(&m_sslctx.eng);
|
||||
// info about the socket connection
|
||||
if (br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED) m_info("Socket was terminated before graceful closure (probably fine)", __func__);
|
||||
// if the engine isn't closed, and the socket is still open
|
||||
while (br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED
|
||||
&& m_run_until(BR_SSL_RECVAPP) == 0) {
|
||||
|
@ -223,23 +213,44 @@ void SSLClientImpl::stop() {
|
|||
}
|
||||
|
||||
uint8_t SSLClientImpl::connected() {
|
||||
const char* func_name = __func__;
|
||||
// check all of the error cases
|
||||
const auto c_con = m_client->connected();
|
||||
const auto br_con = br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED;
|
||||
const auto wr_ok = getWriteError() == 0;
|
||||
// if we're in an error state, close the connection and set a write error
|
||||
if ((br_con && !c_con) || !wr_ok) {
|
||||
m_print("Error: Socket was unexpectedly interrupted");
|
||||
m_print("Terminated with: ");
|
||||
m_print(m_client->getWriteError());
|
||||
if (br_con && !c_con) {
|
||||
m_error("Socket was unexpectedly interrupted. m_client error: ", func_name);
|
||||
m_error(m_client->getWriteError(), func_name);
|
||||
setWriteError(SSL_CLIENT_WRTIE_ERROR);
|
||||
stop();
|
||||
}
|
||||
else if (!wr_ok) {
|
||||
m_error("Not connected because write error is set", func_name);
|
||||
}
|
||||
return c_con && br_con && wr_ok;
|
||||
}
|
||||
|
||||
bool SSLClientImpl::m_soft_connected(const char* func_name) {
|
||||
// check if the socket is still open and such
|
||||
if (getWriteError()) {
|
||||
m_error("Cannot operate if the write error is not reset: ", func_name);
|
||||
m_print_ssl_error(getWriteError(), SSL_ERROR);
|
||||
return false;
|
||||
}
|
||||
// check if the ssl engine is still open
|
||||
if(br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED) {
|
||||
m_error("Cannot operate on a closed SSL connection.", func_name);
|
||||
int error = br_ssl_engine_last_error(&m_sslctx.eng);
|
||||
if(error != BR_ERR_OK) m_print_br_error(error, SSL_ERROR);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** see SSLClientImpl.h */
|
||||
int SSLClientImpl::m_start_ssl(const char* host, SSLSession& ssl_ses) {
|
||||
const char* func_name = __func__;
|
||||
// clear the write error
|
||||
setWriteError(SSL_OK);
|
||||
// get some random data by reading the analog pin we've been handed
|
||||
|
@ -251,21 +262,22 @@ int SSLClientImpl::m_start_ssl(const char* host, SSLSession& ssl_ses) {
|
|||
// inject session parameters for faster reconnection, if we have any
|
||||
if(ssl_ses.is_valid_session()) {
|
||||
br_ssl_engine_set_session_parameters(&m_sslctx.eng, ssl_ses.to_br_session());
|
||||
m_print("Set session!");
|
||||
m_info("Set SSL session!", func_name);
|
||||
}
|
||||
// reset the engine, but make sure that it reset successfully
|
||||
int ret = br_ssl_client_reset(&m_sslctx, host, 1);
|
||||
if (!ret) {
|
||||
m_print("Error: reset failed");
|
||||
m_print(br_ssl_engine_last_error(&m_sslctx.eng));
|
||||
m_error("Reset of bearSSL failed (is bearssl setup properly?)", func_name);
|
||||
m_print_br_error(br_ssl_engine_last_error(&m_sslctx.eng), SSL_ERROR);
|
||||
setWriteError(SSL_BR_CONNECT_FAIL);
|
||||
return 0;
|
||||
}
|
||||
// initlalize the SSL socket over the network
|
||||
// normally this would happen in write, but I think it makes
|
||||
// a little more structural sense to put it here
|
||||
if (m_run_until(BR_SSL_SENDAPP) < 0) {
|
||||
m_print("Error: Failed to initlalize the SSL layer");
|
||||
m_print(br_ssl_engine_last_error(&m_sslctx.eng));
|
||||
setWriteError(SSL_BR_CONNECT_FAIL);
|
||||
m_error("Failed to initlalize the SSL layer", func_name);
|
||||
m_print_br_error(br_ssl_engine_last_error(&m_sslctx.eng), SSL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
// all good to go! the SSL socket should be up and running
|
||||
|
@ -273,40 +285,33 @@ int SSLClientImpl::m_start_ssl(const char* host, SSLSession& ssl_ses) {
|
|||
br_ssl_engine_get_session_parameters(&m_sslctx.eng, ssl_ses.to_br_session());
|
||||
// set the hostname and ip in the session as well
|
||||
ssl_ses.set_parameters(remoteIP(), host);
|
||||
// print the session details
|
||||
m_print("Session:");
|
||||
for (uint8_t i = 0; i < ssl_ses.session_id_len; i++) {
|
||||
Serial.print(", 0x");
|
||||
Serial.print(ssl_ses.session_id[i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
Serial.println(ssl_ses.cipher_suite, HEX);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** see SSLClientImpl.h*/
|
||||
int SSLClientImpl::m_run_until(const unsigned target) {
|
||||
const char* func_name = __func__;
|
||||
unsigned lastState = 0;
|
||||
size_t lastLen = 0;
|
||||
for (;;) {
|
||||
unsigned state = m_update_engine();
|
||||
// error check
|
||||
if (state == BR_SSL_CLOSED || getWriteError()) {
|
||||
m_print("Error: tried to run_until when the engine is closed");
|
||||
m_warn("Tried to run_until when the engine is closed", func_name);
|
||||
return -1;
|
||||
}
|
||||
// debug
|
||||
if (state != lastState) {
|
||||
lastState = state;
|
||||
m_print("m_run stuck:");
|
||||
m_info("m_run waiting:", func_name);
|
||||
printState(state);
|
||||
}
|
||||
if (state & BR_SSL_RECVREC) {
|
||||
size_t len;
|
||||
br_ssl_engine_recvrec_buf(&m_sslctx.eng, &len);
|
||||
if (lastLen != len) {
|
||||
m_print("Expected bytes count: ");
|
||||
m_print(lastLen = len);
|
||||
m_info("Expected bytes count: ", func_name);
|
||||
m_info(lastLen = len, func_name);
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
@ -328,13 +333,14 @@ int SSLClientImpl::m_run_until(const unsigned target) {
|
|||
size_t len;
|
||||
if (br_ssl_engine_recvapp_buf(&m_sslctx.eng, &len) != NULL) {
|
||||
m_write_idx = 0;
|
||||
m_print("Warn: discarded unread data to favor a write operation");
|
||||
m_warn("Discarded unread data to favor a write operation", func_name);
|
||||
br_ssl_engine_recvapp_ack(&m_sslctx.eng, len);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
m_print("Error: ssl engine state is RECVAPP, however the buffer was null!");
|
||||
m_error("SSL engine state is RECVAPP, however the buffer was null! (This is a problem with BearSSL internals)", func_name);
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
stop();
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -352,6 +358,7 @@ int SSLClientImpl::m_run_until(const unsigned target) {
|
|||
|
||||
/** see SSLClientImpl.h*/
|
||||
unsigned SSLClientImpl::m_update_engine() {
|
||||
const char* func_name = __func__;
|
||||
for(;;) {
|
||||
// get the state
|
||||
unsigned state = br_ssl_engine_current_state(&m_sslctx.eng);
|
||||
|
@ -369,7 +376,9 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
wlen = m_client->write(buf, len);
|
||||
// let the chip recover
|
||||
if (wlen < 0) {
|
||||
m_print("Error writing to m_client");
|
||||
m_error("Error writing to m_client", func_name);
|
||||
m_error(m_client->getWriteError(), func_name);
|
||||
setWriteError(SSL_CLIENT_WRTIE_ERROR);
|
||||
/*
|
||||
* If we received a close_notify and we
|
||||
* still send something, then we have our
|
||||
|
@ -377,10 +386,8 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
* the peer is allowed by RFC 5246 not to
|
||||
* wait for it.
|
||||
*/
|
||||
if (!&m_sslctx.eng.shutdown_recv) {
|
||||
return 0;
|
||||
}
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
if (!&m_sslctx.eng.shutdown_recv) return 0;
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
if (wlen > 0) {
|
||||
|
@ -397,8 +404,9 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
// if we've reached the point where BR_SSL_SENDAPP is off but
|
||||
// data has been written to the io buffer, something is wrong
|
||||
if (!(state & BR_SSL_SENDAPP)) {
|
||||
m_print("Error m_write_idx > 0 but the ssl engine is not ready for data");
|
||||
m_error("Error m_write_idx > 0 but the ssl engine is not ready for data", func_name);
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
// else time to send the application data
|
||||
|
@ -407,14 +415,16 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
unsigned char *buf = br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen);
|
||||
// engine check
|
||||
if (alen == 0 || buf == NULL) {
|
||||
m_print("Error: engine set write flag but returned null buffer");
|
||||
m_error("Engine set write flag but returned null buffer", func_name);
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
// sanity check
|
||||
if (alen < m_write_idx) {
|
||||
m_print("Error: alen is less than m_write_idx");
|
||||
m_error("Alen is less than m_write_idx", func_name);
|
||||
setWriteError(SSL_INTERNAL_ERROR);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
// all good? lets send the data
|
||||
|
@ -442,15 +452,17 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
// do we have the record you're looking for?
|
||||
const auto avail = m_client->available();
|
||||
if (avail >= len) {
|
||||
m_print("Read bytes from client: ");
|
||||
m_print(avail);
|
||||
m_print(len);
|
||||
m_info("Read bytes from client: ", func_name);
|
||||
m_info(avail, func_name);
|
||||
m_info(len, func_name);
|
||||
|
||||
// I suppose so!
|
||||
int rlen = m_client->read(buf, len);
|
||||
if (rlen <= 0) {
|
||||
m_print("Error reading bytes from m_client");
|
||||
setWriteError(SSL_BR_WRITE_ERROR);
|
||||
m_error("Error reading bytes from m_client. Write Error: ", func_name);
|
||||
m_error(m_client->getWriteError(), func_name);
|
||||
setWriteError(SSL_CLIENT_WRTIE_ERROR);
|
||||
stop();
|
||||
return 0;
|
||||
}
|
||||
if (rlen > 0) {
|
||||
|
@ -474,3 +486,103 @@ unsigned SSLClientImpl::m_update_engine() {
|
|||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** See SSLClientImpl.h */
|
||||
void SSLClientImpl::m_print_prefix(const char* func_name, const DebugLevel level) const
|
||||
{
|
||||
// print the sslclient prefix
|
||||
Serial.print("(SSLClient)");
|
||||
// print the debug level
|
||||
switch (level) {
|
||||
case SSL_INFO: Serial.print("SSL_INFO"); break;
|
||||
case SSL_WARN: Serial.print("SSL_WARN"); break;
|
||||
case SSL_ERROR: Serial.print("SSL_ERROR"); break;
|
||||
default: Serial.print("Unknown level");
|
||||
}
|
||||
// print the function name
|
||||
Serial.print(func_name);
|
||||
// get ready
|
||||
Serial.print(": ");
|
||||
}
|
||||
|
||||
/** See SSLClientImpl.h */
|
||||
void SSLClientImpl::m_print_ssl_error(const int ssl_error, const DebugLevel level) const {
|
||||
if (level < m_debug) return;
|
||||
m_print_prefix(__func__, level);
|
||||
switch(ssl_error) {
|
||||
case SSL_OK: Serial.println("SSL_OK"); break;
|
||||
case SSL_CLIENT_CONNECT_FAIL: Serial.println("SSL_CLIENT_CONNECT_FAIL"); break;
|
||||
case SSL_BR_CONNECT_FAIL: Serial.println("SSL_BR_CONNECT_FAIL"); break;
|
||||
case SSL_CLIENT_WRTIE_ERROR: Serial.println("SSL_CLIENT_WRITE_FAIL"); break;
|
||||
case SSL_BR_WRITE_ERROR: Serial.println("SSL_BR_WRITE_ERROR"); break;
|
||||
case SSL_INTERNAL_ERROR: Serial.println("SSL_INTERNAL_ERROR"); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* See SSLClientImpl.h */
|
||||
void SSLClientImpl::m_print_br_error(const unsigned br_error_code, const DebugLevel level) const {
|
||||
if (level < m_debug) return;
|
||||
m_print_prefix(__func__, level);
|
||||
switch (br_error_code) {
|
||||
case BR_ERR_BAD_PARAM: Serial.println("Caller-provided parameter is incorrect."); break;
|
||||
case BR_ERR_BAD_STATE: Serial.println("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: Serial.println("Incoming protocol or record version is unsupported."); break;
|
||||
case BR_ERR_BAD_VERSION: Serial.println("Incoming record version does not match the expected version."); break;
|
||||
case BR_ERR_BAD_LENGTH: Serial.println("Incoming record length is invalid."); break;
|
||||
case BR_ERR_TOO_LARGE: Serial.println("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: Serial.println("Decryption found an invalid padding, or the record MAC is not correct."); break;
|
||||
case BR_ERR_NO_RANDOM: Serial.println("No initial entropy was provided, and none can be obtained from the OS."); break;
|
||||
case BR_ERR_UNKNOWN_TYPE: Serial.println("Incoming record type is unknown."); break;
|
||||
case BR_ERR_UNEXPECTED: Serial.println("Incoming record or message has wrong type with regards to the current engine state."); break;
|
||||
case BR_ERR_BAD_CCS: Serial.println("ChangeCipherSpec message from the peer has invalid contents."); break;
|
||||
case BR_ERR_BAD_ALERT: Serial.println("Alert message from the peer has invalid contents (odd length)."); break;
|
||||
case BR_ERR_BAD_HANDSHAKE: Serial.println("Incoming handshake message decoding failed."); break;
|
||||
case BR_ERR_OVERSIZED_ID: Serial.println("ServerHello contains a session ID which is larger than 32 bytes."); break;
|
||||
case BR_ERR_BAD_CIPHER_SUITE: Serial.println("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: Serial.println("Server wants to use a compression that we did not claim to support."); break;
|
||||
case BR_ERR_BAD_FRAGLEN: Serial.println("Server's max fragment length does not match client's."); break;
|
||||
case BR_ERR_BAD_SECRENEG: Serial.println("Secure renegotiation failed."); break;
|
||||
case BR_ERR_EXTRA_EXTENSION: Serial.println("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: Serial.println("Invalid Server Name Indication contents (when used by the server, this extension shall be empty)."); break;
|
||||
case BR_ERR_BAD_HELLO_DONE: Serial.println("Invalid ServerHelloDone from the server (length is not 0)."); break;
|
||||
case BR_ERR_LIMIT_EXCEEDED: Serial.println("Internal limit exceeded (e.g. server's public key is too large)."); break;
|
||||
case BR_ERR_BAD_FINISHED: Serial.println("Finished message from peer does not match the expected value."); break;
|
||||
case BR_ERR_RESUME_MISMATCH: Serial.println("Session resumption attempt with distinct version or cipher suite."); break;
|
||||
case BR_ERR_INVALID_ALGORITHM: Serial.println("Unsupported or invalid algorithm (ECDHE curve, signature algorithm, hash function)."); break;
|
||||
case BR_ERR_BAD_SIGNATURE: Serial.println("Invalid signature in ServerKeyExchange or CertificateVerify message."); break;
|
||||
case BR_ERR_WRONG_KEY_USAGE: Serial.println("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: Serial.println("Client did not send a certificate upon request, or the client certificate could not be validated."); break;
|
||||
case BR_ERR_IO: Serial.println("I/O error or premature close on transport stream."); break;
|
||||
case BR_ERR_X509_INVALID_VALUE: Serial.println("Invalid value in an ASN.1 structure."); break;
|
||||
case BR_ERR_X509_TRUNCATED: Serial.println("Truncated certificate or other ASN.1 object."); break;
|
||||
case BR_ERR_X509_EMPTY_CHAIN: Serial.println("Empty certificate chain (no certificate at all)."); break;
|
||||
case BR_ERR_X509_INNER_TRUNC: Serial.println("Decoding error: inner element extends beyond outer element size."); break;
|
||||
case BR_ERR_X509_BAD_TAG_CLASS: Serial.println("Decoding error: unsupported tag class (application or private)."); break;
|
||||
case BR_ERR_X509_BAD_TAG_VALUE: Serial.println("Decoding error: unsupported tag value."); break;
|
||||
case BR_ERR_X509_INDEFINITE_LENGTH: Serial.println("Decoding error: indefinite length."); break;
|
||||
case BR_ERR_X509_EXTRA_ELEMENT: Serial.println("Decoding error: extraneous element."); break;
|
||||
case BR_ERR_X509_UNEXPECTED: Serial.println("Decoding error: unexpected element."); break;
|
||||
case BR_ERR_X509_NOT_CONSTRUCTED: Serial.println("Decoding error: expected constructed element, but is primitive."); break;
|
||||
case BR_ERR_X509_NOT_PRIMITIVE: Serial.println("Decoding error: expected primitive element, but is constructed."); break;
|
||||
case BR_ERR_X509_PARTIAL_BYTE: Serial.println("Decoding error: BIT STRING length is not multiple of 8."); break;
|
||||
case BR_ERR_X509_BAD_BOOLEAN: Serial.println("Decoding error: BOOLEAN value has invalid length."); break;
|
||||
case BR_ERR_X509_OVERFLOW: Serial.println("Decoding error: value is off-limits."); break;
|
||||
case BR_ERR_X509_BAD_DN: Serial.println("Invalid distinguished name."); break;
|
||||
case BR_ERR_X509_BAD_TIME: Serial.println("Invalid date/time representation."); break;
|
||||
case BR_ERR_X509_UNSUPPORTED: Serial.println("Certificate contains unsupported features that cannot be ignored."); break;
|
||||
case BR_ERR_X509_LIMIT_EXCEEDED: Serial.println("Key or signature size exceeds internal limits."); break;
|
||||
case BR_ERR_X509_WRONG_KEY_TYPE: Serial.println("Key type does not match that which was expected."); break;
|
||||
case BR_ERR_X509_BAD_SIGNATURE: Serial.println("Signature is invalid."); break;
|
||||
case BR_ERR_X509_TIME_UNKNOWN: Serial.println("Validation time is unknown."); break;
|
||||
case BR_ERR_X509_EXPIRED: Serial.println("Certificate is expired or not yet valid."); break;
|
||||
case BR_ERR_X509_DN_MISMATCH: Serial.println("Issuer/Subject DN mismatch in the chain."); break;
|
||||
case BR_ERR_X509_BAD_SERVER_NAME: Serial.println("Expected server name was not found in the chain."); break;
|
||||
case BR_ERR_X509_CRITICAL_EXTENSION: Serial.println("Unknown critical extension in certificate."); break;
|
||||
case BR_ERR_X509_NOT_CA: Serial.println("Not a CA, or path length constraint violation."); break;
|
||||
case BR_ERR_X509_FORBIDDEN_KEY_USAGE: Serial.println("Key Usage extension prohibits intended usage."); break;
|
||||
case BR_ERR_X509_WEAK_PUBLIC_KEY: Serial.println("Public key found in certificate is too small."); break;
|
||||
case BR_ERR_X509_NOT_TRUSTED: Serial.println("Chain could not be linked to a trust anchor."); break;
|
||||
default: Serial.println("Unknown error code."); break;
|
||||
}
|
||||
}
|
|
@ -26,6 +26,30 @@
|
|||
#ifndef SSLClientImpl_H_
|
||||
#define SSLClientImpl_H_
|
||||
|
||||
/** error enums
|
||||
* Static constants defining the possible errors encountered
|
||||
* Read from getWriteError();
|
||||
*/
|
||||
enum Error {
|
||||
SSL_OK = 0,
|
||||
SSL_CLIENT_CONNECT_FAIL,
|
||||
SSL_BR_CONNECT_FAIL,
|
||||
SSL_CLIENT_WRTIE_ERROR,
|
||||
SSL_BR_WRITE_ERROR,
|
||||
SSL_INTERNAL_ERROR
|
||||
};
|
||||
|
||||
/** Debug level enum
|
||||
* Static enum defining the debugging levels to print
|
||||
* into the Serial monitor
|
||||
*/
|
||||
enum DebugLevel {
|
||||
SSL_NONE = 0,
|
||||
SSL_INFO = 1,
|
||||
SSL_WARN = 2,
|
||||
SSL_ERROR = 3
|
||||
};
|
||||
|
||||
/** TODO: Write what this is */
|
||||
|
||||
class SSLClientImpl : public Client {
|
||||
|
@ -51,7 +75,7 @@ public:
|
|||
* @param debug whether to enable or disable debug logging, must be constexpr
|
||||
*/
|
||||
explicit SSLClientImpl(Client* client, const br_x509_trust_anchor *trust_anchors,
|
||||
const size_t trust_anchors_num, const int analog_pin, const bool debug = true);
|
||||
const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug);
|
||||
/** Dtor is implicit since unique_ptr handles it fine */
|
||||
|
||||
/** functions specific to the EthernetClient which I'll have to override */
|
||||
|
@ -133,30 +157,52 @@ protected:
|
|||
*/
|
||||
void set_client(Client* c) { m_client = c; }
|
||||
|
||||
/** @brief Prints a debugging prefix to all logs, so we can attatch them to useful information */
|
||||
void m_print_prefix(const char* func_name, const DebugLevel level) const;
|
||||
|
||||
/** @brief Prints the string associated with a write error */
|
||||
void m_print_ssl_error(const int ssl_error, const DebugLevel level) const;
|
||||
|
||||
/** @brief Print the text string associated with a BearSSL error code */
|
||||
void m_print_br_error(const unsigned br_error_code, const DebugLevel level) const;
|
||||
|
||||
/** @brief debugging print function, only prints if m_debug is true */
|
||||
template<typename T>
|
||||
constexpr void m_print(const T str) const {
|
||||
if (m_debug) {
|
||||
Serial.print("SSLClientImpl: ");
|
||||
void m_print(const T str, const char* func_name, const DebugLevel level) const {
|
||||
// check the current debug level
|
||||
if (level < m_debug) return;
|
||||
// print prefix
|
||||
m_print_prefix(func_name, level);
|
||||
// print the message
|
||||
Serial.println(str);
|
||||
}
|
||||
}
|
||||
|
||||
/** @brief Prints a info message to serial, if info messages are enabled */
|
||||
template<typename T>
|
||||
void m_info(const T str, const char* func_name) const { m_print(str, func_name, SSL_INFO); }
|
||||
|
||||
template<typename T>
|
||||
void m_warn(const T str, const char* func_name) const { m_print(str, func_name, SSL_WARN); }
|
||||
|
||||
template<typename T>
|
||||
void m_error(const T str, const char* func_name) const { m_print(str, func_name, SSL_ERROR); }
|
||||
|
||||
private:
|
||||
|
||||
void printState(unsigned state) const {
|
||||
if(m_debug) {
|
||||
m_print("State: ");
|
||||
if(state == 0) m_print(" Invalid");
|
||||
else if (state & BR_SSL_CLOSED) m_print(" Connection closed");
|
||||
if(m_debug == DebugLevel::SSL_INFO) {
|
||||
m_info("State: ", __func__);
|
||||
if(state == 0) Serial.println(" Invalid");
|
||||
else if (state & BR_SSL_CLOSED) Serial.println(" Connection closed");
|
||||
else {
|
||||
if (state & BR_SSL_SENDREC) m_print(" SENDREC");
|
||||
if (state & BR_SSL_RECVREC) m_print(" RECVREC");
|
||||
if (state & BR_SSL_SENDAPP) m_print(" SENDAPP");
|
||||
if (state & BR_SSL_RECVAPP) m_print(" RECVAPP");
|
||||
if (state & BR_SSL_SENDREC) Serial.println(" SENDREC");
|
||||
if (state & BR_SSL_RECVREC) Serial.println(" RECVREC");
|
||||
if (state & BR_SSL_SENDAPP) Serial.println(" SENDAPP");
|
||||
if (state & BR_SSL_RECVAPP) Serial.println(" RECVAPP");
|
||||
}
|
||||
}
|
||||
}
|
||||
/** Returns whether or not the engine is connected, without polling the client over SPI or other (as opposed to connected()) */
|
||||
bool m_soft_connected(const char* func_name);
|
||||
/** start the ssl engine on the connected client */
|
||||
int m_start_ssl(const char* host, SSLSession& ssl_ses);
|
||||
/** run the bearssl engine until a certain state */
|
||||
|
@ -172,7 +218,7 @@ private:
|
|||
// store the pin to fetch an RNG see from
|
||||
const int m_analog_pin;
|
||||
// store whether to enable debug logging
|
||||
const bool m_debug;
|
||||
const DebugLevel m_debug;
|
||||
// store the context values required for SSL
|
||||
br_ssl_client_context m_sslctx;
|
||||
br_x509_minimal_context m_x509ctx;
|
||||
|
|
Loading…
Reference in a new issue