From 04a2252c207999cbe895187cb6f0dc5b4bfe53d5 Mon Sep 17 00:00:00 2001
From: Fabrice Fontaine <fabrice.fontaine@orange.com>
Date: Wed, 3 Feb 2021 15:22:58 +0100
Subject: [PATCH] Add setEccChain function

This will allow the user to specify a client certificate chain instead
of a single client certificate.

This is useful for example for AWS just-in-time registration or
Just-in-time provisioning (JITP):
https://aws.amazon.com/blogs/iot/just-in-time-registration-of-device-certificates-on-aws-iot
https://docs.aws.amazon.com/iot/latest/developerguide/jit-provisioning.html

Signed-off-by: Fabrice Fontaine <fabrice.fontaine@orange.com>
---
 src/BearSSLClient.cpp | 59 ++++++++++++++++++++++++++++---------------
 src/BearSSLClient.h   |  8 +++++-
 2 files changed, 45 insertions(+), 22 deletions(-)

diff --git a/src/BearSSLClient.cpp b/src/BearSSLClient.cpp
index 30037a1..e8e8d3c 100644
--- a/src/BearSSLClient.cpp
+++ b/src/BearSSLClient.cpp
@@ -44,7 +44,8 @@ BearSSLClient::BearSSLClient(Client* client, const br_x509_trust_anchor* myTAs,
   _client(client),
   _TAs(myTAs),
   _numTAs(myNumTAs),
-  _noSNI(false)
+  _noSNI(false),
+  _ecChainLen(0)
 {
   _ecVrfy = br_ecdsa_vrfy_asn1_get_default();
   _ecSign = br_ecdsa_sign_asn1_get_default();
@@ -53,16 +54,18 @@ BearSSLClient::BearSSLClient(Client* client, const br_x509_trust_anchor* myTAs,
   _ecKey.x = NULL;
   _ecKey.xlen = 0;
 
-  _ecCert.data = NULL;
-  _ecCert.data_len = 0;
+  for (size_t i = 0; i < BEAR_SSL_CLIENT_CHAIN_SIZE; i++) {
+    _ecCert[i].data = NULL;
+    _ecCert[i].data_len = 0;
+  }
   _ecCertDynamic = false;
 }
 
 BearSSLClient::~BearSSLClient()
 {
-  if (_ecCertDynamic && _ecCert.data) {
-    free(_ecCert.data);
-    _ecCert.data = NULL;
+  if (_ecCertDynamic && _ecCert[0].data) {
+    free(_ecCert[0].data);
+    _ecCert[0].data = NULL;
   }
 }
 
@@ -207,7 +210,19 @@ void BearSSLClient::setEccSign(br_ecdsa_sign sign)
 
 void BearSSLClient::setEccCert(br_x509_certificate cert)
 {
-  _ecCert = cert;
+  _ecCert[0] = cert;
+  _ecChainLen = 1;
+}
+
+void BearSSLClient::setEccChain(br_x509_certificate* chain, size_t chainLen)
+{
+  if (chainLen > BEAR_SSL_CLIENT_CHAIN_SIZE)
+    return;
+
+  for (size_t i = 0; i < chainLen; i++) {
+    _ecCert[i] = chain[i];
+  }
+  _ecChainLen = chainLen;
 }
 
 void BearSSLClient::setEccSlot(int ecc508KeySlot, const byte cert[], int certLength)
@@ -217,8 +232,9 @@ void BearSSLClient::setEccSlot(int ecc508KeySlot, const byte cert[], int certLen
   _ecKey.x = (unsigned char*)ecc508KeySlot;
   _ecKey.xlen = 32;
 
-  _ecCert.data = (unsigned char*)cert;
-  _ecCert.data_len = certLength;
+  _ecCert[0].data = (unsigned char*)cert;
+  _ecCert[0].data_len = certLength;
+  _ecChainLen = 1;
   _ecCertDynamic = false;
 
   _ecVrfy = eccX08_vrfy_asn1;
@@ -233,14 +249,15 @@ void BearSSLClient::setEccSlot(int ecc508KeySlot, const char cert[])
   size_t certLen = strlen(cert);
 
   // free old data
-  if (_ecCertDynamic && _ecCert.data) {
-    free(_ecCert.data);
-    _ecCert.data = NULL;
+  if (_ecCertDynamic && _ecCert[0].data) {
+    free(_ecCert[0].data);
+    _ecCert[0].data = NULL;
   }
 
   // assume the decoded cert is 3/4 the length of the input
-  _ecCert.data = (unsigned char*)malloc(((certLen * 3) + 3) / 4);
-  _ecCert.data_len = 0;
+  _ecCert[0].data = (unsigned char*)malloc(((certLen * 3) + 3) / 4);
+  _ecCert[0].data_len = 0;
+  _ecChainLen = 1;
 
   br_pem_decoder_init(&pemDecoder);
 
@@ -256,9 +273,9 @@ void BearSSLClient::setEccSlot(int ecc508KeySlot, const char cert[])
         break;
 
       case BR_PEM_END_OBJ:
-        if (_ecCert.data_len) {
+        if (_ecCert[0].data_len) {
           // done
-          setEccSlot(ecc508KeySlot, _ecCert.data, _ecCert.data_len);
+          setEccSlot(ecc508KeySlot, _ecCert[0].data, _ecCert[0].data_len);
           _ecCertDynamic = true;
           return;
         }
@@ -266,7 +283,7 @@ void BearSSLClient::setEccSlot(int ecc508KeySlot, const char cert[])
 
       case BR_PEM_ERROR:
         // failure
-        free(_ecCert.data);
+        free(_ecCert[0].data);
         setEccSlot(ecc508KeySlot, NULL, 0);
         return;
     }
@@ -301,8 +318,8 @@ int BearSSLClient::connectSSL(const char* host)
   br_x509_minimal_set_ecdsa(&_xc, br_ssl_engine_get_ec(&_sc.eng), br_ssl_engine_get_ecdsa(&_sc.eng));
 
   // enable client auth
-  if (_ecCert.data_len) {
-    br_ssl_client_set_single_ec(&_sc, &_ecCert, 1, &_ecKey, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, BR_KEYTYPE_EC, br_ec_get_default(), _ecSign);
+  if (_ecCert[0].data_len) {
+    br_ssl_client_set_single_ec(&_sc, _ecCert, _ecChainLen, &_ecKey, BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN, BR_KEYTYPE_EC, br_ec_get_default(), _ecSign);
   }
 
   // set the hostname used for SNI
@@ -401,6 +418,6 @@ void BearSSLClient::clientAppendCert(void *ctx, const void *data, size_t len)
 {
   BearSSLClient* c = (BearSSLClient*)ctx;
 
-  memcpy(&c->_ecCert.data[c->_ecCert.data_len], data, len);
-  c->_ecCert.data_len += len;
+  memcpy(&c->_ecCert[0].data[c->_ecCert[0].data_len], data, len);
+  c->_ecCert[0].data_len += len;
 }
diff --git a/src/BearSSLClient.h b/src/BearSSLClient.h
index 125f41e..b45e30a 100644
--- a/src/BearSSLClient.h
+++ b/src/BearSSLClient.h
@@ -33,6 +33,10 @@
 #define BEAR_SSL_CLIENT_IBUF_SIZE 8192 + 85 + 325 - BEAR_SSL_CLIENT_OBUF_SIZE
 #endif
 
+#ifndef BEAR_SSL_CLIENT_CHAIN_SIZE
+#define BEAR_SSL_CLIENT_CHAIN_SIZE 3
+#endif
+
 #include <Arduino.h>
 #include <Client.h>
 
@@ -75,6 +79,7 @@ class BearSSLClient : public Client {
   void setEccSign(br_ecdsa_sign sign);
 
   void setEccCert(br_x509_certificate cert);
+  void setEccChain(br_x509_certificate* chain, size_t chainLen);
 
   void setEccSlot(int ecc508KeySlot, const byte cert[], int certLength);
   void setEccSlot(int ecc508KeySlot, const char cert[]);
@@ -98,7 +103,8 @@ class BearSSLClient : public Client {
   br_ecdsa_sign _ecSign;
 
   br_ec_private_key _ecKey;
-  br_x509_certificate _ecCert;
+  br_x509_certificate _ecCert[BEAR_SSL_CLIENT_CHAIN_SIZE];
+  int _ecChainLen;
   bool _ecCertDynamic;
 
   br_ssl_client_context _sc;