mirror of https://github.com/arendst/Tasmota.git
Support for AWS IoT via TLS 1.2
This commit is contained in:
parent
164b3aaf11
commit
bc3d0add4c
Binary file not shown.
|
@ -70,14 +70,16 @@ build_flags = ${esp82xx_defaults.build_flags}
|
|||
platform = espressif8266@~2.2.1
|
||||
build_flags = ${esp82xx_defaults.build_flags}
|
||||
-Wl,-Teagle.flash.1m.ld
|
||||
; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473
|
||||
; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473
|
||||
-O2
|
||||
-DBEARSSL_SSL_BASIC
|
||||
; link with an memory optimized version of lib_bearssl
|
||||
-Llib_bearssl -lbearssl
|
||||
; nonos-sdk 22x
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x
|
||||
; nonos-sdk-pre-v3
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
|
||||
; lwIP 1.4
|
||||
; lwIP 1.4
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; lwIP 2 - Low Memory
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY
|
||||
|
@ -96,13 +98,15 @@ build_flags = ${esp82xx_defaults.build_flags}
|
|||
platform = https://github.com/platformio/platform-espressif8266.git#feature/stage
|
||||
build_flags = ${esp82xx_defaults.build_flags}
|
||||
-Wl,-Teagle.flash.1m.ld
|
||||
; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473
|
||||
; Code optimization see https://github.com/esp8266/Arduino/issues/5790#issuecomment-475672473
|
||||
-O2
|
||||
-DBEARSSL_SSL_BASIC
|
||||
; nonos-sdk 22x
|
||||
-DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x
|
||||
; if you compile for AWS IoT, you should set MQTT keep alive to at least 30s instead of 10s, include is required here for PubSubClient lib
|
||||
-DMQTT_KEEPALIVE=30
|
||||
; nonos-sdk-pre-v3
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
|
||||
; -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3
|
||||
; lwIP 1.4
|
||||
; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH
|
||||
; lwIP 2 - Low Memory
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
StackThunk_light.c - Allow use second stack for BearSSL calls
|
||||
Light version with reduced Stack size due to Tasmota optimizations.
|
||||
|
||||
BearSSL uses a significant amount of stack space, much larger than
|
||||
the default Arduino core stack. These routines handle swapping
|
||||
between a secondary, user-allocated stack on the heap and the real
|
||||
stack.
|
||||
|
||||
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "StackThunk_light.h"
|
||||
#include <ets_sys.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
uint32_t *stack_thunk_light_ptr = NULL;
|
||||
uint32_t *stack_thunk_light_top = NULL;
|
||||
uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */
|
||||
uint32_t stack_thunk_light_refcnt = 0;
|
||||
|
||||
//#define _stackSize (5600/4)
|
||||
#define _stackSize (5100/4) // using a light version of bearssl we can save 1KB
|
||||
#define _stackPaint 0xdeadbeef
|
||||
|
||||
/* Add a reference, and allocate the stack if necessary */
|
||||
void stack_thunk_light_add_ref()
|
||||
{
|
||||
stack_thunk_light_refcnt++;
|
||||
if (stack_thunk_light_refcnt == 1) {
|
||||
stack_thunk_light_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t));
|
||||
stack_thunk_light_top = stack_thunk_light_ptr + _stackSize - 1;
|
||||
stack_thunk_light_save = NULL;
|
||||
stack_thunk_light_repaint();
|
||||
}
|
||||
}
|
||||
|
||||
/* Drop a reference, and free stack if no more in use */
|
||||
void stack_thunk_light_del_ref()
|
||||
{
|
||||
if (stack_thunk_light_refcnt == 0) {
|
||||
/* Error! */
|
||||
return;
|
||||
}
|
||||
stack_thunk_light_refcnt--;
|
||||
if (!stack_thunk_light_refcnt) {
|
||||
free(stack_thunk_light_ptr);
|
||||
stack_thunk_light_ptr = NULL;
|
||||
stack_thunk_light_top = NULL;
|
||||
stack_thunk_light_save = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void stack_thunk_light_repaint()
|
||||
{
|
||||
if (stack_thunk_light_ptr) {
|
||||
for (int i=0; i < _stackSize; i++) {
|
||||
stack_thunk_light_ptr[i] = _stackPaint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Simple accessor functions used by postmortem */
|
||||
uint32_t stack_thunk_light_get_refcnt() {
|
||||
return stack_thunk_light_refcnt;
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_light_get_stack_top() {
|
||||
return (uint32_t)stack_thunk_light_top;
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_light_get_stack_bot() {
|
||||
return (uint32_t)stack_thunk_light_ptr;
|
||||
}
|
||||
|
||||
uint32_t stack_thunk_light_get_cont_sp() {
|
||||
return (uint32_t)stack_thunk_light_save;
|
||||
}
|
||||
|
||||
/* Return the number of bytes ever used since the stack was created */
|
||||
uint32_t stack_thunk_light_get_max_usage()
|
||||
{
|
||||
uint32_t cnt = 0;
|
||||
|
||||
/* No stack == no usage by definition! */
|
||||
if (!stack_thunk_light_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (cnt=0; (cnt < _stackSize) && (stack_thunk_light_ptr[cnt] == _stackPaint); cnt++) {
|
||||
/* Noop, all work done in for() */
|
||||
}
|
||||
return 4 * (_stackSize - cnt);
|
||||
}
|
||||
|
||||
/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */
|
||||
void stack_thunk_light_dump_stack()
|
||||
{
|
||||
uint32_t *pos = stack_thunk_light_top;
|
||||
while (pos < stack_thunk_light_ptr) {
|
||||
if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint))
|
||||
break;
|
||||
pos += 4;
|
||||
}
|
||||
ets_printf(">>>stack>>>\n");
|
||||
while (pos < stack_thunk_light_ptr) {
|
||||
ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]);
|
||||
pos += 4;
|
||||
}
|
||||
ets_printf("<<<stack<<<\n");
|
||||
}
|
||||
|
||||
/* Called when the stack overflow is detected by a thunk. Main memory is corrupted at this point. Do not return. */
|
||||
void stack_thunk_light_fatal_overflow()
|
||||
{
|
||||
ets_printf("FATAL ERROR: BSSL stack overflow\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
StackThunk_light.h - Allow use second stack for BearSSL calls
|
||||
Light version with reduced Stack size due to Tasmota optimizations.
|
||||
|
||||
BearSSL uses a significant amount of stack space, much larger than
|
||||
the default Arduino core stack. These routines handle swapping
|
||||
between a secondary, user-allocated stack on the heap and the real
|
||||
stack.
|
||||
|
||||
Copyright (c) 2017 Earle F. Philhower, III. All rights reserved.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
|
||||
*/
|
||||
|
||||
#ifndef _STACKTHUNK_LIGHT_H
|
||||
#define _STACKTHUNK__LIGHTH
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern void stack_thunk_light_add_ref();
|
||||
extern void stack_thunk_light_del_ref();
|
||||
extern void stack_thunk_light_repaint();
|
||||
|
||||
extern uint32_t stack_thunk_light_get_refcnt();
|
||||
extern uint32_t stack_thunk_light_get_stack_top();
|
||||
extern uint32_t stack_thunk_light_get_stack_bot();
|
||||
extern uint32_t stack_thunk_light_get_cont_sp();
|
||||
extern uint32_t stack_thunk_light_get_max_usage();
|
||||
extern void stack_thunk_light_dump_stack();
|
||||
extern void stack_thunk_light_fatal_overflow();
|
||||
|
||||
// Globals required for thunking operation
|
||||
extern uint32_t *stack_thunk_light_ptr;
|
||||
extern uint32_t *stack_thunk_light_top;
|
||||
extern uint32_t *stack_thunk_light_save;
|
||||
extern uint32_t stack_thunk_light_refcnt;
|
||||
|
||||
// Thunking macro
|
||||
#define make_stack_thunk_light(fcnToThunk) \
|
||||
__asm("\n\
|
||||
.text\n\
|
||||
.literal_position\n\
|
||||
.literal .LC_STACK_VALUE"#fcnToThunk", 0xdeadbeef\n\
|
||||
\n\
|
||||
.text\n\
|
||||
.global thunk_light_"#fcnToThunk"\n\
|
||||
.type thunk_light_"#fcnToThunk", @function\n\
|
||||
.align 4\n\
|
||||
thunk_light_"#fcnToThunk":\n\
|
||||
addi a1, a1, -16 /* Allocate space for saved registers on stack */\n\
|
||||
s32i a0, a1, 12 /* Store A0, trounced by calls */\n\
|
||||
s32i a15, a1, 8 /* Store A15 (our temporary one) */\n\
|
||||
movi a15, stack_thunk_light_save /* Store A1(SP) in temp space */\n\
|
||||
s32i a1, a15, 0\n\
|
||||
movi a15, stack_thunk_light_top /* Load A1(SP) with thunk stack */\n\
|
||||
l32i.n a1, a15, 0\n\
|
||||
call0 "#fcnToThunk" /* Do the call */\n\
|
||||
/* Check the stack canary wasn't overwritten */\n\
|
||||
movi a15, stack_thunk_light_ptr\n\
|
||||
l32i.n a15, a15, 0 /* A15 now has the pointer to stack end*/ \n\
|
||||
l32i.n a15, a15, 0 /* A15 now has contents of last stack entry */\n\
|
||||
l32r a0, .LC_STACK_VALUE"#fcnToThunk" /* A0 now has the check value */\n\
|
||||
beq a0, a15, .L1"#fcnToThunk"\n\
|
||||
call0 stack_thunk_light_fatal_overflow\n\
|
||||
.L1"#fcnToThunk":\n\
|
||||
movi a15, stack_thunk_light_save /* Restore A1(SP) */\n\
|
||||
l32i.n a1, a15, 0\n\
|
||||
l32i.n a15, a1, 8 /* Restore the saved registers */\n\
|
||||
l32i.n a0, a1, 12\n\
|
||||
addi a1, a1, 16 /* Free up stack and return to caller */\n\
|
||||
ret\n\
|
||||
.size thunk_light_"#fcnToThunk", . - thunk_light_"#fcnToThunk"\n");
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,882 @@
|
|||
/*
|
||||
WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
|
||||
- Mostly compatible with Arduino WiFi shield library and standard
|
||||
WiFiClient/ServerSecure (except for certificate handling).
|
||||
|
||||
Copyright (c) 2018 Earle F. Philhower, III
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#define LWIP_INTERNAL
|
||||
|
||||
#include <list>
|
||||
#include <errno.h>
|
||||
#include <algorithm>
|
||||
|
||||
extern "C" {
|
||||
#include "osapi.h"
|
||||
#include "ets_sys.h"
|
||||
}
|
||||
#include "debug.h"
|
||||
#include "ESP8266WiFi.h"
|
||||
#include "WiFiClient.h"
|
||||
#include "WiFiClientSecureLightBearSSL.h"
|
||||
#include "StackThunk_light.h"
|
||||
#include "lwip/opt.h"
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/tcp.h"
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netif.h"
|
||||
#include <include/ClientContext.h>
|
||||
#include "c_types.h"
|
||||
#include "coredecls.h"
|
||||
|
||||
#define SKEY_ON_STACK // copy private key+cert on stack rather than on heap, this works for now because it takes ~800 bytes
|
||||
//#define DEBUG_TLS
|
||||
|
||||
#ifdef DEBUG_TLS
|
||||
#define LOG_HEAP_SIZE(a) _Log_heap_size(a)
|
||||
void _Log_heap_size(const char *msg) {
|
||||
register uint32_t *sp asm("a1");
|
||||
int freestack = 4 * (sp - g_pcont->stack);
|
||||
Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n",
|
||||
msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(),
|
||||
freestack, ESP.getFreeContStack());
|
||||
}
|
||||
#else
|
||||
#define LOG_HEAP_SIZE(a)
|
||||
#endif
|
||||
|
||||
// Stack thunked versions of calls
|
||||
// Initially in BearSSLHelpers.h
|
||||
extern "C" {
|
||||
extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len);
|
||||
extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len);
|
||||
extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len);
|
||||
extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len);
|
||||
extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len);
|
||||
extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len);
|
||||
extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len);
|
||||
extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len);
|
||||
};
|
||||
|
||||
// Second stack thunked helpers
|
||||
make_stack_thunk_light(br_ssl_engine_recvapp_ack);
|
||||
make_stack_thunk_light(br_ssl_engine_recvapp_buf);
|
||||
make_stack_thunk_light(br_ssl_engine_recvrec_ack);
|
||||
make_stack_thunk_light(br_ssl_engine_recvrec_buf);
|
||||
make_stack_thunk_light(br_ssl_engine_sendapp_ack);
|
||||
make_stack_thunk_light(br_ssl_engine_sendapp_buf);
|
||||
make_stack_thunk_light(br_ssl_engine_sendrec_ack);
|
||||
make_stack_thunk_light(br_ssl_engine_sendrec_buf);
|
||||
|
||||
// create new version of Thunk function to store on SYS stack
|
||||
// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep
|
||||
// symetric processing on the stack
|
||||
void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_recvapp_ack(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_recvapp_ack(cc, len);
|
||||
}
|
||||
}
|
||||
unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_recvapp_buf(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_recvapp_buf(cc, len);
|
||||
}
|
||||
}
|
||||
void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_recvrec_ack(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_recvrec_ack(cc, len);
|
||||
}
|
||||
}
|
||||
unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_recvrec_buf(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_recvrec_buf(cc, len);
|
||||
}
|
||||
}
|
||||
void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_sendapp_ack(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_sendapp_ack(cc, len);
|
||||
}
|
||||
}
|
||||
unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_sendapp_buf(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_sendapp_buf(cc, len);
|
||||
}
|
||||
}
|
||||
void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_sendrec_ack(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_sendrec_ack(cc, len);
|
||||
}
|
||||
}
|
||||
unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) {
|
||||
if (stack_thunk_light_get_refcnt()) {
|
||||
return thunk_light_br_ssl_engine_sendrec_buf(cc, len);
|
||||
} else {
|
||||
return br_ssl_engine_sendrec_buf(cc, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Use min_ instead of original thunk_
|
||||
#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack
|
||||
#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf
|
||||
#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack
|
||||
#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf
|
||||
#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack
|
||||
#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf
|
||||
#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack
|
||||
#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf
|
||||
|
||||
//#define DEBUG_ESP_SSL
|
||||
#ifdef DEBUG_ESP_SSL
|
||||
#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__)
|
||||
//#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_BSSL(...)
|
||||
#endif
|
||||
|
||||
namespace BearSSL {
|
||||
|
||||
void WiFiClientSecure_light::_clear() {
|
||||
// TLS handshake may take more than the 5 second default timeout
|
||||
_timeout = 10000; // 10 seconds max, it should never go over 6 seconds
|
||||
|
||||
_sc = nullptr;
|
||||
_ctx_present = false;
|
||||
_eng = nullptr;
|
||||
_iobuf_in = nullptr;
|
||||
_iobuf_out = nullptr;
|
||||
_now = 0; // You can override or ensure time() is correct w/configTime
|
||||
setBufferSizes(1024, 1024); // reasonable minimum
|
||||
_handshake_done = false;
|
||||
_last_error = 0;
|
||||
_recvapp_buf = nullptr;
|
||||
_recvapp_len = 0;
|
||||
_fingerprint_any = true; // by default accept all fingerprints
|
||||
_fingerprint1 = nullptr;
|
||||
_fingerprint2 = nullptr;
|
||||
}
|
||||
|
||||
// Constructor
|
||||
WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() {
|
||||
_clear();
|
||||
LOG_HEAP_SIZE("StackThunk before");
|
||||
//stack_thunk_light_add_ref();
|
||||
LOG_HEAP_SIZE("StackThunk after");
|
||||
// now finish the setup
|
||||
setBufferSizes(recv, xmit); // reasonable minimum
|
||||
allocateBuffers();
|
||||
}
|
||||
|
||||
WiFiClientSecure_light::~WiFiClientSecure_light() {
|
||||
if (_client) {
|
||||
_client->unref();
|
||||
_client = nullptr;
|
||||
}
|
||||
//_cipher_list = nullptr; // std::shared will free if last reference
|
||||
_freeSSL();
|
||||
}
|
||||
|
||||
void WiFiClientSecure_light::allocateBuffers(void) {
|
||||
// We prefer to allocate all buffers at start, rather than lazy allocation and deallocation
|
||||
// in the long run it avoids heap fragmentation and improves stability
|
||||
LOG_HEAP_SIZE("allocateBuffers before");
|
||||
_sc = std::make_shared<br_ssl_client_context>();
|
||||
LOG_HEAP_SIZE("allocateBuffers ClientContext");
|
||||
_iobuf_in = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_in_size], std::default_delete<unsigned char[]>());
|
||||
_iobuf_out = std::shared_ptr<unsigned char>(new unsigned char[_iobuf_out_size], std::default_delete<unsigned char[]>());
|
||||
LOG_HEAP_SIZE("allocateBuffers after");
|
||||
}
|
||||
|
||||
void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
|
||||
unsigned allowed_usages, unsigned cert_issuer_key_type) {
|
||||
_chain_P = cert;
|
||||
_chain.data_len = _chain_P->data_len;
|
||||
_chain.data = nullptr;
|
||||
_sk_ec_P = sk;
|
||||
_sk_ec.curve = _sk_ec_P->curve;
|
||||
_sk_ec.xlen = _sk_ec_P->xlen;
|
||||
_sk_ec.x = nullptr;
|
||||
_allowed_usages = allowed_usages;
|
||||
_cert_issuer_key_type = cert_issuer_key_type;
|
||||
}
|
||||
|
||||
void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) {
|
||||
// Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately)
|
||||
const int MAX_OUT_OVERHEAD = 85;
|
||||
const int MAX_IN_OVERHEAD = 325;
|
||||
|
||||
// The data buffers must be between 512B and 16KB
|
||||
recv = std::max(512, std::min(16384, recv));
|
||||
xmit = std::max(512, std::min(16384, xmit));
|
||||
|
||||
// Add in overhead for SSL protocol
|
||||
recv += MAX_IN_OVERHEAD;
|
||||
xmit += MAX_OUT_OVERHEAD;
|
||||
_iobuf_in_size = recv;
|
||||
_iobuf_out_size = xmit;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) {
|
||||
bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush()
|
||||
_freeSSL();
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) {
|
||||
(void) _run_until(BR_SSL_SENDAPP);
|
||||
return WiFiClient::flush(maxWaitMs);
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) {
|
||||
clearLastError();
|
||||
if (!WiFiClient::connect(ip, port)) {
|
||||
setLastError(ERR_TCP_CONNECT);
|
||||
return 0;
|
||||
}
|
||||
return _connectSSL(nullptr);
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::connect(const char* name, uint16_t port) {
|
||||
IPAddress remote_addr;
|
||||
clearLastError();
|
||||
if (!WiFi.hostByName(name, remote_addr)) {
|
||||
DEBUG_BSSL("connect: Name loopup failure\n");
|
||||
setLastError(ERR_CANT_RESOLVE_IP);
|
||||
return 0;
|
||||
}
|
||||
if (!WiFiClient::connect(remote_addr, port)) {
|
||||
DEBUG_BSSL("connect: Unable to connect TCP socket\n");
|
||||
_last_error = ERR_TCP_CONNECT;
|
||||
return 0;
|
||||
}
|
||||
LOG_HEAP_SIZE("Before calling _connectSSL");
|
||||
return _connectSSL(name);
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::connect(const String& host, uint16_t port) {
|
||||
return connect(host.c_str(), port);
|
||||
}
|
||||
|
||||
void WiFiClientSecure_light::_freeSSL() {
|
||||
_ctx_present = false;
|
||||
_recvapp_buf = nullptr;
|
||||
_recvapp_len = 0;
|
||||
// This connection is toast
|
||||
_handshake_done = false;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure_light::_clientConnected() {
|
||||
return (_client && _client->state() == ESTABLISHED);
|
||||
}
|
||||
|
||||
uint8_t WiFiClientSecure_light::connected() {
|
||||
if (available() || (_clientConnected() && _handshake_done)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) {
|
||||
size_t sent_bytes = 0;
|
||||
|
||||
if (!connected() || !size || !_handshake_done) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
// Ensure we yield if we need multiple fragments to avoid WDT
|
||||
if (sent_bytes) {
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
|
||||
// Get BearSSL to a state where we can send
|
||||
if (_run_until(BR_SSL_SENDAPP) < 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
|
||||
size_t sendapp_len;
|
||||
unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len);
|
||||
int to_send = size > sendapp_len ? sendapp_len : size;
|
||||
if (pmem) {
|
||||
memcpy_P(sendapp_buf, buf, to_send);
|
||||
} else {
|
||||
memcpy(sendapp_buf, buf, to_send);
|
||||
}
|
||||
br_ssl_engine_sendapp_ack(_eng, to_send);
|
||||
br_ssl_engine_flush(_eng, 0);
|
||||
flush();
|
||||
buf += to_send;
|
||||
sent_bytes += to_send;
|
||||
size -= to_send;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while (size);
|
||||
|
||||
LOG_HEAP_SIZE("_write");
|
||||
return sent_bytes;
|
||||
}
|
||||
|
||||
size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) {
|
||||
return _write(buf, size, false);
|
||||
}
|
||||
|
||||
size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) {
|
||||
return _write((const uint8_t *)buf, size, true);
|
||||
}
|
||||
|
||||
// We have to manually read and send individual chunks.
|
||||
size_t WiFiClientSecure_light::write(Stream& stream) {
|
||||
size_t totalSent = 0;
|
||||
size_t countRead;
|
||||
size_t countSent;
|
||||
|
||||
if (!connected() || !_handshake_done) {
|
||||
DEBUG_BSSL("write: Connect/handshake not completed yet\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
do {
|
||||
uint8_t temp[256]; // Temporary chunk size same as ClientContext
|
||||
countSent = 0;
|
||||
countRead = stream.readBytes(temp, sizeof(temp));
|
||||
if (countRead) {
|
||||
countSent = _write((const uint8_t*)temp, countRead, true);
|
||||
totalSent += countSent;
|
||||
}
|
||||
yield(); // Feed the WDT
|
||||
} while ((countSent == countRead) && (countSent > 0));
|
||||
return totalSent;
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::read(uint8_t *buf, size_t size) {
|
||||
if (!ctx_present() || !_handshake_done) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int avail = available();
|
||||
bool conn = connected();
|
||||
if (!avail && conn) {
|
||||
return 0; // We're still connected, but nothing to read
|
||||
}
|
||||
if (!avail && !conn) {
|
||||
DEBUG_BSSL("read: Not connected, none left available\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (avail) {
|
||||
// Take data from the recvapp buffer
|
||||
int to_copy = _recvapp_len < size ? _recvapp_len : size;
|
||||
memcpy(buf, _recvapp_buf, to_copy);
|
||||
br_ssl_engine_recvapp_ack(_eng, to_copy);
|
||||
_recvapp_buf = nullptr;
|
||||
_recvapp_len = 0;
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
if (!conn) {
|
||||
DEBUG_BSSL("read: Not connected\n");
|
||||
return -1;
|
||||
}
|
||||
return 0; // If we're connected, no error but no read.
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::read() {
|
||||
uint8_t c;
|
||||
if (1 == read(&c, 1)) {
|
||||
return c;
|
||||
}
|
||||
DEBUG_BSSL("read: failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::available() {
|
||||
if (_recvapp_buf) {
|
||||
return _recvapp_len; // Anything from last call?
|
||||
}
|
||||
_recvapp_buf = nullptr;
|
||||
_recvapp_len = 0;
|
||||
if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) {
|
||||
return 0;
|
||||
}
|
||||
int st = br_ssl_engine_current_state(_eng);
|
||||
if (st == BR_SSL_CLOSED) {
|
||||
return 0; // Nothing leftover, SSL is closed
|
||||
}
|
||||
if (st & BR_SSL_RECVAPP) {
|
||||
_recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len);
|
||||
return _recvapp_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WiFiClientSecure_light::peek() {
|
||||
if (!ctx_present() || !available()) {
|
||||
DEBUG_BSSL("peek: Not connected, none left available\n");
|
||||
return -1;
|
||||
}
|
||||
if (_recvapp_buf && _recvapp_len) {
|
||||
return _recvapp_buf[0];
|
||||
}
|
||||
DEBUG_BSSL("peek: No data left\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) {
|
||||
size_t to_copy = 0;
|
||||
if (!ctx_present()) {
|
||||
DEBUG_BSSL("peekBytes: Not connected\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
_startMillis = millis();
|
||||
while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) {
|
||||
yield();
|
||||
}
|
||||
|
||||
to_copy = _recvapp_len < length ? _recvapp_len : length;
|
||||
memcpy(buffer, _recvapp_buf, to_copy);
|
||||
return to_copy;
|
||||
}
|
||||
|
||||
/* --- Copied almost verbatim from BEARSSL SSL_IO.C ---
|
||||
Run the engine, until the specified target state is achieved, or
|
||||
an error occurs. The target state is SENDAPP, RECVAPP, or the
|
||||
combination of both (the combination matches either). When a match is
|
||||
achieved, this function returns 0. On error, it returns -1.
|
||||
*/
|
||||
int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) {
|
||||
//LOG_HEAP_SIZE("_run_until 1");
|
||||
if (!ctx_present()) {
|
||||
DEBUG_BSSL("_run_until: Not connected\n");
|
||||
return -1;
|
||||
}
|
||||
for (int no_work = 0; blocking || no_work < 2;) {
|
||||
if (blocking) {
|
||||
// Only for blocking operations can we afford to yield()
|
||||
optimistic_yield(100);
|
||||
}
|
||||
|
||||
int state;
|
||||
state = br_ssl_engine_current_state(_eng);
|
||||
if (state & BR_SSL_CLOSED) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) {
|
||||
return (state & target) ? 0 : -1;
|
||||
}
|
||||
|
||||
/*
|
||||
If there is some record data to send, do it. This takes
|
||||
precedence over everything else.
|
||||
*/
|
||||
if (state & BR_SSL_SENDREC) {
|
||||
unsigned char *buf;
|
||||
size_t len;
|
||||
int wlen;
|
||||
|
||||
buf = br_ssl_engine_sendrec_buf(_eng, &len);
|
||||
wlen = WiFiClient::write(buf, len);
|
||||
if (wlen <= 0) {
|
||||
/*
|
||||
If we received a close_notify and we
|
||||
still send something, then we have our
|
||||
own response close_notify to send, and
|
||||
the peer is allowed by RFC 5246 not to
|
||||
wait for it.
|
||||
*/
|
||||
return -1;
|
||||
}
|
||||
if (wlen > 0) {
|
||||
br_ssl_engine_sendrec_ack(_eng, wlen);
|
||||
}
|
||||
no_work = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
If we reached our target, then we are finished.
|
||||
*/
|
||||
if (state & target) {
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
If some application data must be read, and we did not
|
||||
exit, then this means that we are trying to write data,
|
||||
and that's not possible until the application data is
|
||||
read. This may happen if using a shared in/out buffer,
|
||||
and the underlying protocol is not strictly half-duplex.
|
||||
This is unrecoverable here, so we report an error.
|
||||
*/
|
||||
if (state & BR_SSL_RECVAPP) {
|
||||
DEBUG_BSSL("_run_until: Fatal protocol state\n");
|
||||
return -1;
|
||||
}
|
||||
/*
|
||||
If we reached that point, then either we are trying
|
||||
to read data and there is some, or the engine is stuck
|
||||
until a new record is obtained.
|
||||
*/
|
||||
if (state & BR_SSL_RECVREC) {
|
||||
if (WiFiClient::available()) {
|
||||
unsigned char *buf;
|
||||
size_t len;
|
||||
int rlen;
|
||||
|
||||
buf = br_ssl_engine_recvrec_buf(_eng, &len);
|
||||
rlen = WiFiClient::read(buf, len);
|
||||
if (rlen < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (rlen > 0) {
|
||||
br_ssl_engine_recvrec_ack(_eng, rlen);
|
||||
}
|
||||
no_work = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/*
|
||||
We can reach that point if the target RECVAPP, and
|
||||
the state contains SENDAPP only. This may happen with
|
||||
a shared in/out buffer. In that case, we must flush
|
||||
the buffered data to "make room" for a new incoming
|
||||
record.
|
||||
*/
|
||||
br_ssl_engine_flush(_eng, 0);
|
||||
|
||||
no_work++; // We didn't actually advance here
|
||||
}
|
||||
// We only get here if we ran through the loop without getting anything done
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool WiFiClientSecure_light::_wait_for_handshake() {
|
||||
_handshake_done = false;
|
||||
while (!_handshake_done && _clientConnected()) {
|
||||
int ret = _run_until(BR_SSL_SENDAPP);
|
||||
if (ret < 0) {
|
||||
DEBUG_BSSL("_wait_for_handshake: failed\n");
|
||||
break;
|
||||
}
|
||||
if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) {
|
||||
_handshake_done = true;
|
||||
}
|
||||
optimistic_yield(1000);
|
||||
}
|
||||
return _handshake_done;
|
||||
}
|
||||
|
||||
static uint8_t htoi (unsigned char c)
|
||||
{
|
||||
if (c>='0' && c <='9') return c - '0';
|
||||
else if (c>='A' && c<='F') return 10 + c - 'A';
|
||||
else if (c>='a' && c<='f') return 10 + c - 'a';
|
||||
else return 255;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
// see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c
|
||||
void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) {
|
||||
unsigned char * pin = in;
|
||||
static const char * hex = "0123456789ABCDEF";
|
||||
char * pout = out;
|
||||
for(; pin < in+insz; pout +=3, pin++){
|
||||
pout[0] = hex[(*pin>>4) & 0xF];
|
||||
pout[1] = hex[ *pin & 0xF];
|
||||
pout[2] = ':';
|
||||
if (pout + 3 - out > outsz){
|
||||
/* Better to truncate output string than overflow buffer */
|
||||
/* it would be still better to either return a status */
|
||||
/* or ensure the target buffer is large enough and it never happen */
|
||||
break;
|
||||
}
|
||||
}
|
||||
pout[-1] = 0;
|
||||
}
|
||||
|
||||
|
||||
// BearSSL doesn't define a true insecure decoder, so we make one ourselves
|
||||
// from the simple parser. It generates the issuer and subject hashes and
|
||||
// the SHA1 fingerprint, only one (or none!) of which will be used to
|
||||
// "verify" the certificate.
|
||||
|
||||
// Private x509 decoder state
|
||||
struct br_x509_pubkeyfingerprint_context {
|
||||
const br_x509_class *vtable;
|
||||
bool done_cert; // did we parse the first cert already?
|
||||
bool fingerprint_all;
|
||||
uint8_t *pubkey_recv_fingerprint;
|
||||
const uint8_t *fingerprint1;
|
||||
const uint8_t *fingerprint2;
|
||||
unsigned usages; // pubkey usage
|
||||
br_x509_decoder_context ctx; // defined in BearSSL
|
||||
};
|
||||
|
||||
// Callback on the first byte of any certificate
|
||||
static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) {
|
||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||
// Don't process anything but the first certificate in the chain
|
||||
if (!xc->done_cert) {
|
||||
br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
(void)server_name; // ignore server name
|
||||
}
|
||||
|
||||
// Callback for each certificate present in the chain (but only operates
|
||||
// on the first one by design).
|
||||
static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) {
|
||||
(void) ctx; // do nothing
|
||||
(void) length;
|
||||
}
|
||||
|
||||
// Callback for each byte stream in the chain. Only process first cert.
|
||||
static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) {
|
||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||
// Don't process anything but the first certificate in the chain
|
||||
if (!xc->done_cert) {
|
||||
br_x509_decoder_push(&xc->ctx, (const void*)buf, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Callback on individual cert end.
|
||||
static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) {
|
||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||
xc->done_cert = true; // first cert already processed
|
||||
}
|
||||
|
||||
static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) {
|
||||
br_sha1_init(shactx);
|
||||
br_sha1_update(shactx, "ssh-rsa", 7); // tag
|
||||
br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent
|
||||
br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus
|
||||
}
|
||||
|
||||
// Callback when complete chain has been parsed.
|
||||
// Return 0 on validation success, !0 on validation error
|
||||
static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) {
|
||||
br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx;
|
||||
|
||||
br_sha1_context sha1_context;
|
||||
pubkeyfingerprint_pubkey_fingerprint(&sha1_context, xc->ctx.pkey.key.rsa);
|
||||
br_sha1_out(&sha1_context, xc->pubkey_recv_fingerprint); // copy to fingerprint
|
||||
|
||||
if (!xc->fingerprint_all) {
|
||||
if (0 == memcmp(xc->fingerprint1, xc->pubkey_recv_fingerprint, 20)) {
|
||||
return 0;
|
||||
}
|
||||
if (0 == memcmp(xc->fingerprint2, xc->pubkey_recv_fingerprint, 20)) {
|
||||
return 0;
|
||||
}
|
||||
return 1; // no match, error
|
||||
} else {
|
||||
// Default (no validation at all) or no errors in prior checks = success.
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the public key from the validator (set by x509_minimal)
|
||||
static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) {
|
||||
const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx;
|
||||
|
||||
if (usages != NULL) {
|
||||
*usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure!
|
||||
}
|
||||
return &xc->ctx.pkey;
|
||||
}
|
||||
|
||||
// Set up the x509 insecure data structures for BearSSL core to use.
|
||||
void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx,
|
||||
const uint8_t *fingerprint1, const uint8_t *fingerprint2,
|
||||
uint8_t *recv_fingerprint,
|
||||
bool fingerprint_all) {
|
||||
static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = {
|
||||
sizeof(br_x509_pubkeyfingerprint_context),
|
||||
pubkeyfingerprint_start_chain,
|
||||
pubkeyfingerprint_start_cert,
|
||||
pubkeyfingerprint_append,
|
||||
pubkeyfingerprint_end_cert,
|
||||
pubkeyfingerprint_end_chain,
|
||||
pubkeyfingerprint_get_pkey
|
||||
};
|
||||
|
||||
memset(ctx, 0, sizeof * ctx);
|
||||
ctx->vtable = &br_x509_pubkeyfingerprint_vtable;
|
||||
ctx->done_cert = false;
|
||||
ctx->fingerprint1 = fingerprint1;
|
||||
ctx->fingerprint2 = fingerprint2;
|
||||
ctx->pubkey_recv_fingerprint = recv_fingerprint;
|
||||
ctx->fingerprint_all = fingerprint_all;
|
||||
}
|
||||
|
||||
// We limit to a single cipher to reduce footprint
|
||||
// we reference it, don't put in PROGMEM
|
||||
static const uint16_t suites[] = {
|
||||
BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
|
||||
};
|
||||
|
||||
// Default initializion for our SSL clients
|
||||
static void br_ssl_client_base_init(br_ssl_client_context *cc) {
|
||||
br_ssl_client_zero(cc);
|
||||
// forbid SSL renegociation, as we free the Private Key after handshake
|
||||
br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION);
|
||||
|
||||
br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12);
|
||||
br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0]));
|
||||
br_ssl_client_set_default_rsapub(cc);
|
||||
br_ssl_engine_set_default_rsavrfy(&cc->eng);
|
||||
|
||||
// install hashes
|
||||
br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable);
|
||||
br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf);
|
||||
|
||||
// AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways)
|
||||
br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable);
|
||||
br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable);
|
||||
br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32);
|
||||
|
||||
// we support only P256 EC curve
|
||||
br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15);
|
||||
}
|
||||
}
|
||||
|
||||
// Called by connect() to do the actual SSL setup and handshake.
|
||||
// Returns if the SSL handshake succeeded.
|
||||
bool WiFiClientSecure_light::_connectSSL(const char* hostName) {
|
||||
br_ec_private_key sk_ec;
|
||||
br_x509_certificate chain;
|
||||
#ifdef SKEY_ON_STACK
|
||||
unsigned char chain_data[_chain_P->data_len];
|
||||
unsigned char sk_data[_sk_ec_P->xlen];
|
||||
#endif
|
||||
br_x509_pubkeyfingerprint_context *x509_insecure;
|
||||
|
||||
LOG_HEAP_SIZE("_connectSSL.start");
|
||||
|
||||
stack_thunk_light_add_ref();
|
||||
LOG_HEAP_SIZE("Thunk allocated");
|
||||
DEBUG_BSSL("_connectSSL: start connection\n");
|
||||
_freeSSL();
|
||||
clearLastError();
|
||||
|
||||
_ctx_present = true;
|
||||
_eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr
|
||||
|
||||
br_ssl_client_base_init(_sc.get());
|
||||
|
||||
LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation");
|
||||
// Only failure possible in the installation is OOM
|
||||
x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context));
|
||||
|
||||
br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2,
|
||||
_recv_fingerprint, _fingerprint_any);
|
||||
br_ssl_engine_set_x509(_eng, &x509_insecure->vtable);
|
||||
|
||||
br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size);
|
||||
|
||||
LOG_HEAP_SIZE("_connectSSL after PrivKey allocation");
|
||||
// allocate Private key and client certificate
|
||||
//chain = new X509List(_chain_PEM);
|
||||
//sk = new PrivateKey(_sk_PEM);
|
||||
chain.data_len = _chain_P->data_len;
|
||||
#ifdef SKEY_ON_STACK // allocate on stack
|
||||
chain.data = &chain_data[0];
|
||||
#else // allocate with malloc
|
||||
chain.data = (unsigned char *) malloc(chain.data_len);
|
||||
#endif
|
||||
if (chain.data) memcpy_P(chain.data, _chain_P->data, chain.data_len);
|
||||
|
||||
sk_ec.curve = _sk_ec_P->curve;
|
||||
sk_ec.xlen = _sk_ec_P->xlen;
|
||||
#ifdef SKEY_ON_STACK
|
||||
sk_ec.x = &sk_data[0];
|
||||
#else
|
||||
sk_ec.x = (unsigned char *) malloc(sk_ec.xlen);
|
||||
#endif
|
||||
if (sk_ec.x) memcpy_P(sk_ec.x, _sk_ec_P->x, sk_ec.xlen);
|
||||
LOG_HEAP_SIZE("_connectSSL after PrivKey allocation");
|
||||
|
||||
// check if memory was correctly allocated
|
||||
if ((!stack_thunk_light_get_stack_bot()) || (!x509_insecure) ||
|
||||
(!chain.data) || (!sk_ec.x)) {
|
||||
// memory allocation problem
|
||||
setLastError(ERR_OOM);
|
||||
#ifndef SKEY_ON_STACK
|
||||
free(chain.data);
|
||||
free(sk_ec.x);
|
||||
#endif
|
||||
free(x509_insecure);
|
||||
stack_thunk_light_del_ref();
|
||||
DEBUG_BSSL("_connectSSL: Out of memory\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// limited to P256 curve
|
||||
br_ssl_client_set_single_ec(_sc.get(), &chain, 1,
|
||||
&sk_ec, _allowed_usages,
|
||||
_cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default());
|
||||
|
||||
if (!br_ssl_client_reset(_sc.get(), hostName, 0)) {
|
||||
#ifndef SKEY_ON_STACK
|
||||
free(chain.data);
|
||||
free(sk_ec.x);
|
||||
#endif
|
||||
free(x509_insecure);
|
||||
stack_thunk_light_del_ref();
|
||||
_freeSSL();
|
||||
DEBUG_BSSL("_connectSSL: Can't reset client\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ret = _wait_for_handshake();
|
||||
#ifdef DEBUG_ESP_SSL
|
||||
if (!ret) {
|
||||
DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError());
|
||||
} else {
|
||||
DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus());
|
||||
}
|
||||
#endif
|
||||
LOG_HEAP_SIZE("_connectSSL.end");
|
||||
stack_thunk_light_del_ref();
|
||||
//stack_thunk_light_repaint();
|
||||
LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk");
|
||||
#ifndef SKEY_ON_STACK
|
||||
free(chain.data);
|
||||
free(sk_ec.x);
|
||||
#endif
|
||||
free(x509_insecure);
|
||||
LOG_HEAP_SIZE("_connectSSL after release of Priv Key");
|
||||
return ret;
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries
|
||||
- Mostly compatible with Arduino WiFi shield library and standard
|
||||
WiFiClient/ServerSecure (except for certificate handling).
|
||||
|
||||
Copyright (c) 2018 Earle F. Philhower, III
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
|
||||
#ifndef wificlientlightbearssl_h
|
||||
#define wificlientlightbearssl_h
|
||||
#include <vector>
|
||||
#include "WiFiClient.h"
|
||||
#include <bearssl/bearssl.h>
|
||||
#include "BearSSLHelpers.h"
|
||||
#include "CertStoreBearSSL.h"
|
||||
|
||||
namespace BearSSL {
|
||||
|
||||
class WiFiClientSecure_light : public WiFiClient {
|
||||
public:
|
||||
WiFiClientSecure_light(int recv, int xmit);
|
||||
~WiFiClientSecure_light() override;
|
||||
|
||||
void allocateBuffers(void);
|
||||
|
||||
int connect(IPAddress ip, uint16_t port) override;
|
||||
int connect(const String& host, uint16_t port) override;
|
||||
int connect(const char* name, uint16_t port) override;
|
||||
|
||||
uint8_t connected() override;
|
||||
size_t write(const uint8_t *buf, size_t size) override;
|
||||
size_t write_P(PGM_P buf, size_t size) override;
|
||||
size_t write(const char *buf) {
|
||||
return write((const uint8_t*)buf, strlen(buf));
|
||||
}
|
||||
size_t write_P(const char *buf) {
|
||||
return write_P((PGM_P)buf, strlen_P(buf));
|
||||
}
|
||||
size_t write(Stream& stream); // Note this is not virtual
|
||||
int read(uint8_t *buf, size_t size) override;
|
||||
int available() override;
|
||||
int read() override;
|
||||
int peek() override;
|
||||
size_t peekBytes(uint8_t *buffer, size_t length) override;
|
||||
bool flush(unsigned int maxWaitMs);
|
||||
bool stop(unsigned int maxWaitMs);
|
||||
void flush() override { (void)flush(0); }
|
||||
void stop() override { (void)stop(0); }
|
||||
|
||||
// Only check SHA1 fingerprint of public key
|
||||
void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2,
|
||||
bool f_any = false) {
|
||||
_fingerprint1 = f1;
|
||||
_fingerprint2 = f2;
|
||||
_fingerprint_any = f_any;
|
||||
}
|
||||
const uint8_t * getRecvPubKeyFingerprint(void) {
|
||||
return _recv_fingerprint;
|
||||
}
|
||||
|
||||
void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk,
|
||||
unsigned allowed_usages, unsigned cert_issuer_key_type);
|
||||
|
||||
// Sets the requested buffer size for transmit and receive
|
||||
void setBufferSizes(int recv, int xmit);
|
||||
|
||||
// Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection)
|
||||
int getMFLNStatus() {
|
||||
return connected() && br_ssl_engine_get_mfln_negotiated(_eng);
|
||||
}
|
||||
|
||||
int32_t getLastError(void) {
|
||||
if (_last_error) {
|
||||
return _last_error;
|
||||
} else {
|
||||
return br_ssl_engine_last_error(_eng);
|
||||
}
|
||||
}
|
||||
inline void setLastError(int32_t err) {
|
||||
_last_error = err;
|
||||
}
|
||||
inline void clearLastError(void) {
|
||||
_last_error = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
void _clear();
|
||||
bool _ctx_present;
|
||||
std::shared_ptr<br_ssl_client_context> _sc;
|
||||
inline bool ctx_present() {
|
||||
return _ctx_present;
|
||||
}
|
||||
br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts
|
||||
std::shared_ptr<unsigned char> _iobuf_in;
|
||||
std::shared_ptr<unsigned char> _iobuf_out;
|
||||
time_t _now;
|
||||
int _iobuf_in_size;
|
||||
int _iobuf_out_size;
|
||||
bool _handshake_done;
|
||||
uint64_t _last_error;
|
||||
|
||||
bool _fingerprint_any; // accept all fingerprints
|
||||
const uint8_t *_fingerprint1; // fingerprint1 to be checked against
|
||||
const uint8_t *_fingerprint2; // fingerprint2 to be checked against
|
||||
uint8_t _recv_fingerprint[20]; // fingerprint received
|
||||
|
||||
unsigned char *_recvapp_buf;
|
||||
size_t _recvapp_len;
|
||||
|
||||
bool _clientConnected(); // Is the underlying socket alive?
|
||||
bool _connectSSL(const char *hostName); // Do initial SSL handshake
|
||||
void _freeSSL();
|
||||
int _run_until(unsigned target, bool blocking = true);
|
||||
size_t _write(const uint8_t *buf, size_t size, bool pmem);
|
||||
bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting
|
||||
|
||||
// Optional client certificate
|
||||
br_x509_certificate _chain; // local RAM copy
|
||||
const br_x509_certificate *_chain_P; // PROGMEM certificate
|
||||
br_ec_private_key _sk_ec;
|
||||
const br_ec_private_key *_sk_ec_P; // PROGMEM private key
|
||||
unsigned _allowed_usages;
|
||||
unsigned _cert_issuer_key_type;
|
||||
|
||||
};
|
||||
|
||||
#define ERR_OOM -1000
|
||||
#define ERR_CANT_RESOLVE_IP -1001
|
||||
#define ERR_TCP_CONNECT -1002
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -88,7 +88,7 @@
|
|||
#define MQTT_USE 1 // [SetOption3] Select default MQTT use (0 = Off, 1 = On)
|
||||
|
||||
#define MQTT_HOST "" // [MqttHost]
|
||||
#define MQTT_FINGERPRINT1 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint1]
|
||||
#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00" // [MqttFingerprint1]
|
||||
#define MQTT_FINGERPRINT2 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07" // [MqttFingerprint2]
|
||||
#define MQTT_PORT 1883 // [MqttPort] MQTT port (10123 on CloudMQTT)
|
||||
#define MQTT_USER "DVES_USER" // [MqttUser] MQTT user
|
||||
|
@ -267,6 +267,11 @@
|
|||
//#define USE_MQTT_TLS // Use TLS for MQTT connection (+53k code, +15k mem)
|
||||
// #define USE_MQTT_TLS_CA_CERT // Use LetsEncrypt Certificate from sonoff_letsencrypt.h - Not supported with core 2.3.0
|
||||
|
||||
// -- MQTT - Special version for AWS IoT
|
||||
//#define USE_MQTT_AWS_IOT // Enable MQTT for AWS IoT - requires a private key (+56.7k code, +6.0k mem and +6.6k additional during connection handshake)
|
||||
// you need to generate a private key + certificate per device
|
||||
// and update 'sonoff/sonoff_aws_iot.cpp'
|
||||
|
||||
// -- KNX IP Protocol -----------------------------
|
||||
//#define USE_KNX // Enable KNX IP Protocol Support (+9.4k code, +3k7 mem)
|
||||
#define USE_KNX_WEB_MENU // Enable KNX WEB MENU (+8.3k code, +144 mem)
|
||||
|
@ -473,4 +478,18 @@
|
|||
#error "Select either USE_MQTT_TLS or USE_WEBSERVER as there is just not enough memory to play with"
|
||||
#endif
|
||||
|
||||
#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT)
|
||||
#error "Select either USE_MQTT_TLS or USE_MQTT_AWS_IOT, they are not compatible"
|
||||
#endif
|
||||
|
||||
#if defined(USE_DISCOVERY) && defined(USE_MQTT_AWS_IOT)
|
||||
#error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT"
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(USE_MQTT_TLS) || defined(USE_MQTT_AWS_IOT)
|
||||
#undef WEB_LOG_SIZE
|
||||
#define WEB_LOG_SIZE (2000) // reduce log buffer size when using TLS
|
||||
#endif
|
||||
|
||||
#endif // _MY_USER_CONFIG_H_
|
||||
|
|
|
@ -120,11 +120,7 @@ const uint16_t MIN_MESSZ = 893; // Min number of characters in MQTT
|
|||
|
||||
const uint8_t SENSOR_MAX_MISS = 5; // Max number of missed sensor reads before deciding it's offline
|
||||
|
||||
#ifdef USE_MQTT_TLS
|
||||
const uint16_t WEB_LOG_SIZE = 2000; // Max number of characters in weblog
|
||||
#else
|
||||
const uint16_t WEB_LOG_SIZE = 4000; // Max number of characters in weblog
|
||||
#endif
|
||||
#define WEB_LOG_SIZE (4000) // Max number of characters in weblog
|
||||
|
||||
const uint8_t MAX_BACKLOG = 30; // Max number of commands in backlog
|
||||
const uint32_t MIN_BACKLOG_DELAY = 2; // Minimal backlog delay in 0.1 seconds
|
||||
|
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
sonoff_aws_iot.cpp - TLS AWS IoT for Sonoff-Tasmota - Private key
|
||||
|
||||
Copyright (C) 2019 Theo Arends
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <bearssl/bearssl.h>
|
||||
#include <pgmspace.h>
|
||||
|
||||
// nasty hack to force PROGMEM
|
||||
#define static static PROGMEM
|
||||
|
||||
namespace aws_iot_privkey {
|
||||
/*********************************************************************************************\
|
||||
* Private key for AWS IoT
|
||||
*
|
||||
* Create the private key, generate the CSR and sign it with AWS IoT Console.
|
||||
*
|
||||
* Then generate C version of Private Key and Certificate, cut and paste below
|
||||
*
|
||||
* Downloaded from https://www.identrust.com/support/downloads
|
||||
\*********************************************************************************************/
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Export Private Key as a C structure
|
||||
*
|
||||
* $ bearssl skey -C <private_key.PEM>
|
||||
\*********************************************************************************************/
|
||||
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
|
||||
static const unsigned char EC_X[] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
|
||||
};
|
||||
|
||||
static const br_ec_private_key EC = {
|
||||
23,
|
||||
(unsigned char *)EC_X, sizeof EC_X
|
||||
};
|
||||
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Export Private Key as a C structure
|
||||
*
|
||||
* $ bearssl chain <certificate.PEM>
|
||||
\*********************************************************************************************/
|
||||
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY BELOW --------------- */
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
|
||||
static const unsigned char CERT0[] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB,
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77
|
||||
};
|
||||
|
||||
static const br_x509_certificate CHAIN[] = {
|
||||
{ (unsigned char *)CERT0, sizeof CERT0 }
|
||||
};
|
||||
|
||||
#define CHAIN_LEN 1
|
||||
|
||||
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
|
||||
/* --------------- CUT AND PASTE PRIVATE KEY ABOVE --------------- */
|
||||
|
||||
const br_ec_private_key *AWS_IoT_Private_Key = &EC;
|
||||
const br_x509_certificate *AWS_IoT_Client_Certificate = &CHAIN[0];
|
||||
|
||||
}
|
|
@ -419,7 +419,8 @@ void KNX_CB_Action(message_t const &msg, void *arg);
|
|||
#endif
|
||||
|
||||
#ifndef MQTT_FINGERPRINT1
|
||||
#define MQTT_FINGERPRINT1 "A5 02 FF 13 99 9F 8B 39 8E F1 83 4F 11 23 65 0B 32 36 FC 07"
|
||||
// Set an all-zeros default fingerprint to activate auto-learning on first connection (AWS IoT)
|
||||
#define MQTT_FINGERPRINT1 "00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00"
|
||||
#endif
|
||||
|
||||
#ifndef MQTT_FINGERPRINT2
|
||||
|
@ -434,7 +435,8 @@ void KNX_CB_Action(message_t const &msg, void *arg);
|
|||
#define MQTT_MAX_PACKET_SIZE 1000 // Bytes
|
||||
#endif
|
||||
#ifndef MQTT_KEEPALIVE
|
||||
#define MQTT_KEEPALIVE 15 // Seconds
|
||||
//#define MQTT_KEEPALIVE 15 // Seconds
|
||||
#define MQTT_KEEPALIVE 30 // Changed to 30s which is min for AWS IoT, hoping it does not break anthing
|
||||
#endif
|
||||
#ifndef MQTT_TIMEOUT
|
||||
#define MQTT_TIMEOUT 10000 // milli seconds
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
#include "sonoff_letsencrypt.h" // LetsEncrypt certificate
|
||||
#endif
|
||||
WiFiClientSecure EspClient; // Wifi Secure Client
|
||||
#elif defined(USE_MQTT_AWS_IOT)
|
||||
#include "WiFiClientSecureLightBearSSL.h"
|
||||
BearSSL::WiFiClientSecure_light *awsClient;
|
||||
#else
|
||||
WiFiClient EspClient; // Wifi Client
|
||||
#endif
|
||||
|
@ -46,6 +49,32 @@ uint8_t mqtt_initial_connection_state = 2; // MQTT connection messages state
|
|||
bool mqtt_connected = false; // MQTT virtual connection status
|
||||
bool mqtt_allowed = false; // MQTT enabled and parameters valid
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
|
||||
namespace aws_iot_privkey {
|
||||
// this is where the Private Key and Certificate are stored
|
||||
extern const br_ec_private_key *AWS_IoT_Private_Key;
|
||||
extern const br_x509_certificate *AWS_IoT_Client_Certificate;
|
||||
}
|
||||
|
||||
// A typical AWS IoT endpoint is 50 characters long, it does not fit
|
||||
// in MqttHost field (32 chars). We need to concatenate both MqttUser and MqttHost
|
||||
char AWS_endpoint[65]; // aWS IOT endpoint, concatenation of user and host
|
||||
|
||||
// check whether the fingerprint is filled with a single value
|
||||
// Filled with 0x00 = accept any fingerprint and learn it for next time
|
||||
// Filled with 0xFF = accept any fingerpring forever
|
||||
bool is_fingerprint_mono_value(uint8_t finger[20], uint8_t value) {
|
||||
for (uint32_t i = 0; i<20; i++) {
|
||||
if (finger[i] != value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif // USE_MQTT_AWS_IOT
|
||||
|
||||
/*********************************************************************************************\
|
||||
* MQTT driver specific code need to provide the following functions:
|
||||
*
|
||||
|
@ -62,7 +91,35 @@ bool mqtt_allowed = false; // MQTT enabled and parameters valid
|
|||
#error "MQTT_MAX_PACKET_SIZE is too small in libraries/PubSubClient/src/PubSubClient.h, increase it to at least 1000"
|
||||
#endif
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
PubSubClient MqttClient;
|
||||
#else
|
||||
PubSubClient MqttClient(EspClient);
|
||||
#endif
|
||||
|
||||
|
||||
void MqttInit(void) {
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
AWS_endpoint[0] = 0;
|
||||
uint8_t len_user = strlen(Settings.mqtt_user);
|
||||
uint8_t len_host = strlen(Settings.mqtt_host);
|
||||
if (len_user > 0) {
|
||||
strcpy(AWS_endpoint, Settings.mqtt_user);
|
||||
if (('.' != AWS_endpoint[len_user-1]) && ('.' != Settings.mqtt_host[0])) {
|
||||
AWS_endpoint[len_user++] = '.';
|
||||
}
|
||||
strcpy(&AWS_endpoint[len_user], Settings.mqtt_host);
|
||||
}
|
||||
|
||||
awsClient = new BearSSL::WiFiClientSecure_light(1024,1024);
|
||||
awsClient->setClientECCert(aws_iot_privkey::AWS_IoT_Client_Certificate,
|
||||
aws_iot_privkey::AWS_IoT_Private_Key,
|
||||
0xFFFF /* all usages, don't care */, 0);
|
||||
|
||||
MqttClient.setClient(*awsClient);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool MqttIsConnected(void)
|
||||
{
|
||||
|
@ -179,6 +236,10 @@ void MqttPublishDirect(const char* topic, bool retained)
|
|||
void MqttPublish(const char* topic, bool retained)
|
||||
{
|
||||
char *me;
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained is not supported by AWS IoT, using retained = false."));
|
||||
retained = false; // AWS IoT does not support retained, it will disconnect if received
|
||||
#endif
|
||||
|
||||
if (!strcmp(Settings.mqtt_prefix[0],Settings.mqtt_prefix[1])) {
|
||||
me = strstr(topic,Settings.mqtt_prefix[0]);
|
||||
|
@ -282,7 +343,11 @@ void MqttDisconnected(int state)
|
|||
mqtt_connected = false;
|
||||
mqtt_retry_counter = Settings.mqtt_retry;
|
||||
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), AWS_endpoint, Settings.mqtt_port, state, mqtt_retry_counter);
|
||||
#else
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT D_CONNECT_FAILED_TO " %s:%d, rc %d. " D_RETRY_IN " %d " D_UNIT_SECOND), Settings.mqtt_host, Settings.mqtt_port, state, mqtt_retry_counter);
|
||||
#endif
|
||||
rules_flag.mqtt_disconnected = 1;
|
||||
}
|
||||
|
||||
|
@ -443,6 +508,8 @@ void MqttReconnect(void)
|
|||
|
||||
#ifdef USE_MQTT_TLS
|
||||
EspClient = WiFiClientSecure(); // Wifi Secure Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
|
||||
#elif defined(USE_MQTT_AWS_IOT)
|
||||
awsClient->stop();
|
||||
#else
|
||||
EspClient = WiFiClient(); // Wifi Client reconnect issue 4497 (https://github.com/esp8266/Arduino/issues/4497)
|
||||
#endif
|
||||
|
@ -456,7 +523,11 @@ void MqttReconnect(void)
|
|||
}
|
||||
|
||||
MqttClient.setCallback(MqttDataHandler);
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
MqttClient.setServer(AWS_endpoint, Settings.mqtt_port);
|
||||
#else
|
||||
MqttClient.setServer(Settings.mqtt_host, Settings.mqtt_port);
|
||||
#endif
|
||||
/*
|
||||
// Skip MQTT host DNS lookup if not needed
|
||||
uint32_t current_hash = GetHash(Settings.mqtt_host, strlen(Settings.mqtt_host));
|
||||
|
@ -466,9 +537,50 @@ void MqttReconnect(void)
|
|||
}
|
||||
MqttClient.setServer(mqtt_host_addr, Settings.mqtt_port);
|
||||
*/
|
||||
uint32_t time = millis();
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
bool allow_all_fingerprints = false;
|
||||
bool learn_fingerprint1 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0x00);
|
||||
bool learn_fingerprint2 = is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0x00);
|
||||
allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[0], 0xff);
|
||||
allow_all_fingerprints |= is_fingerprint_mono_value(Settings.mqtt_fingerprint[1], 0xff);
|
||||
allow_all_fingerprints |= learn_fingerprint1;
|
||||
allow_all_fingerprints |= learn_fingerprint2;
|
||||
awsClient->setPubKeyFingerprint(Settings.mqtt_fingerprint[0], Settings.mqtt_fingerprint[1], allow_all_fingerprints);
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT endpoint: %s"), AWS_endpoint);
|
||||
if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, nullptr, 0, false, nullptr)) {
|
||||
#else
|
||||
if (MqttClient.connect(mqtt_client, mqtt_user, mqtt_pwd, stopic, 1, true, mqtt_data)) {
|
||||
#endif
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT connected in %d ms"), millis() - time);
|
||||
if (learn_fingerprint1 || learn_fingerprint2) {
|
||||
// we potentially need to learn the fingerprint just seen
|
||||
bool fingerprint_matched = false;
|
||||
const uint8_t *recv_fingerprint = awsClient->getRecvPubKeyFingerprint();
|
||||
if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[0], 20)) {
|
||||
fingerprint_matched = true;
|
||||
}
|
||||
if (0 == memcmp(recv_fingerprint, Settings.mqtt_fingerprint[1], 20)) {
|
||||
fingerprint_matched = true;
|
||||
}
|
||||
if (!fingerprint_matched) {
|
||||
// we had no match, so we need to change all fingerprints ready to learn
|
||||
if (learn_fingerprint1) {
|
||||
memcpy(Settings.mqtt_fingerprint[0], recv_fingerprint, 20);
|
||||
}
|
||||
if (learn_fingerprint2) {
|
||||
memcpy(Settings.mqtt_fingerprint[1], recv_fingerprint, 20);
|
||||
}
|
||||
restart_flag = 2; // save and restart
|
||||
}
|
||||
}
|
||||
#endif
|
||||
MqttConnected();
|
||||
} else {
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_MQTT "AWS IoT connection error: %d"), awsClient->getLastError());
|
||||
#endif
|
||||
MqttDisconnected(MqttClient.state()); // status codes are documented here http://pubsubclient.knolleary.net/api.html#state
|
||||
}
|
||||
}
|
||||
|
@ -549,7 +661,7 @@ bool MqttCommand(void)
|
|||
}
|
||||
Response_P(S_JSON_COMMAND_INDEX_SVALUE, command, index, GetStateText(index -1));
|
||||
}
|
||||
#ifdef USE_MQTT_TLS
|
||||
#if defined(USE_MQTT_TLS) || defined(USE_MQTT_AWS_IOT)
|
||||
else if ((CMND_MQTTFINGERPRINT == command_code) && (index > 0) && (index <= 2)) {
|
||||
char fingerprint[60];
|
||||
if ((data_len > 0) && (data_len < sizeof(fingerprint))) {
|
||||
|
@ -827,6 +939,11 @@ bool Xdrv02(uint8_t function)
|
|||
|
||||
if (Settings.flag.mqtt_enabled) {
|
||||
switch (function) {
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
case FUNC_PRE_INIT:
|
||||
MqttInit();
|
||||
break;
|
||||
#endif
|
||||
case FUNC_EVERY_50_MSECOND: // https://github.com/knolleary/pubsubclient/issues/556
|
||||
MqttClient.loop();
|
||||
break;
|
||||
|
|
Loading…
Reference in New Issue