mirror of https://github.com/arendst/Tasmota.git
Merge pull request #5923 from s-hadinger/aws_iot2
Added support for AWS IoT
This commit is contained in:
commit
c14bfb3364
|
@ -0,0 +1,21 @@
|
|||
# Trimmed down version of BearSSL
|
||||
|
||||
Standard BearSSL lib is able to handle RSA keys up to 4096 bits and EC keys up to 521 bits. As we are limiting the use to AWS IoT, we can save hundreds of bytes of memory by limiting to 2048 bit RSA keys and 256 bits EC keys.
|
||||
|
||||
This is just the normal [Arduino version of BearSSL](https://github.com/earlephilhower/bearssl-esp8266)
|
||||
|
||||
There are only two changes in `src/inner.h`:
|
||||
|
||||
* Line 59
|
||||
|
||||
```#define BR_MAX_RSA_SIZE 2048```
|
||||
|
||||
* Line 85
|
||||
|
||||
```#define BR_MAX_EC_SIZE 256```
|
||||
|
||||
Then compile with
|
||||
|
||||
```make CONF=esp8266```
|
||||
|
||||
Finally copy `libbearssl.a` to this directory.
|
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
|
|
@ -8,6 +8,7 @@
|
|||
* Fix PZem startup issue (#5875)
|
||||
* Add command SetOption39 1..255 to control CSE7766 (Pow R2) or HLW8032 (Blitzwolf SHP5) handling of power loads below 6W. Default setting is 128 (#5756)
|
||||
* Add Toggle functionality to button double press when more devices are detected
|
||||
* Add support for AWS IoT with TLS 1.2. Full doc here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT
|
||||
*
|
||||
* 6.5.0.13 20190527
|
||||
* Add command SetOption38 6..255 to set IRReceive protocol detection sensitivity mimizing UNKNOWN protocols (#5853)
|
||||
|
|
|
@ -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,12 @@
|
|||
//#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'
|
||||
// Full documentation here: https://github.com/arendst/Sonoff-Tasmota/wiki/AWS-IoT
|
||||
|
||||
// -- 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 +479,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_
|
||||
|
|
|
@ -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];
|
||||
|
||||
}
|
|
@ -425,7 +425,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
|
||||
|
|
|
@ -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,12 @@ void MqttPublishDirect(const char* topic, bool retained)
|
|||
void MqttPublish(const char* topic, bool retained)
|
||||
{
|
||||
char *me;
|
||||
#ifdef USE_MQTT_AWS_IOT
|
||||
if (retained) {
|
||||
AddLog_P(LOG_LEVEL_INFO, S_LOG_MQTT, PSTR("Retained are 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 +345,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 +510,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 +525,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 +539,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 +663,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 +941,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