made a lot of progress creating the SSLClient, slow but steady
This commit is contained in:
parent
0d424049f0
commit
093d1fac8b
5 changed files with 178 additions and 21 deletions
|
@ -18,4 +18,67 @@
|
||||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "SSLClient.h"
|
#include "SSLClient.h"
|
||||||
|
|
||||||
|
/** see SSLClient.h */
|
||||||
|
SSLClient::SSLClient(const C &client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const bool debug) {
|
||||||
|
// initlalize the various bearssl libraries so they're ready to go when we connect
|
||||||
|
br_client_init_TLS12_only(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num);
|
||||||
|
br_ssl_engine_set_buffer(&m_sslctx, m_iobuf, sizeof m_iobuf, 0);
|
||||||
|
br_sslio_init(&m_ioctx, &m_sslctx.eng, m_readraw, NULL, m_writeraw, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see SSLClient.h */
|
||||||
|
virtual int SSLClient::connect(IPAddress ip, uint16_t port) {
|
||||||
|
// 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.")
|
||||||
|
// first we need our hidden client member to negotiate the socket for us,
|
||||||
|
// since most times socket functionality is implemented in hardeware.
|
||||||
|
if (!this->m_client.connect(ip, port)) {
|
||||||
|
m_print("Failed to connect using m_client");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// reset the client context, and look for previous sessions
|
||||||
|
// in this case we also provide NULL host since we only have an IP
|
||||||
|
br_ssl_client_reset(&sc, NULL, 1);
|
||||||
|
// initlalize the SSL socket over the network
|
||||||
|
// normally this would happen in br_sslio_write, but I think it makes
|
||||||
|
// a little more structural sense to put it here
|
||||||
|
if (br_run_until(ctx, BR_SSL_SENDAPP) < 0) {
|
||||||
|
m_print("Failed to initlalize the SSL layer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// all good to go! the SSL socket should be up and running
|
||||||
|
m_print("SSL Initialized");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* see SSLClient.h */
|
||||||
|
virtual int SSLClient::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 (!this->m_client.connect(host, port)) {
|
||||||
|
m_print("Failed to connect using m_client");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// reset the client context, and look for previous sessions
|
||||||
|
br_ssl_client_reset(&sc, host, 1);
|
||||||
|
// initlalize the SSL socket over the network
|
||||||
|
// normally this would happen in br_sslio_write, but I think it makes
|
||||||
|
// a little more structural sense to put it here
|
||||||
|
if (br_run_until(ctx, BR_SSL_SENDAPP) < 0) {
|
||||||
|
m_print("Failed to initlalize the SSL layer");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// all good to go! the SSL socket should be up and running
|
||||||
|
m_print("SSL Initialized");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual size_t SSLClient::write(const uint8_t *buf, size_t size) {
|
||||||
|
// check if the socket is still open and such
|
||||||
|
|
||||||
|
// write to the ssl socket using bearssl, and error check
|
||||||
|
int status = br_sslio_write_all(&m_ioctx, buf, len);
|
||||||
|
if (status < 0 )
|
||||||
|
}
|
|
@ -50,18 +50,22 @@ static_assert(std::is_base_of(Client, C)::value, "C must be a Client Class!");
|
||||||
static_assert(std::is_function(decltype(C::status))::value, "C must have a status() function!");
|
static_assert(std::is_function(decltype(C::status))::value, "C must have a status() function!");
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
/** Ctor
|
* @brief copies the client object and initializes SSL contexts for bearSSL
|
||||||
* Creates a new dynamically allocated Client object based on the
|
|
||||||
* one passed to client
|
|
||||||
* We copy the client because we aren't sure the Client object
|
* We copy the client because we aren't sure the Client object
|
||||||
* is going to exists past the inital creation of the SSLClient.
|
* is going to exists past the inital creation of the SSLClient.
|
||||||
* @param client the (Ethernet)client object
|
* @param client the (Ethernet)client object
|
||||||
|
* @param trust_anchors Trust anchors used in the verification
|
||||||
|
* of the SSL server certificate, generated using the `brssl` command
|
||||||
|
* line utility. For more information see the samples or bearssl.org
|
||||||
|
* @param trust_anchors_num The number of trust anchors stored
|
||||||
|
* @param debug whether to enable or disable debug logging, must be constexpr
|
||||||
*/
|
*/
|
||||||
SSLClient(const C &client):
|
SSLClient(const C &client, const br_x509_trust_anchor *trust_anchors, const size_t trust_anchors_num, const bool debug = true)
|
||||||
m_client(client)
|
: m_client(client)
|
||||||
{
|
, m_trust_anchors(trust_anchors)
|
||||||
}
|
, m_trust_anchors_num(trust_anchors_num)
|
||||||
|
, m_debug(debug);
|
||||||
|
|
||||||
/** Dtor is implicit since unique_ptr handles it fine */
|
/** Dtor is implicit since unique_ptr handles it fine */
|
||||||
|
|
||||||
|
@ -83,10 +87,50 @@ public:
|
||||||
/** functions specific to the EthernetClient which I'll have to override */
|
/** functions specific to the EthernetClient which I'll have to override */
|
||||||
uint8_t status() const;
|
uint8_t status() const;
|
||||||
uint8_t getSocketNumber() const;
|
uint8_t getSocketNumber() const;
|
||||||
|
|
||||||
/** functions dealing with read/write that BearSSL will be injected into */
|
/** functions dealing with read/write that BearSSL will be injected into */
|
||||||
virtual int connect(IPAddress ip, uint16_t port);
|
/**
|
||||||
virtual int connect(const char *host, uint16_t port);
|
* @brief Connect over SSL to a host specified by an ip address
|
||||||
virtual size_t write(uint8_t);
|
*
|
||||||
|
* SSLClient::connect(host, port) should be preffered over this function,
|
||||||
|
* as verifying the domain name is a step in ensuring the certificate is
|
||||||
|
* legitimate, which is important to the security of the device. Additionally,
|
||||||
|
* SSL sessions cannot be resumed, which can drastically increase initial
|
||||||
|
* connect time.
|
||||||
|
*
|
||||||
|
* This function initializes EthernetClient by calling EthernetClient::connect
|
||||||
|
* with the parameters supplied, then once the socket is open initializes
|
||||||
|
* the appropriete bearssl contexts using the TLS_only_profile. Due to the
|
||||||
|
* design of the SSL standard, this function will probably take an extended
|
||||||
|
* period (1-2sec) to negotiate the handshake and finish the connection.
|
||||||
|
*
|
||||||
|
* @param ip The ip address to connect to
|
||||||
|
* @param port the port to connect to
|
||||||
|
* @returns 1 if success, 0 if failure (as found in EthernetClient)
|
||||||
|
*/
|
||||||
|
virtual int connect(IPAddress ip, uint16_t port = 443);
|
||||||
|
/**
|
||||||
|
* @brief Connect over SSL using connect(ip, port), but use a DNS lookup to
|
||||||
|
* get the IP Address first.
|
||||||
|
*
|
||||||
|
* This function initializes EthernetClient by calling EthernetClient::connect
|
||||||
|
* with the parameters supplied, then once the socket is open initializes
|
||||||
|
* the appropriete bearssl contexts using the TLS_only_profile.
|
||||||
|
*
|
||||||
|
* Due to the design of the SSL standard, this function will probably take an
|
||||||
|
* extended period (1-2sec) to negotiate the handshake and finish the
|
||||||
|
* connection. Since the hostname is provided, however, BearSSL is able to keep
|
||||||
|
* a session cache of the clients we have connected to. This should reduce
|
||||||
|
* connection time greatly. In order to use this feature, you must reuse the
|
||||||
|
* same SSLClient object to connect to the reused host. Doing this will allow
|
||||||
|
* BearSSL to automatically match the hostname to a cached session.
|
||||||
|
*
|
||||||
|
* @param host The cstring host ("www.google.com")
|
||||||
|
* @param port the port to connect to
|
||||||
|
* @returns 1 of success, 0 if failure (as found in EthernetClient)
|
||||||
|
*/
|
||||||
|
virtual int connect(const char *host, uint16_t port = 443);
|
||||||
|
virtual size_t write(uint8_t b) { return write(&b, 1); }
|
||||||
virtual size_t write(const uint8_t *buf, size_t size);
|
virtual size_t write(const uint8_t *buf, size_t size);
|
||||||
virtual int available();
|
virtual int available();
|
||||||
virtual int read();
|
virtual int read();
|
||||||
|
@ -100,8 +144,26 @@ public:
|
||||||
C& getClient() { return m_client; }
|
C& getClient() { return m_client; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/** @brief debugging print function, only prints if m_debug is true */
|
||||||
|
template<type T>
|
||||||
|
constexpr void m_print(const T str) {
|
||||||
|
if (m_debug) {
|
||||||
|
Serial.print("SSLClient: ");
|
||||||
|
Serial.println(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Callback function pointing to m_client.read to be used by the br_sslio API */
|
||||||
|
int m_readraw(void *ctx, unsigned char *buf, size_t len);
|
||||||
|
/** Callback function pointing to m_client.write to be used by the br_sslio API */
|
||||||
|
int m_writeraw(void *ctx, unsigned char *buf, size_t len);
|
||||||
// create a copy of the client
|
// create a copy of the client
|
||||||
C m_client;
|
C m_client;
|
||||||
|
// store pointers to the trust anchors
|
||||||
|
// should not be computed at runtime
|
||||||
|
constexpr br_x509_trust_anchor *m_trust_anchors;
|
||||||
|
constexpr size_t m_trust_anchors_num;
|
||||||
|
// store whether to enable debug logging
|
||||||
|
constexpr bool 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;
|
||||||
|
|
|
@ -49,18 +49,18 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
TLS12_only_profile(br_ssl_client_context *cc
|
br_client_init_TLS12_only(br_ssl_client_context *cc,
|
||||||
br_x509_minimal_context *xc,
|
br_x509_minimal_context *xc,
|
||||||
const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num)
|
const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The "full" profile supports all implemented cipher suites.
|
* The TLS1.2 profile supports widely used implemented cipher suites.
|
||||||
*
|
*
|
||||||
* Rationale for suite order, from most important to least
|
* Rationale for suite order, from most important to least
|
||||||
* important rule:
|
* important rule:
|
||||||
*
|
*
|
||||||
* -- Only support TLS 1.2
|
* -- Only support TLS 1.2
|
||||||
* -- Don't support RSA and 3DES as they are weaker protocols
|
* -- Don't support RSA and 3DES as primary encryption as they are weaker protocols
|
||||||
* -- Try to have Forward Secrecy (ECDHE suite) if possible.
|
* -- Try to have Forward Secrecy (ECDHE suite) if possible.
|
||||||
* -- When not using Forward Secrecy, ECDH key exchange is
|
* -- When not using Forward Secrecy, ECDH key exchange is
|
||||||
* better than RSA key exchange (slightly more expensive on the
|
* better than RSA key exchange (slightly more expensive on the
|
||||||
|
|
|
@ -48,8 +48,8 @@ br_sslio_init(br_sslio_context *ctx,
|
||||||
* combination of both (the combination matches either). When a match is
|
* combination of both (the combination matches either). When a match is
|
||||||
* achieved, this function returns 0. On error, it returns -1.
|
* achieved, this function returns 0. On error, it returns -1.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
run_until(br_sslio_context *ctx, unsigned target)
|
br_run_until(br_sslio_context *ctx, unsigned target)
|
||||||
{
|
{
|
||||||
for (;;) {
|
for (;;) {
|
||||||
unsigned state;
|
unsigned state;
|
||||||
|
@ -152,7 +152,7 @@ br_sslio_read(br_sslio_context *ctx, void *dst, size_t len)
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (run_until(ctx, BR_SSL_RECVAPP) < 0) {
|
if (br_run_until(ctx, BR_SSL_RECVAPP) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen);
|
buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen);
|
||||||
|
@ -194,7 +194,7 @@ br_sslio_write(br_sslio_context *ctx, const void *src, size_t len)
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (run_until(ctx, BR_SSL_SENDAPP) < 0) {
|
if (br_run_until(ctx, BR_SSL_SENDAPP) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen);
|
buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen);
|
||||||
|
@ -238,7 +238,7 @@ br_sslio_flush(br_sslio_context *ctx)
|
||||||
* first sent down the wire before considering anything else.
|
* first sent down the wire before considering anything else.
|
||||||
*/
|
*/
|
||||||
br_ssl_engine_flush(ctx->engine, 0);
|
br_ssl_engine_flush(ctx->engine, 0);
|
||||||
return run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
|
return br_run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* see bearssl_ssl.h */
|
/* see bearssl_ssl.h */
|
||||||
|
@ -252,7 +252,7 @@ br_sslio_close(br_sslio_context *ctx)
|
||||||
*/
|
*/
|
||||||
size_t len;
|
size_t len;
|
||||||
|
|
||||||
run_until(ctx, BR_SSL_RECVAPP);
|
br_run_until(ctx, BR_SSL_RECVAPP);
|
||||||
if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) {
|
if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) {
|
||||||
br_ssl_engine_recvapp_ack(ctx->engine, len);
|
br_ssl_engine_recvapp_ack(ctx->engine, len);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2735,6 +2735,24 @@ void br_ssl_client_init_full(br_ssl_client_context *cc,
|
||||||
br_x509_minimal_context *xc,
|
br_x509_minimal_context *xc,
|
||||||
const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
|
const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief SSL client profile: TLS1.2 minus weak ciphers
|
||||||
|
*
|
||||||
|
* This function initialises the provided SSL client context with
|
||||||
|
* most (see brief) supported algorithms and cipher suites. It also initialises
|
||||||
|
* a companion X.509 validation engine with most supported algorithms,
|
||||||
|
* and the provided trust anchors; the X.509 engine will be used by
|
||||||
|
* the client context to validate the server's certificate.
|
||||||
|
*
|
||||||
|
* \param cc client context to initialise.
|
||||||
|
* \param xc X.509 validation context to initialise.
|
||||||
|
* \param trust_anchors trust anchors to use.
|
||||||
|
* \param trust_anchors_num number of trust anchors.
|
||||||
|
*/
|
||||||
|
void br_client_init_TLS12_only(br_ssl_client_context *cc,
|
||||||
|
br_x509_minimal_context *xc,
|
||||||
|
const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Clear the complete contents of a SSL client context.
|
* \brief Clear the complete contents of a SSL client context.
|
||||||
*
|
*
|
||||||
|
@ -4136,6 +4154,20 @@ int br_sslio_flush(br_sslio_context *cc);
|
||||||
*/
|
*/
|
||||||
int br_sslio_close(br_sslio_context *cc);
|
int br_sslio_close(br_sslio_context *cc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* Static function made public since we would like to be able to
|
||||||
|
* initialize the ssl socket in a single function
|
||||||
|
*
|
||||||
|
* \return 0 on success, or -1 on error.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
br_run_until(br_sslio_context *ctx, unsigned target);
|
||||||
|
|
||||||
/* ===================================================================== */
|
/* ===================================================================== */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in a new issue