added lots and lots of Serial debugging framework, need to test and make sure it works

This commit is contained in:
Noah Laptop 2019-03-11 18:31:15 -07:00
parent 2375ec0339
commit c212e355a4
3 changed files with 246 additions and 101 deletions

View file

@ -42,19 +42,6 @@
#ifndef SSLClient_H_ #ifndef SSLClient_H_
#define 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. * \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 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 * @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) : SSLClientImpl(NULL, trust_anchors, trust_anchors_num, analog_pin, debug)
, m_client(client) , m_client(client)
, m_sessions{} , m_sessions{}
@ -153,8 +140,8 @@ public:
// increment m_index so the session cache is a circular buffer // increment m_index so the session cache is a circular buffer
if (temp_index == m_index && ++m_index >= SessionCache) m_index = 0; if (temp_index == m_index && ++m_index >= SessionCache) m_index = 0;
// return the pointed to value // return the pointed to value
m_print("Using index: "); m_info("Using session index: ", __func__);
m_print(temp_index); Serial.println(temp_index);
return m_sessions[temp_index]; return m_sessions[temp_index];
} }

View file

@ -22,7 +22,7 @@
/** see SSLClientImpl.h */ /** see SSLClientImpl.h */
SSLClientImpl::SSLClientImpl(Client *client, const br_x509_trust_anchor *trust_anchors, 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_client(client)
, m_trust_anchors(trust_anchors) , m_trust_anchors(trust_anchors)
, m_trust_anchors_num(trust_anchors_num) , 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*/ /* see SSLClientImpl.h*/
int SSLClientImpl::connect(IPAddress ip, uint16_t port) { int SSLClientImpl::connect(IPAddress ip, uint16_t port) {
const char* func_name = __func__;
// connection check // connection check
if (connected()) { 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; return -1;
} }
// reset indexs for saftey // reset indexs for saftey
m_write_idx = 0; m_write_idx = 0;
// Warning for security // 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, // first we need our hidden client member to negotiate the socket for us,
// since most times socket functionality is implemented in hardeware. // since most times socket functionality is implemented in hardeware.
if (!m_client->connect(ip, port)) { 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); setWriteError(SSL_CLIENT_CONNECT_FAIL);
return 0; return 0;
} }
m_print("Base ethernet client connected!"); m_info("Base client connected!", func_name);
return m_start_ssl(NULL, getSession(NULL, ip)); return m_start_ssl(NULL, getSession(NULL, ip));
} }
/* see SSLClientImpl.h*/ /* see SSLClientImpl.h*/
int SSLClientImpl::connect(const char *host, uint16_t port) { int SSLClientImpl::connect(const char *host, uint16_t port) {
const char* func_name = __func__;
// connection check // connection check
if (connected()) { 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; return -1;
} }
// reset indexs for saftey // reset indexs for saftey
@ -78,7 +80,7 @@ int SSLClientImpl::connect(const char *host, uint16_t port) {
SSLSession& ses = getSession(host, INADDR_NONE); SSLSession& ses = getSession(host, INADDR_NONE);
if (ses.is_valid_session()) { if (ses.is_valid_session()) {
// if so, then connect using the stored 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); connect_ok = m_client->connect(ses.get_ip(), port);
} }
// else connect with the provided hostname // 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, // first we need our hidden client member to negotiate the socket for us,
// since most times socket functionality is implemented in hardeware. // since most times socket functionality is implemented in hardeware.
if (!connect_ok) { 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); setWriteError(SSL_CLIENT_CONNECT_FAIL);
return 0; return 0;
} }
m_print("Base ethernet client connected!"); m_info("Base client connected!", func_name);
// start ssl! // start ssl!
return m_start_ssl(host, ses); return m_start_ssl(host, ses);
} }
/** see SSLClientImpl.h*/ /** see SSLClientImpl.h*/
size_t SSLClientImpl::write(const uint8_t *buf, size_t size) { 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 // check if the socket is still open and such
if(br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED || getWriteError()) { if (!m_soft_connected(func_name)) return 0;
m_print("Client is not connected! Perhaps something has happened?");
return 0;
}
// add to the bearssl io buffer, simply appending whatever we want to write // add to the bearssl io buffer, simply appending whatever we want to write
size_t alen; size_t alen;
unsigned char *br_buf = br_ssl_engine_sendapp_buf(&m_sslctx.eng, &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 // to the buffer in which we conclude it's already safe to write
if(m_write_idx == 0) { if(m_write_idx == 0) {
if (m_run_until(BR_SSL_SENDAPP) < 0) { if (m_run_until(BR_SSL_SENDAPP) < 0) {
m_print("Error: could not run until sendapp"); m_error("Failed while waiting for the engine to enter BR_SSL_SENDAPP", func_name);
setWriteError(SSL_BR_WRITE_ERROR);
return 0; return 0;
} }
// reset the buffer pointer // reset the buffer pointer
br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen); 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 // if we're about to fill the buffer, we need to send the data and then wait
// for another oppurtinity to send // 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; 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); memcpy(br_buf + m_write_idx, buf + cur_idx, cpamount);
// if we filled the buffer, reset m_write_idx // 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*/ /** see SSLClientImpl.h*/
int SSLClientImpl::available() { int SSLClientImpl::available() {
const char* func_name = __func__;
// connection check // connection check
if (br_ssl_engine_current_state(&m_sslctx.eng) == BR_SSL_CLOSED || getWriteError()) { if (!m_soft_connected(func_name)) return 0;
m_print("Warn: Cannot check available of disconnected client");
return 0;
}
// run the SSL engine until we are waiting for either user input or a server response // run the SSL engine until we are waiting for either user input or a server response
unsigned state = m_update_engine(); unsigned state = m_update_engine();
if (state == 0) { if (state == 0) m_error("SSL engine failed to update.", func_name);
m_print("Error: SSL engine failed: ");
m_print(br_ssl_engine_last_error(&m_sslctx.eng));
setWriteError(SSL_BR_WRITE_ERROR);
}
else if(state & BR_SSL_RECVAPP) { else if(state & BR_SSL_RECVAPP) {
// return how many received bytes we have // return how many received bytes we have
size_t alen; size_t alen;
br_ssl_engine_recvapp_buf(&m_sslctx.eng, &alen); br_ssl_engine_recvapp_buf(&m_sslctx.eng, &alen);
return (int)(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 // 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); else if (state & BR_SSL_SENDAPP) br_ssl_engine_flush(&m_sslctx.eng, 0);
// other state, or client is closed // other state, or client is closed
@ -199,13 +187,15 @@ void SSLClientImpl::flush() {
// trigger a flush, incase there's any leftover data // trigger a flush, incase there's any leftover data
br_ssl_engine_flush(&m_sslctx.eng, 0); br_ssl_engine_flush(&m_sslctx.eng, 0);
// run until application data is ready for pickup // 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*/ /** see SSLClientImpl.h*/
void SSLClientImpl::stop() { void SSLClientImpl::stop() {
// tell the SSL connection to gracefully close // tell the SSL connection to gracefully close
br_ssl_engine_close(&m_sslctx.eng); 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 // if the engine isn't closed, and the socket is still open
while (br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED while (br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED
&& m_run_until(BR_SSL_RECVAPP) == 0) { && m_run_until(BR_SSL_RECVAPP) == 0) {
@ -223,23 +213,44 @@ void SSLClientImpl::stop() {
} }
uint8_t SSLClientImpl::connected() { uint8_t SSLClientImpl::connected() {
const char* func_name = __func__;
// check all of the error cases // check all of the error cases
const auto c_con = m_client->connected(); const auto c_con = m_client->connected();
const auto br_con = br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED; const auto br_con = br_ssl_engine_current_state(&m_sslctx.eng) != BR_SSL_CLOSED;
const auto wr_ok = getWriteError() == 0; const auto wr_ok = getWriteError() == 0;
// if we're in an error state, close the connection and set a write error // if we're in an error state, close the connection and set a write error
if ((br_con && !c_con) || !wr_ok) { if (br_con && !c_con) {
m_print("Error: Socket was unexpectedly interrupted"); m_error("Socket was unexpectedly interrupted. m_client error: ", func_name);
m_print("Terminated with: "); m_error(m_client->getWriteError(), func_name);
m_print(m_client->getWriteError());
setWriteError(SSL_CLIENT_WRTIE_ERROR); setWriteError(SSL_CLIENT_WRTIE_ERROR);
stop(); stop();
} }
else if (!wr_ok) {
m_error("Not connected because write error is set", func_name);
}
return c_con && br_con && wr_ok; 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 */ /** see SSLClientImpl.h */
int SSLClientImpl::m_start_ssl(const char* host, SSLSession& ssl_ses) { int SSLClientImpl::m_start_ssl(const char* host, SSLSession& ssl_ses) {
const char* func_name = __func__;
// clear the write error // clear the write error
setWriteError(SSL_OK); setWriteError(SSL_OK);
// get some random data by reading the analog pin we've been handed // 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 // inject session parameters for faster reconnection, if we have any
if(ssl_ses.is_valid_session()) { if(ssl_ses.is_valid_session()) {
br_ssl_engine_set_session_parameters(&m_sslctx.eng, ssl_ses.to_br_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 // reset the engine, but make sure that it reset successfully
int ret = br_ssl_client_reset(&m_sslctx, host, 1); int ret = br_ssl_client_reset(&m_sslctx, host, 1);
if (!ret) { if (!ret) {
m_print("Error: reset failed"); m_error("Reset of bearSSL failed (is bearssl setup properly?)", func_name);
m_print(br_ssl_engine_last_error(&m_sslctx.eng)); 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 // initlalize the SSL socket over the network
// normally this would happen in write, but I think it makes // normally this would happen in write, but I think it makes
// a little more structural sense to put it here // a little more structural sense to put it here
if (m_run_until(BR_SSL_SENDAPP) < 0) { if (m_run_until(BR_SSL_SENDAPP) < 0) {
m_print("Error: Failed to initlalize the SSL layer"); m_error("Failed to initlalize the SSL layer", func_name);
m_print(br_ssl_engine_last_error(&m_sslctx.eng)); m_print_br_error(br_ssl_engine_last_error(&m_sslctx.eng), SSL_ERROR);
setWriteError(SSL_BR_CONNECT_FAIL);
return 0; return 0;
} }
// all good to go! the SSL socket should be up and running // 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()); br_ssl_engine_get_session_parameters(&m_sslctx.eng, ssl_ses.to_br_session());
// set the hostname and ip in the session as well // set the hostname and ip in the session as well
ssl_ses.set_parameters(remoteIP(), host); 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; return 1;
} }
/** see SSLClientImpl.h*/ /** see SSLClientImpl.h*/
int SSLClientImpl::m_run_until(const unsigned target) { int SSLClientImpl::m_run_until(const unsigned target) {
const char* func_name = __func__;
unsigned lastState = 0; unsigned lastState = 0;
size_t lastLen = 0; size_t lastLen = 0;
for (;;) { for (;;) {
unsigned state = m_update_engine(); unsigned state = m_update_engine();
// error check // error check
if (state == BR_SSL_CLOSED || getWriteError()) { 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; return -1;
} }
// debug // debug
if (state != lastState) { if (state != lastState) {
lastState = state; lastState = state;
m_print("m_run stuck:"); m_info("m_run waiting:", func_name);
printState(state); printState(state);
} }
if (state & BR_SSL_RECVREC) { if (state & BR_SSL_RECVREC) {
size_t len; size_t len;
br_ssl_engine_recvrec_buf(&m_sslctx.eng, &len); br_ssl_engine_recvrec_buf(&m_sslctx.eng, &len);
if (lastLen != len) { if (lastLen != len) {
m_print("Expected bytes count: "); m_info("Expected bytes count: ", func_name);
m_print(lastLen = len); m_info(lastLen = len, func_name);
} }
} }
/* /*
@ -328,13 +333,14 @@ int SSLClientImpl::m_run_until(const unsigned target) {
size_t len; size_t len;
if (br_ssl_engine_recvapp_buf(&m_sslctx.eng, &len) != NULL) { if (br_ssl_engine_recvapp_buf(&m_sslctx.eng, &len) != NULL) {
m_write_idx = 0; 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); br_ssl_engine_recvapp_ack(&m_sslctx.eng, len);
continue; continue;
} }
else { 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); setWriteError(SSL_BR_WRITE_ERROR);
stop();
return -1; return -1;
} }
} }
@ -352,6 +358,7 @@ int SSLClientImpl::m_run_until(const unsigned target) {
/** see SSLClientImpl.h*/ /** see SSLClientImpl.h*/
unsigned SSLClientImpl::m_update_engine() { unsigned SSLClientImpl::m_update_engine() {
const char* func_name = __func__;
for(;;) { for(;;) {
// get the state // get the state
unsigned state = br_ssl_engine_current_state(&m_sslctx.eng); 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); wlen = m_client->write(buf, len);
// let the chip recover // let the chip recover
if (wlen < 0) { 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 * If we received a close_notify and we
* still send something, then we have our * 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 * the peer is allowed by RFC 5246 not to
* wait for it. * wait for it.
*/ */
if (!&m_sslctx.eng.shutdown_recv) { if (!&m_sslctx.eng.shutdown_recv) return 0;
return 0; stop();
}
setWriteError(SSL_BR_WRITE_ERROR);
return 0; return 0;
} }
if (wlen > 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 // if we've reached the point where BR_SSL_SENDAPP is off but
// data has been written to the io buffer, something is wrong // data has been written to the io buffer, something is wrong
if (!(state & BR_SSL_SENDAPP)) { 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); setWriteError(SSL_BR_WRITE_ERROR);
stop();
return 0; return 0;
} }
// else time to send the application data // 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); unsigned char *buf = br_ssl_engine_sendapp_buf(&m_sslctx.eng, &alen);
// engine check // engine check
if (alen == 0 || buf == NULL) { 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); setWriteError(SSL_BR_WRITE_ERROR);
stop();
return 0; return 0;
} }
// sanity check // sanity check
if (alen < m_write_idx) { 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); setWriteError(SSL_INTERNAL_ERROR);
stop();
return 0; return 0;
} }
// all good? lets send the data // all good? lets send the data
@ -442,15 +452,17 @@ unsigned SSLClientImpl::m_update_engine() {
// do we have the record you're looking for? // do we have the record you're looking for?
const auto avail = m_client->available(); const auto avail = m_client->available();
if (avail >= len) { if (avail >= len) {
m_print("Read bytes from client: "); m_info("Read bytes from client: ", func_name);
m_print(avail); m_info(avail, func_name);
m_print(len); m_info(len, func_name);
// I suppose so! // I suppose so!
int rlen = m_client->read(buf, len); int rlen = m_client->read(buf, len);
if (rlen <= 0) { if (rlen <= 0) {
m_print("Error reading bytes from m_client"); m_error("Error reading bytes from m_client. Write Error: ", func_name);
setWriteError(SSL_BR_WRITE_ERROR); m_error(m_client->getWriteError(), func_name);
setWriteError(SSL_CLIENT_WRTIE_ERROR);
stop();
return 0; return 0;
} }
if (rlen > 0) { if (rlen > 0) {
@ -474,3 +486,103 @@ unsigned SSLClientImpl::m_update_engine() {
return state; 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;
}
}

View file

@ -26,6 +26,30 @@
#ifndef SSLClientImpl_H_ #ifndef SSLClientImpl_H_
#define 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 */ /** TODO: Write what this is */
class SSLClientImpl : public Client { class SSLClientImpl : public Client {
@ -51,7 +75,7 @@ public:
* @param debug whether to enable or disable debug logging, must be constexpr * @param debug whether to enable or disable debug logging, must be constexpr
*/ */
explicit SSLClientImpl(Client* client, const br_x509_trust_anchor *trust_anchors, 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 */ /** Dtor is implicit since unique_ptr handles it fine */
/** functions specific to the EthernetClient which I'll have to override */ /** functions specific to the EthernetClient which I'll have to override */
@ -133,30 +157,52 @@ protected:
*/ */
void set_client(Client* c) { m_client = c; } 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 */ /** @brief debugging print function, only prints if m_debug is true */
template<typename T> template<typename T>
constexpr void m_print(const T str) const { void m_print(const T str, const char* func_name, const DebugLevel level) const {
if (m_debug) { // check the current debug level
Serial.print("SSLClientImpl: "); if (level < m_debug) return;
Serial.println(str); // print prefix
} m_print_prefix(func_name, level);
// print the message
Serial.println(str);
} }
private: /** @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 { void printState(unsigned state) const {
if(m_debug) { if(m_debug == DebugLevel::SSL_INFO) {
m_print("State: "); m_info("State: ", __func__);
if(state == 0) m_print(" Invalid"); if(state == 0) Serial.println(" Invalid");
else if (state & BR_SSL_CLOSED) m_print(" Connection closed"); else if (state & BR_SSL_CLOSED) Serial.println(" Connection closed");
else { else {
if (state & BR_SSL_SENDREC) m_print(" SENDREC"); if (state & BR_SSL_SENDREC) Serial.println(" SENDREC");
if (state & BR_SSL_RECVREC) m_print(" RECVREC"); if (state & BR_SSL_RECVREC) Serial.println(" RECVREC");
if (state & BR_SSL_SENDAPP) m_print(" SENDAPP"); if (state & BR_SSL_SENDAPP) Serial.println(" SENDAPP");
if (state & BR_SSL_RECVAPP) m_print(" RECVAPP"); 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 */ /** start the ssl engine on the connected client */
int m_start_ssl(const char* host, SSLSession& ssl_ses); int m_start_ssl(const char* host, SSLSession& ssl_ses);
/** run the bearssl engine until a certain state */ /** run the bearssl engine until a certain state */
@ -172,7 +218,7 @@ private:
// store the pin to fetch an RNG see from // store the pin to fetch an RNG see from
const int m_analog_pin; const int m_analog_pin;
// store whether to enable debug logging // store whether to enable debug logging
const bool m_debug; const DebugLevel m_debug;
// store the context values required for SSL // store the context values required for SSL
br_ssl_client_context m_sslctx; br_ssl_client_context m_sslctx;
br_x509_minimal_context m_x509ctx; br_x509_minimal_context m_x509ctx;