Merge pull request #11 from brentru/certificate-work

Extending WifiSSLClient
This commit is contained in:
Limor "Ladyada" Fried
2019-10-08 23:06:47 -04:00
committed by GitHub
5 changed files with 246 additions and 59 deletions

View File

@@ -1,5 +1,15 @@
PROJECT_NAME := nina-fw PROJECT_NAME := nina-fw
# Passthrough Board Port
M4_PORT := /dev/cu.usbmodem14121301
UPLOAD_BAUD = 115200
# ESP32 USB Serial Port
ESP_PORT := /dev/cu.usbserial-AH03B302
# Directories and Files
BOOT_VOLUME := /Volumes/FEATHERBOOT/.
CIRCUITPYTHON_UF2 := circuitpython.uf2
EXTRA_COMPONENT_DIRS := $(PWD)/arduino EXTRA_COMPONENT_DIRS := $(PWD)/arduino
ifeq ($(RELEASE),1) ifeq ($(RELEASE),1)
@@ -14,7 +24,27 @@ endif
include $(IDF_PATH)/make/project.mk include $(IDF_PATH)/make/project.mk
load-passthrough:
cp passthrough.UF2 $(BOOT_VOLUME)
load-nina:
esptool.py --port $(M4_PORT) --before no_reset --baud $(UPLOAD_BAUD) write_flash 0 NINA_W102-1.3.1.bin
load-circuitpython:
cp $(CIRCUITPYTHON_UF2) $(BOOT_VOLUME)
serial:
miniterm.py $(ESP_PORT) $(UPLOAD_BAUD)
firmware: all firmware: all
python combine.py python combine.py
.PHONY: firmware .PHONY: firmware
.PHONY: load-passthrough
.PHONY: load-nina
.PHONY: load-circuitpython
.PHONY: serial

View File

