diff --git a/src/SSLClient.h b/src/SSLClient.h index cd9291e..0c078e1 100644 --- a/src/SSLClient.h +++ b/src/SSLClient.h @@ -22,6 +22,7 @@ #include "Client.h" #include "SSLClientImpl.h" #include "SSLSession.h" +#include "SSLClientParameters.h" #ifndef SSLClient_H_ #define SSLClient_H_ @@ -67,17 +68,41 @@ public: * @param trust_anchors_num The number of objects in the trust_anchors array. * @param analog_pin An analog pin to pull random bytes from, used in seeding the RNG. * @param debug The level of debug logging (use the ::DebugLevel enum). + * @param mutual_auth_params Configuration to use for mutual authentication, nullptr to disable mutual auth. (see ::SSLClientParameters). */ - 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_WARN) + 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_WARN) : SSLClientImpl(trust_anchors, trust_anchors_num, analog_pin, debug) , m_client(client) - , m_sessions{SSLSession()} + , 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); } + /** + * 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 //======================================== diff --git a/src/SSLClientImpl.cpp b/src/SSLClientImpl.cpp index d46faa6..d0d4255 100644 --- a/src/SSLClientImpl.cpp +++ b/src/SSLClientImpl.cpp @@ -52,9 +52,7 @@ static int freeMemory() { /* 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) - : m_trust_anchors(trust_anchors) - , m_trust_anchors_num(trust_anchors_num) - , m_analog_pin(analog_pin) + : m_analog_pin(analog_pin) , m_session_index(0) , m_debug(debug) , m_is_connected(false) @@ -63,7 +61,7 @@ SSLClientImpl::SSLClientImpl(const br_x509_trust_anchor *trust_anchors, // zero the iobuf just in case it's still garbage memset(m_iobuf, 0, sizeof m_iobuf); // 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_client_init_TLS12_only(&m_sslctx, &m_x509ctx, trust_anchors, trust_anchors_num); // comment the above line and uncomment the line below if you're having trouble connecting over SSL // br_ssl_client_init_full(&m_sslctx, &m_x509ctx, m_trust_anchors, m_trust_anchors_num); // check if the buffer size is half or full duplex @@ -71,6 +69,23 @@ 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__; diff --git a/src/SSLClientImpl.h b/src/SSLClientImpl.h index 896bc7e..f955194 100644 --- a/src/SSLClientImpl.h +++ b/src/SSLClientImpl.h @@ -22,6 +22,7 @@ #include "Arduino.h" #include "Client.h" #include "SSLSession.h" +#include "SSLClientParameters.h" #ifndef SSLClientImpl_H_ #define SSLClientImpl_H_ @@ -72,7 +73,13 @@ class SSLClientImpl : public Client { public: /** @see SSLClient::SSLClient */ explicit SSLClientImpl(const br_x509_trust_anchor *trust_anchors, - const size_t trust_anchors_num, const int analog_pin, const DebugLevel debug); + const size_t trust_anchors_num, const int analog_pin, + const DebugLevel debug); + + /** @see SSLClient::SSLClient */ + explicit 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); //============================================ //= Functions implemented in SSLClientImpl.cpp @@ -171,10 +178,6 @@ private: //= Data Members //============================================ - // store pointers to the trust anchors - // should not be computed at runtime - const br_x509_trust_anchor *m_trust_anchors; - const size_t m_trust_anchors_num; // store the pin to fetch an RNG see from const int m_analog_pin; // store an index of where a new session can be placed if we don't have any corresponding sessions diff --git a/src/SSLClientParameters.h b/src/SSLClientParameters.h new file mode 100644 index 0000000..10d478d --- /dev/null +++ b/src/SSLClientParameters.h @@ -0,0 +1,66 @@ +/* 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. + */ + +/** + * SSLClientParameters.h + * + * This file contains a simple utility class to store parameters about an SSL Session + * for reuse later. + */ + +#include "bearssl.h" + +#ifndef SSLClientParameters_H_ +#define SSLClientParameters_H_ + +/** + * This file contains a simple struct to package together all the data required to + * use client certificate authentication with SSLClient. + */ + +/** + * \brief This struct stores data required for SSLClient to use mutual authentication. + * + * TLS mutual authentication is a process in which both the server and client + * perform cryptographic operations to verify the authenticity of eachother, for more + * information check out this article: https://medium.com/sitewards/the-magic-of-tls-x509-and-mutual-authentication-explained-b2162dec4401 . + * If this struct is provided to SSLClient::SSLClient, SSLClient will automatically + * send a client certificate if one is requested by the server. This will happen for all + * SSLClient connections, and may cause issues for websites that do not need mutual authentication--- + * as a result, please only turn on mutual authentication if you are sure it is neccesary. + * + * At the moment SSLClient only supports mutual authentication using ECC client certificates. + */ + +struct SSLClientParameters { + /** + * \brief Pointer to the client certificate chain. + * + * Must be availible in memory AT ALL TIMES, should not be a local object. + * Certificates must be ordered from Client->Intermediate->...->Root. + */ + const br_x509_certificate* client_cert_chain; + /** The number of certificates in SSLClientParameters::client_cert_chain */ + const size_t chain_len; + /** The private key corresponding to the first certificate in SSLClientParameters::client_cert_chain */ + const br_ec_private_key ec_key; +}; + +#endif \ No newline at end of file diff --git a/tools/pycert_bearssl/pycert_bearssl.py b/tools/pycert_bearssl/pycert_bearssl.py index 327be0f..392838e 100644 --- a/tools/pycert_bearssl/pycert_bearssl.py +++ b/tools/pycert_bearssl/pycert_bearssl.py @@ -100,8 +100,10 @@ def download(port, cert_var, cert_length_var, output, use_store, keep_dupes, dom help='the location of the .pem file containing a list of trusted root certificates (default: use certifi.where())') @click.option('--keep-dupes', '-d', is_flag=True, default=False, help='write all certs including any duplicates (default: remove duplicates)') +@click.option('--no-verify', '-n', is_flag=True, default=False, + help='Do not attempt to match a root certificate to the provided PEM files') @click.argument('cert', type=click.File('r'), nargs=-1) -def convert(cert_var, cert_length_var, output, use_store, keep_dupes, cert): +def convert(cert_var, cert_length_var, output, use_store, keep_dupes, no_verify, cert): """Convert PEM certificates into a C header that can be imported into a sketch. Specify each certificate to encode as a separate argument (each must be in PEM format) and they will be merged into a single file. @@ -132,12 +134,15 @@ def convert(cert_var, cert_length_var, output, use_store, keep_dupes, cert): cert_objs.append(cert_parsed) # find a root certificate for each root_certs = [] - for i, c in enumerate(cert_objs): - cn_hash = c.get_issuer().hash() - if cn_hash not in cert_dict: - click.echo('Could not find a root certificate for {0}'.format(cert[i].name)) - else: - root_certs.append(cert_dict[cn_hash]) + if no_verify: + root_certs = cert_objs + else: + for i, c in enumerate(cert_objs): + cn_hash = c.get_issuer().hash() + if cn_hash not in cert_dict: + click.echo('Could not find a root certificate for {0}'.format(cert[i].name)) + else: + root_certs.append(cert_dict[cn_hash]) # Combine PEMs and write output header. cert_util.x509_to_header(root_certs, cert_var, cert_length_var, output, keep_dupes)