Rework client certificates, add support for decoding a PEM object

This commit is contained in:
Noah Laptop 2019-08-12 17:27:14 -07:00
parent 43d517c9df
commit 2b287f5179
5 changed files with 132 additions and 37 deletions

View file

@ -22,6 +22,7 @@
#include "SSLClientImpl.h"
#include "SSLSession.h"
#include "SSLClientParameters.h"
#include "SSLObj.h"
#ifndef SSLClient_H_
#define SSLClient_H_
@ -82,25 +83,6 @@ public:
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
*

View file

@ -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,
&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);
}
bool SSLClientImpl::m_soft_connected(const char* func_name) {
// check if the socket is still open and such
if (getWriteError()) {

View file

@ -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
//============================================

57
src/SSLObj.cpp Normal file
View file

@ -0,0 +1,57 @@
#include "SSLObj.h"
struct ssl_pem_decode_state {
std::vector<unsigned char>* 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<ssl_pem_decode_state*>(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<const unsigned char*>(src)[i];
// update index
ctx->index += len;
}
const std::vector<unsigned char> 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<unsigned char> 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<const void*>(&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<unsigned char> SSLObj::make_vector_der(const char* data, const size_t len) {
if (data == nullptr || len == 0) return { 0 };
// create a temporary vector
std::vector<unsigned char> temp(len);
// copy the elements over
for (size_t i = 0; i < len; i++) temp[i] = data[i];
// return the new SSLObj
return { temp };
}

51
src/SSLObj.h Normal file
View file

@ -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 <vector>
#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<unsigned char> make_vector_pem(const char* data, const size_t len);
const std::vector<unsigned char> make_vector_der(const char* data, const size_t len);
}
#endif