diff --git a/src/SSLClient.h b/src/SSLClient.h index 215a2b3..6116d61 100644 --- a/src/SSLClient.h +++ b/src/SSLClient.h @@ -22,6 +22,7 @@ #include "SSLClientImpl.h" #include "SSLSession.h" #include "SSLClientParameters.h" +#include "SSLObj.h" #ifndef SSLClient_H_ #define SSLClient_H_ @@ -81,25 +82,6 @@ public: // SSL Connections take a really long time so we don't want to time out a legitimate thing setTimeout(30 * 1000); } - - /** - * Same as SSLClient::SSLClient(const C &, const br_x509_trust_anchor*, const size_t, const int, const DebugLevel), - * but can compile support for mutual authentication. - */ - 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, - const SSLClientParameters* mutual_auth_params) - : SSLClientImpl(trust_anchors, trust_anchors_num, analog_pin, debug, mutual_auth_params) - , m_client(client) - , m_sessions{} - { - // set the timeout to a reasonable number (it can always be changes later) - // SSL Connections take a really long time so we don't want to time out a legitimate thing - setTimeout(30 * 1000); - } //======================================== //= Functions implemented in SSLClientImpl @@ -307,6 +289,13 @@ public: //= Functions Not in the Client Interface //======================================== + /** + * @brief Add a client certificate and enable support for mutual auth + * + * This function must be called BEFORE making an SSL connection. + */ + void setMutualAuthParams(const SSLClientParameters* params) { return set_mutual_impl(params); } + /** * @brief Gets a session reference corresponding to a host and IP, or a reference to a empty session if none exist * diff --git a/src/SSLClientImpl.cpp b/src/SSLClientImpl.cpp index 13ed636..eaa94aa 100644 --- a/src/SSLClientImpl.cpp +++ b/src/SSLClientImpl.cpp @@ -69,23 +69,6 @@ SSLClientImpl::SSLClientImpl(const br_x509_trust_anchor *trust_anchors, br_ssl_engine_set_buffer(&m_sslctx.eng, m_iobuf, sizeof m_iobuf, duplex); } -/* see SSLClientImpl.h */ -SSLClientImpl::SSLClientImpl(const br_x509_trust_anchor *trust_anchors, - const size_t trust_anchors_num, const int analog_pin, - const DebugLevel debug, const SSLClientParameters* mutual_auth_params) - : SSLClientImpl(trust_anchors, trust_anchors_num, analog_pin, debug) { - // if mutual authentication if needed, configure bearssl to support it. - if (mutual_auth_params != nullptr) - br_ssl_client_set_single_ec( &m_sslctx, - mutual_auth_params->client_cert_chain, - mutual_auth_params->chain_len, - &mutual_auth_params->ec_key, - BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, - BR_KEYTYPE_EC, - br_ssl_engine_get_ec(&m_sslctx.eng), - &br_ecdsa_i15_sign_asn1); - } - /* see SSLClientImpl.h*/ int SSLClientImpl::connect_impl(IPAddress ip, uint16_t port) { const char* func_name = __func__; @@ -331,6 +314,20 @@ void SSLClientImpl::remove_session_impl(const char* host, const IPAddress& addr) } } +/* see SSLClientImpl.h */ +void SSLClientImpl::set_mutual_impl(const SSLClientParameters* params) { + // if mutual authentication if needed, configure bearssl to support it. + if (params != nullptr) + br_ssl_client_set_single_ec( &m_sslctx, + params->client_cert_chain, + params->chain_len, + ¶ms->ec_key, + BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, + BR_KEYTYPE_EC, + br_ssl_engine_get_ec(&m_sslctx.eng), + &br_ecdsa_i15_sign_asn1); +} + bool SSLClientImpl::m_soft_connected(const char* func_name) { // check if the socket is still open and such if (getWriteError()) { diff --git a/src/SSLClientImpl.h b/src/SSLClientImpl.h index 01af2f1..931ad77 100644 --- a/src/SSLClientImpl.h +++ b/src/SSLClientImpl.h @@ -107,7 +107,8 @@ public: SSLSession& get_session_impl(const char* host, const IPAddress& addr); /** @see SSLClient::removeSession */ void remove_session_impl(const char* host, const IPAddress& addr); - + /** @see SSLClient::setMutualAuthParams */ + void set_mutual_impl(const SSLClientParameters* params); //============================================ //= Functions implemented in SSLClient.h //============================================ diff --git a/src/SSLObj.cpp b/src/SSLObj.cpp new file mode 100644 index 0000000..68682ea --- /dev/null +++ b/src/SSLObj.cpp @@ -0,0 +1,57 @@ +#include "SSLObj.h" + +struct ssl_pem_decode_state { + std::vector* vect; + size_t index = 0; +}; + +static void ssl_pem_decode(void *dest_ctx, const void *src, size_t len) { + ssl_pem_decode_state* ctx = static_cast(dest_ctx); + // copy the recieved bytes into the vector, resizing if needed + if (ctx->vect->size() < len + ctx->index) { + Serial.println("Overflow!"); + return; + } + for (size_t i = 0; i < len; i++) (*(ctx->vect))[i + ctx->index] = static_cast(src)[i]; + // update index + ctx->index += len; +} + +const std::vector SSLObj::make_vector_pem(const char* data, const size_t len) { + if (data == nullptr || len == 0) return { 0 }; + // initialize the bearssl PEM context + br_pem_decoder_context pctx; + br_pem_decoder_init(&pctx); + // create a temporary vector + std::vector temp(len * 3 / 4 + 5); + // initialize the DER storage context + ssl_pem_decode_state state; + state.vect = &temp; + state.index = 0; + // set the byte reciever + br_pem_decoder_setdest(&pctx, &ssl_pem_decode, &state); + // start decoding! + int br_state = 0; + size_t index = 0; + do { + index += br_pem_decoder_push(&pctx, static_cast(&data[index]), len - index); + br_state = br_pem_decoder_event(&pctx); + } while (br_state != BR_PEM_ERROR && br_state != BR_PEM_END_OBJ); + // error check + if (br_state == BR_PEM_ERROR) { + // set data to error + temp.clear(); + } + // else we're good! + return { temp }; +} + +const std::vector SSLObj::make_vector_der(const char* data, const size_t len) { + if (data == nullptr || len == 0) return { 0 }; + // create a temporary vector + std::vector temp(len); + // copy the elements over + for (size_t i = 0; i < len; i++) temp[i] = data[i]; + // return the new SSLObj + return { temp }; +} \ No newline at end of file diff --git a/src/SSLObj.h b/src/SSLObj.h new file mode 100644 index 0000000..25e8827 --- /dev/null +++ b/src/SSLObj.h @@ -0,0 +1,51 @@ +/* Copyright 2019 OSU OPEnS Lab + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this + * software and associated documentation files (the "Software"), to deal in the Software + * without restriction, including without limitation the rights to use, copy, modify, + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/** + * SSLObj.h + * + * This file contains a utility class to take PEM input and store it as a DER object + * for later use by BearSSL. + */ + +#include +#include "bearssl_pem.h" +#include "Arduino.h" + +#ifndef SSLObj_H_ +#define SSLObj_H_ + +/** + * \brief This namespace works with raw DER byte arrays for use later with TLS mutual auth. + * + * This namespace was created to store some of the values stored in ::SSLClientParameters, + * which allow BearSSL use client certificates when creating a TLS connection. Since + * most certificates are transmitted over the internet in PEM format, a certificate can + * be provided in PEM or DER format, and will be converted internally to DER format for + * later use. A PEM file provided to this class MUST CONTAIN the `----BEGIN ... -----` + * header in order to be parsed correctly. + */ + +namespace SSLObj { + const std::vector make_vector_pem(const char* data, const size_t len); + const std::vector make_vector_der(const char* data, const size_t len); +} + +#endif \ No newline at end of file