@@ -17,6 +17,8 @@
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include "Arduino.h"
#include <lwip/err.h>
#include <lwip/netdb.h> #include <lwip/netdb.h>
#include <lwip/sockets.h> #include <lwip/sockets.h>
#include "esp_partition.h" #include "esp_partition.h"
@@ -52,128 +54,203 @@ WiFiSSLClient::WiFiSSLClient() :
int WiFiSSLClient::connect(const char* host, uint16_t port) int WiFiSSLClient::connect(const char* host, uint16_t port)
{ {
ets_printf("** Connect(host/port) Called\n");
return connect(host, port, _cert, _private_key);
}
int WiFiSSLClient::connect(const char* host, uint16_t port, const char* client_cert, const char* client_key)
{
ets_printf("** Connect(host/port/cert/key) called\n");
int ret, flags;
synchronized { synchronized {
_netContext.fd = -1; _netContext.fd = -1;
_connected = false; _connected = false;
ets_printf("Free internal heap before TLS %u", heap_caps_get_free_size(MALLOC_CAP_8BIT));
ets_printf("*** connect init\n"); ets_printf("*** connect init\n");
// SSL Client Initialization
mbedtls_ssl_init(&_sslContext); mbedtls_ssl_init(&_sslContext);
mbedtls_ctr_drbg_init(&_ctrDrbgContext); mbedtls_ctr_drbg_init(&_ctrDrbgContext);
mbedtls_ssl_config_init(&_sslConfig); mbedtls_ssl_config_init(&_sslConfig);
mbedtls_entropy_init(&_entropyContext);
mbedtls_x509_crt_init(&_caCrt);
mbedtls_net_init(&_netContext); mbedtls_net_init(&_netContext);
ets_printf("*** connect inited\n"); ets_printf("*** connect inited\n");
ets_printf("*** connect drbgseed\n"); ets_printf("*** connect drbgseed\n");
mbedtls_entropy_init(&_entropyContext);
if (mbedtls_ctr_drbg_seed(&_ctrDrbgContext, mbedtls_entropy_func, &_entropyContext, NULL, 0) != 0) { // Seeds and sets up CTR_DRBG for future reseeds, pers is device personalization (esp)
ret = mbedtls_ctr_drbg_seed(&_ctrDrbgContext, mbedtls_entropy_func,
&_entropyContext, (const unsigned char *) pers, strlen(pers));
if (ret < 0) {
ets_printf("Unable to set up mbedtls_entropy.\n");
stop(); stop();
return 0; return 0;
} }
ets_printf("*** connect ssl hostname\n"); ets_printf("*** connect ssl hostname\n");
/* Hostname set here should match CN in server certificate */ /* Hostname set here should match CN in server certificate */
if(mbedtls_ssl_set_hostname(&_sslContext, host) != 0) if(mbedtls_ssl_set_hostname(&_sslContext, host) != 0) {
{
stop(); stop();
return 0; return 0;
} }
ets_printf("*** connect ssl config\n"); ets_printf("*** connect ssl config\n");
ret= mbedtls_ssl_config_defaults(&_sslConfig, MBEDTLS_SSL_IS_CLIENT,
if (mbedtls_ssl_config_defaults(&_sslConfig, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT) != 0) { MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
stop(); stop();
ets_printf("Error Setting up SSL Config: %d\n", ret);
return 0; return 0;
} }
ets_printf("*** connect authmode\n"); ets_printf("*** connect authmode\n");
// we're always using the root CA cert from partition, so MBEDTLS_SSL_VERIFY_REQUIRED
ets_printf("*** Loading CA Cert...\n");
mbedtls_x509_crt_init(&_caCrt);
mbedtls_ssl_conf_authmode(&_sslConfig, MBEDTLS_SSL_VERIFY_REQUIRED); mbedtls_ssl_conf_authmode(&_sslConfig, MBEDTLS_SSL_VERIFY_REQUIRED);
// setting up CA certificates from partition
spi_flash_mmap_handle_t handle; spi_flash_mmap_handle_t handle;
const unsigned char* certs_data = {}; const unsigned char* certs_data = {};
ets_printf("*** connect part findfirst\n"); ets_printf("*** connect part findfirst\n");
const esp_partition_t* part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "certs"); const esp_partition_t* part = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "certs");
if (part == NULL) if (part == NULL) {
{ stop();
return 0; return 0;
} }
ets_printf("*** connect part mmap\n"); ets_printf("*** connect part mmap\n");
int ret = esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, (const void**)&certs_data, &handle); int ret = esp_partition_mmap(part, 0, part->size, SPI_FLASH_MMAP_DATA, (const void**)&certs_data, &handle);
if (ret != ESP_OK) if (ret != ESP_OK) {
{ ets_printf("*** Error partition mmap %d\n", ret);
return 0;
}
ets_printf("*** connect crt parse\n");
ret = mbedtls_x509_crt_parse(&_caCrt, certs_data, strlen((char*)certs_data) + 1);
if (ret < 0) {
stop(); stop();
return 0; return 0;
} }
ets_printf("Length of certs_data: %d", strlen((char*)certs_data)+1);
ets_printf("*** connect crt parse\n");
ret = mbedtls_x509_crt_parse(&_caCrt, certs_data, strlen((char*)certs_data) + 1);
ets_printf("*** connect conf ca chain\n"); ets_printf("*** connect conf ca chain\n");
mbedtls_ssl_conf_ca_chain(&_sslConfig, &_caCrt, NULL); mbedtls_ssl_conf_ca_chain(&_sslConfig, &_caCrt, NULL);
if (ret < 0) {
ets_printf("*** Error parsing CA chain.\n");
stop();
return 0;
}
ets_printf("*** check for client_cert and client_key\n");
if (client_cert != NULL && client_key != NULL) {
mbedtls_x509_crt_init(&_clientCrt);
mbedtls_pk_init(&_clientKey);
ets_printf("*** Loading client certificate.\n");
// note: +1 added for line ending
ret = mbedtls_x509_crt_parse(&_clientCrt, (const unsigned char *)client_cert, strlen(client_cert) + 1);
if (ret != 0) {
ets_printf("ERROR: Client cert not parsed properly(%d)\n", ret);
stop();
return 0;
}
ets_printf("*** Loading private key.\n");
ret = mbedtls_pk_parse_key(&_clientKey, (const unsigned char *)client_key, strlen(client_key)+1,
NULL, 0);
if (ret != 0) {
ets_printf("ERROR: Private key not parsed properly:(%d)\n", ret);
stop();
return 0;
}
// set own certificate chain and key
ret = mbedtls_ssl_conf_own_cert(&_sslConfig, &_clientCrt, &_clientKey);
if (ret != 0) {
if (ret == -0x7f00) {
ets_printf("ERROR: Memory allocation failed, MBEDTLS_ERR_SSL_ALLOC_FAILED");
}
ets_printf("Private key not parsed properly(%d)\n", ret);
stop();
return 0;
}
}
else {
ets_printf("Client certificate and key not provided.\n");
}
ets_printf("*** connect conf RNG\n"); ets_printf("*** connect conf RNG\n");
mbedtls_ssl_conf_rng(&_sslConfig, mbedtls_ctr_drbg_random, &_ctrDrbgContext); mbedtls_ssl_conf_rng(&_sslConfig, mbedtls_ctr_drbg_random, &_ctrDrbgContext);
ets_printf("*** connect ssl setup\n"); ets_printf("*** connect ssl setup\n");
if ((ret = mbedtls_ssl_setup(&_sslContext, &_sslConfig)) != 0) {
if (mbedtls_ssl_setup(&_sslContext, &_sslConfig) != 0) { if (ret == -0x7f00){
ets_printf("%s", &_clientCrt);
ets_printf("Memory allocation failed (MBEDTLS_ERR_SSL_ALLOC_FAILED)\n");
ets_printf("Free internal heap: %u\n", heap_caps_get_free_size(MALLOC_CAP_8BIT));
}
ets_printf("Unable to connect ssl setup %d\n", ret);
stop(); stop();
return 0; return 0;
} }
char portStr[6]; char portStr[6];
itoa(port, portStr, 10); itoa(port, portStr, 10);
ets_printf("*** connect netconnect\n"); ets_printf("*** connect netconnect\n");
if (mbedtls_net_connect(&_netContext, host, portStr, MBEDTLS_NET_PROTO_TCP) != 0) { if (mbedtls_net_connect(&_netContext, host, portStr, MBEDTLS_NET_PROTO_TCP) != 0) {
stop(); stop();
return 0; return 0;
} }
ets_printf("*** connect set bio\n"); ets_printf("*** connect set bio\n");
mbedtls_ssl_set_bio(&_sslContext, &_netContext, mbedtls_net_send, mbedtls_net_recv, NULL); mbedtls_ssl_set_bio(&_sslContext, &_netContext, mbedtls_net_send, mbedtls_net_recv, NULL);
int result = -1; ets_printf("*** start SSL/TLS handshake...\n");
unsigned long start_handshake = millis();
do { // ref: https://tls.mbed.org/api/ssl_8h.html#a4a37e497cd08c896870a42b1b618186e
ets_printf("*** connect ssl handshake\n"); while ((ret = mbedtls_ssl_handshake(&_sslContext)) !=0) {
result = mbedtls_ssl_handshake(&_sslContext); if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
} while (result == MBEDTLS_ERR_SSL_WANT_READ || result == MBEDTLS_ERR_SSL_WANT_WRITE); ets_printf("Error performing SSL handshake");
}
if (result != 0) { if((millis() - start_handshake) > _handshake_timeout){
uint8_t module_id = (result >> 12) & 0x7; ets_printf("SSL Handshake Timeout\n");
uint8_t module_dep = (result >> 7) & 0x1F; stop();
uint8_t lowlevel = result & 0x7F; return -1;
ets_printf("*** ssl fail! result %x\t module id: %x module dependant: %x lowlevel: %x\n", result, module_id, module_dep, lowlevel); }
vTaskDelay(10 / portTICK_PERIOD_MS);
char str[100];
mbedtls_strerror(result, str, 100);
ets_printf("strerror: %s\n", str);
stop();
return 0;
} }
if (client_cert != NULL && client_key != NULL)
{
ets_printf("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&_sslContext), mbedtls_ssl_get_ciphersuite(&_sslContext));
}
ets_printf("Verifying peer X.509 certificate\n");
char buf[512];
if ((flags = mbedtls_ssl_get_verify_result(&_sslContext)) != 0) {
bzero(buf, sizeof(buf));
mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags);
ets_printf("Failed to verify peer certificate! verification info: %s\n", buf);
stop(); // invalid certificate, stop
return -1;
} else {
ets_printf("Certificate chain verified.\n");
}
ets_printf("*** ssl set nonblock\n"); ets_printf("*** ssl set nonblock\n");
mbedtls_net_set_nonblock(&_netContext); mbedtls_net_set_nonblock(&_netContext);
_connected = true;
ets_printf("Free internal heap before cleanup: %u\n", heap_caps_get_free_size(MALLOC_CAP_8BIT));
// free the heap
if (certs_data != NULL) {
mbedtls_x509_crt_free(&_caCrt);
}
if (client_cert != NULL) {
mbedtls_x509_crt_free(&_clientCrt);
}
if (client_key !=NULL) {
mbedtls_pk_free(&_clientKey);
}
ets_printf("Free internal heap after cleanup: %u\n", heap_caps_get_free_size(MALLOC_CAP_8BIT));
_connected = true;
return 1; return 1;
} }
} }
@@ -264,6 +341,26 @@ int WiFiSSLClient::peek()
return _peek; return _peek;
} }
void WiFiSSLClient::setCertificate(const char *client_ca)
{
ets_printf("\n*** Setting client certificate...\n");
_cert = client_ca;
ets_printf("%s", _cert);
ets_printf("\n*** Set client certificate\n");
}
void WiFiSSLClient::setPrivateKey(const char *private_key)
{
ets_printf("Setting client private key...\n");
_private_key = private_key;
ets_printf("Set client private key\n");
}
void WiFiSSLClient::setHandshakeTimeout(unsigned long timeout)
{
_handshake_timeout = timeout * 1000;
}
void WiFiSSLClient::flush() void WiFiSSLClient::flush()
{ {
} }
@@ -272,10 +369,12 @@ void WiFiSSLClient::stop()
{ {
synchronized { synchronized {
if (_netContext.fd > 0) { if (_netContext.fd > 0) {
mbedtls_ssl_session_reset(&_sslContext); mbedtls_ssl_session_reset(&_sslContext);
mbedtls_net_free(&_netContext); mbedtls_net_free(&_netContext);
mbedtls_x509_crt_free(&_caCrt); mbedtls_x509_crt_free(&_caCrt);
mbedtls_x509_crt_free(&_clientCrt);
mbedtls_pk_free(&_clientKey);
mbedtls_entropy_free(&_entropyContext); mbedtls_entropy_free(&_entropyContext);
mbedtls_ssl_config_free(&_sslConfig); mbedtls_ssl_config_free(&_sslConfig);
mbedtls_ctr_drbg_free(&_ctrDrbgContext); mbedtls_ctr_drbg_free(&_ctrDrbgContext);

View File

@@ -22,13 +22,14 @@
#include <mbedtls/net.h> #include <mbedtls/net.h>
#include <mbedtls/ssl.h> #include <mbedtls/ssl.h>
#include <mbedtls/platform.h>
#include <mbedtls/entropy.h> #include <mbedtls/entropy.h>
#include <mbedtls/ctr_drbg.h> #include <mbedtls/ctr_drbg.h>
#include <mbedtls/error.h> #include <mbedtls/error.h>
#include <mbedtls/debug.h>
#include <Arduino.h> #include <Arduino.h>
// #include <Client.h>
// #include <IPAddress.h>
class WiFiSSLClient /*: public Client*/ { class WiFiSSLClient /*: public Client*/ {
@@ -39,6 +40,7 @@ public:
virtual int connect(/*IPAddress*/uint32_t ip, uint16_t port); virtual int connect(/*IPAddress*/uint32_t ip, uint16_t port);
virtual int connect(const char* host, uint16_t port); virtual int connect(const char* host, uint16_t port);
virtual int connect(const char* host, uint16_t port, const char* client_cert, const char* client_key);
virtual size_t write(uint8_t); virtual size_t write(uint8_t);
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();
@@ -49,14 +51,20 @@ public:
virtual void stop(); virtual void stop();
virtual uint8_t connected(); virtual uint8_t connected();
virtual operator bool(); virtual operator bool();
virtual void setCertificate(const char *client_ca);
virtual void setPrivateKey (const char *private_key);
virtual void setHandshakeTimeout(unsigned long timeout);
// using Print::write; // using Print::write;
virtual /*IPAddress*/uint32_t remoteIP(); virtual /*IPAddress*/uint32_t remoteIP();
virtual uint16_t remotePort(); virtual uint16_t remotePort();
const char *pers = "esp32-tls";
private: private:
static const char* ROOT_CAs; static const char* ROOT_CAs;
const char *_cert; // user-provided certificate
const char *_private_key; // user-provided private
mbedtls_entropy_context _entropyContext; mbedtls_entropy_context _entropyContext;
mbedtls_ctr_drbg_context _ctrDrbgContext; mbedtls_ctr_drbg_context _ctrDrbgContext;
@@ -64,8 +72,11 @@ private:
mbedtls_ssl_config _sslConfig; mbedtls_ssl_config _sslConfig;
mbedtls_net_context _netContext; mbedtls_net_context _netContext;
mbedtls_x509_crt _caCrt; mbedtls_x509_crt _caCrt;
mbedtls_x509_crt _clientCrt;
mbedtls_pk_context _clientKey;
bool _connected; bool _connected;
int _peek; int _peek;
unsigned long _handshake_timeout = 120000;
SemaphoreHandle_t _mbedMutex; SemaphoreHandle_t _mbedMutex;
}; };

View File

@@ -31,7 +31,7 @@ for i in range(0, len(certsData)):
# zero terminate the pem file # zero terminate the pem file
outputData[0x10000 + len(certsData)] = 0 outputData[0x10000 + len(certsData)] = 0
outputFilename = "NINA_W102-1.3.1.bin" outputFilename = "NINA_W102-1.4.0.bin"
if (len(sys.argv) > 1): if (len(sys.argv) > 1):
outputFilename = sys.argv[1] outputFilename = sys.argv[1]

View File

@@ -26,7 +26,17 @@
#include "CommandHandler.h" #include "CommandHandler.h"
const char FIRMWARE_VERSION[6] = "1.3.1"; #include "Arduino.h"
const char FIRMWARE_VERSION[6] = "1.4.0";
// Optional, user-defined X.509 certificate
char CERT_BUF[1300];
bool setCert = 0;
// Optional, user-defined RSA private key
char PK_BUFF[1700];
bool setPSK = 0;
/*IPAddress*/uint32_t resolvedHostname; /*IPAddress*/uint32_t resolvedHostname;
@@ -589,10 +599,17 @@ int startClientTcp(const uint8_t command[], uint8_t response[])
} }
} else if (type == 0x02) { } else if (type == 0x02) {
int result; int result;
if (host[0] != '\0') { if (host[0] != '\0') {
if (setCert && setPSK) {
tlsClients[socket].setCertificate(CERT_BUF);
tlsClients[socket].setPrivateKey(PK_BUFF);
}
result = tlsClients[socket].connect(host, port); result = tlsClients[socket].connect(host, port);
} else { } else {
if (setCert && setPSK) {
tlsClients[socket].setCertificate(CERT_BUF);
tlsClients[socket].setPrivateKey(PK_BUFF);
}
result = tlsClients[socket].connect(ip, port); result = tlsClients[socket].connect(ip, port);
} }
@@ -1046,6 +1063,36 @@ int wpa2EntEnable(const uint8_t command[], uint8_t response[]) {
return 6; return 6;
} }
int setClientCert(const uint8_t command[], uint8_t response[]){
ets_printf("*** Called setClientCert\n");
memset(CERT_BUF, 0x00, sizeof(CERT_BUF));
memcpy(CERT_BUF, &command[4], sizeof(CERT_BUF));
response[2] = 1; // number of parameters
response[3] = 1; // parameter 1 length
response[4] = 1;
setCert = 1;
return 6;
}
int setCertKey(const uint8_t command[], uint8_t response[]){
ets_printf("*** Called setCertKey\n");
memset(PK_BUFF, 0x00, sizeof(PK_BUFF));
memcpy(PK_BUFF, &command[4], sizeof(PK_BUFF));
response[2] = 1; // number of parameters
response[3] = 1; // parameter 1 length
response[4] = 1;
setPSK = 1;
return 6;
}
typedef int (*CommandHandlerType)(const uint8_t command[], uint8_t response[]); typedef int (*CommandHandlerType)(const uint8_t command[], uint8_t response[]);
const CommandHandlerType commandHandlers[] = { const CommandHandlerType commandHandlers[] = {
@@ -1062,7 +1109,7 @@ const CommandHandlerType commandHandlers[] = {
disconnect, NULL, getIdxRSSI, getIdxEnct, reqHostByName, getHostByName, startScanNetworks, getFwVersion, NULL, sendUDPdata, getRemoteData, getTime, getIdxBSSID, getIdxChannel, ping, getSocket, disconnect, NULL, getIdxRSSI, getIdxEnct, reqHostByName, getHostByName, startScanNetworks, getFwVersion, NULL, sendUDPdata, getRemoteData, getTime, getIdxBSSID, getIdxChannel, ping, getSocket,
// 0x40 -> 0x4f // 0x40 -> 0x4f
NULL, NULL, NULL, NULL, sendDataTcp, getDataBufTcp, insertDataBuf, NULL, NULL, NULL, wpa2EntSetIdentity, wpa2EntSetUsername, wpa2EntSetPassword, wpa2EntSetCACert, wpa2EntSetCertKey, wpa2EntEnable, setClientCert, setCertKey, NULL, NULL, sendDataTcp, getDataBufTcp, insertDataBuf, NULL, NULL, NULL, wpa2EntSetIdentity, wpa2EntSetUsername, wpa2EntSetPassword, wpa2EntSetCACert, wpa2EntSetCertKey, wpa2EntEnable,
// 0x50 -> 0x5f // 0x50 -> 0x5f
setPinMode, setDigitalWrite, setAnalogWrite, setPinMode, setDigitalWrite, setAnalogWrite,