espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.01k stars 7.3k forks source link

Intermittant crash using WifiSecure and x509 certificates on POST #800

Closed steeeeeeved closed 6 years ago

steeeeeeved commented 6 years ago

Hardware:

Board: Wemos ESP32 Dev Board Core Installation/update date: 30/10/2017 IDE name: Arduino IDE Flash Frequency: 80Mhz Upload Speed: 115200

Description:

Intermittent problems connecting to AWS IOT via HTTPS calls using WifiSecure and TLS Certificates. When attempting to connect tot he AWS IOT endpoint with x509 certificates (which have been redacted in sample code), it fails about 80% of the time.

Yesterday I updated the version of the https://github.com/espressif/arduino-esp32 to latest master, from a version several months old (I dont have an exact date unfortunately). This code was working successfully previously.

Resetting the board will occasionally allow the HTTP POST to complete, where there will be anything from 1 to 5 successful posts, before the crash.

(Apologies if i have missed something easy!)

Sketch:


//Change the code below by your sketch
#include <WiFi.h>
#include <WiFiClient.h>
#include <WiFiGeneric.h>
#include <WiFiMulti.h>
#include <WiFiServer.h>
#include <WiFiSTA.h>
#include <WiFiType.h>
#include <WiFiUdp.h>

/*
  Connects to AWS IOT over HTTPS and pushes sensor data
  Apache 2.0 Licence. Steve Drew <steve@sdrew.net>
  Beta Quality - May eat hampster
*/

#include <WiFiClientSecure.h>
#include <ArduinoJson.h>

#include <DHT.h>

//Device
const char* deviceid = "test";
#define SLEEP_DELAY_IN_SECONDS  600
const char* devicelocation = "gh";

#define DHTTYPE DHT11
const int DHTPin = 22;
const int soilpin = 32;
const int POWER_PIN = 34;
const int LIGHT_PIN = 33;

//WIFI
const char* ssid     = "iot-things";     // your network SSID (name of wifi network)
const char* password = "XXXXXXXXXXXX"; // your network password

//IOT Endpoint
const int httpsPort = 8443;
const char* server = "XXXXXXXXXXXX.iot.eu-west-1.amazonaws.com";  // Server URL
const char* topic = "/topics/ESP32/DHT11"; // TOPIC to post to
// ASW IOT CA CERT

const char* test_ca_cert = \
                           "-----BEGIN CERTIFICATE-----\n" \
                           "-----END CERTIFICATE-----\n";

// You can use x.509 client certificates if you want
const char* privateKeyBuff = \
                             "-----BEGIN RSA PRIVATE KEY----- \n" \
                             "-----END RSA PRIVATE KEY-----\n";

const char* certificateBuff = \
                              "-----BEGIN CERTIFICATE----- \n" \
                              "-----END CERTIFICATE-----\n";

//Initialise Libs
DHT dht(DHTPin, DHTTYPE);
WiFiClientSecure client;

//Init Vars
static char celsiusTemp[7];
static char humidityTemp[7];

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(115200);
  delay(100);

  //Init Sensor
  dht.begin();

  //Wire.begin (BHSDAPin, BHSCLPin);

  //Connect to WiFi
  Serial.print("Attempting to connect to SSID: ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  Serial.println(WiFi.status());

  // attempt to connect to Wifi network:
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("WifiStatus: ");
    Serial.println(WiFi.status());
    // wait 2 second for re-trying
    delay(2000);
  }
  Serial.println(WiFi.localIP());
  Serial.println(WiFi.RSSI());
}

void loop() {

  Serial.println("Start of loop sleep");
  //Sleep waiting for sensor
  delay(5000);

  int waterlevel = analogRead(soilpin);
  int lightlevel = analogRead(LIGHT_PIN);

  waterlevel = map(waterlevel, 0, 4095, 0, 1023);
  waterlevel = constrain(waterlevel, 0, 1023);
  lightlevel = map(lightlevel, 0, 4095, 0, 1023);
  lightlevel = constrain(lightlevel, 0, 1023);

  // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
  float humidity = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float temperature = dht.readTemperature();

  float hic = dht.computeHeatIndex(temperature, humidity, false);
  dtostrf(hic, 6, 2, celsiusTemp);
  dtostrf(humidity, 6, 2, humidityTemp);

  String did = String(deviceid);
  String water = String((int)waterlevel);
  String light = String((int)lightlevel);

  //publish(deviceid, "HIGROW", "light", lightlevel, devicelocation);
  //publish(deviceid, "DHT11", "temp", temperature, devicelocation);
  //publish(deviceid, "DHT11", "humid", humidity, devicelocation);
  //publish(deviceid, "HIGROW", "water", int(waterlevel), devicelocation);
  //Testing
  publish("moo", "moo", "moo", 0, "moo");

  Serial.print("Entering sleep mode");
  delay(5000);
}

void publish(const char *deviceid, const char *sensortype, const char *sensordesc, uint16_t sensorval, const char *sensorlocation) {

  // Create JSON object to send from sensor data
  StaticJsonBuffer<300> jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  JsonArray& array = jsonBuffer.createArray();
  JsonObject& reported = array.createNestedObject();
  JsonObject& data = array.createNestedObject();

  data["device"] = deviceid;
  data["sensortype"] = sensortype;
  data["sensordesc"] = sensordesc;
  data["sensorval"] = sensorval;
  data["sensorloc"] = sensorlocation;

  reported["reported"] = data;
  root["state"] = reported;

  // Publish Data to AWS IoT
  String output;
  root.printTo(output);

  client.setCACert(test_ca_cert);
  client.setCertificate(certificateBuff); 
  client.setPrivateKey(privateKeyBuff);  

  Serial.println("\nStarting connection to server...");
  if (!client.connect(server, httpsPort))
    Serial.println("Connection failed!");
  else {
    Serial.println("Connected to server!");
    Serial.println(server);

    //client.println("POST /topics/HIGROW/higrow1?qos=1 HTTP/1.1");
    String topicreq = "POST ";
    String topicqos = "?qos=1 HTTP/1.1";
    String topictmp = topicreq + topic + topicqos;
    //Serial.println(topictmp);
    client.println(topictmp);

    //client.println("Host: a2alduq2b5zo9j.iot.eu-west-1.amazonaws.com");
    String hostreq = "Host: ";
    String hosttmp = hostreq + server;
    client.println(hosttmp);
    client.print("Content-Length: ");
    client.println(String(output.length()));
    client.println("Connection: close");
    client.println();
    client.println(output);
    //Serial.println(output);

    while (client.connected()) {
      String line = client.readStringUntil('\n');
      if (line == "\r") {
        Serial.println("headers received");
        Serial.print(line);
        break;
      }
    }
    // if there are incoming bytes available
    // from the server, read them and print them:
    while (client.available()) {
      char c = client.read();
      Serial.write(c);
    }

    client.stop();
    delay(5000);
  }
}

Debug Messages:

Try1 (failure)

Starting connection to server...
[I][ssl_client.cpp:45] start_ssl_client(): Free heap before TLS 166788
[I][ssl_client.cpp:47] start_ssl_client(): Starting socket
[I][ssl_client.cpp:75] start_ssl_client(): Seeding the random number generator
[I][ssl_client.cpp:84] start_ssl_client(): Setting up the SSL/TLS structure...
[I][ssl_client.cpp:97] start_ssl_client(): Loading CA cert
[I][ssl_client.cpp:115] start_ssl_client(): Loading CRT cert
[I][ssl_client.cpp:122] start_ssl_client(): Loading private key
[I][ssl_client.cpp:153] start_ssl_client(): Performing the SSL/TLS handshake...
Guru Meditation Error: Core  1 panic'ed (Unhandled debug exception)
Debug exception reason: Stack canary watchpoint triggered 
Register dump:
PC      : 0x4000bff0  PS      : 0x00060736  A0      : 0x800851cc  A1      : 0x3ffcc920  
A2      : 0x00000000  A3      : 0x00060723  A4      : 0x00060720  A5      : 0x0000014c  
A6      : 0x3ffde714  A7      : 0x00000013  A8      : 0x800840c5  A9      : 0x3ffcc9c0  
A10     : 0x00000003  A11     : 0x00060723  A12     : 0x00060723  A13     : 0x00000000  
A14     : 0x3ffddd72  A15     : 0x00000010  SAR     : 0x0000001f  EXCCAUSE: 0x00000001  
EXCVADDR: 0x00000000  LBEG    : 0x4000c46c  LEND    : 0x4000c477  LCOUNT  : 0x00000000  

Backtrace: 0x4000bff0:0x3ffcc920 0x400851cc:0x3ffcc9f0 0x4008478a:0x3ffcca10 0x40082332:0x3ffcca50 0x400823f0:0x3ffcca80 0x400f79f8:0x3ffccaa0 0x400f7f47:0x3ffccac0 0x400fc0fc:0x3ffccb00 0x400fc43f:0x3ffccb30 0x400fc5c6:0x3ffccbb0 0x400f79e2:0x3ffccbd0 0x400f7d08:0x3ffccc00 0x400f149b:0x3ffccc50 0x400f21f8:0x3ffccc80 0x400f23d8:0x3ffcd0c0 0x400f01a2:0x3ffcd0f0 0x400efee6:0x3ffcd120 0x400f005a:0x3ffcd140 0x400f6ef8:0x3ffcd180 0x400f7114:0x3ffcd220 0x400f735a:0x3ffcd2c0 0x400f5fbe:0x3ffcd300 0x40108b27:0x3ffcd340 0x400f3ddc:0x3ffcd360 0x400f3e1c:0x3ffcd380 0x400d2d89:0x3ffcd3a0 0x400d2933:0x3ffcd600 0x400d29bc:0x3ffcd620 0x400d29da:0x3ffcd650 0x400d20a7:0x3ffcd670 0x400d22fc:0x3ffcd830 0x40131aef:0x3ffcd890

CPU halted.

Decoding 35 results
0x400851cc: prvIdleTask at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 4415
0x4008478a: prvAddCurrentTaskToDelayedList at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 4415
0x40082332: pp_post at ?? line ?
0x400823f0: wDev_ProcessTxop at ?? line ?
0x400f79f8: mpi_to_mem_block at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 308
:  (inlined by) modular_multiply_finish at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 410
:  (inlined by) mpi_mult_mpi_failover_mod_mult at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 570
0x400f7f47: mbedtls_sha1_software_process at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_sha1.c line 155
0x400fc0fc: mbedtls_mpi_div_mpi at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/bignum.c line 2152
0x400fc43f: mbedtls_mpi_mod_mpi at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/bignum.c line 2152
0x400fc5c6: mbedtls_mpi_inv_mod at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/bignum.c line 1903
0x400f79e2: mpi_to_mem_block at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 308
:  (inlined by) modular_multiply_finish at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 410
:  (inlined by) mpi_mult_mpi_failover_mod_mult at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 570
0x400f7d08: bits_to_hardware_words at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 135
:  (inlined by) mbedtls_mpi_mul_mpi at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/port/esp_bignum.c line 439
0x400f149b: mbedtls_rsa_private at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/rsa.c line 1607
0x400f21f8: mbedtls_rsa_rsassa_pkcs1_v15_verify at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/rsa.c line 1607
0x400f23d8: ssl_swap_epochs at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/ssl_tls.c line 6123
0x400f01a2: ecdsa_verify_wrap at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/pk_wrap.c line 56
0x400efee6: mbedtls_pk_verify_ext at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/pk.c line 361
0x400f005a: rsa_decrypt_wrap at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/pk_wrap.c line 56
0x400f6ef8: x509_crt_verify_child at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/x509_crt.c line 1251
0x400f7114: mbedtls_x509_crt_verify_with_profile at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/x509_crt.c line 1251
0x400f735a: x509_crt_parse_der_core at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/x509_crt.c line 1251
0x400f5fbe: mbedtls_ssl_parse_finished at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/ssl_tls.c line 6123
0x40108b27: ssl_write_renegotiation_ext at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/ssl_srv.c line 87
0x400f3ddc: ssl_handshake_init at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/ssl_tls.c line 6123
0x400f3e1c: ssl_handshake_init at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/mbedtls/library/ssl_tls.c line 6123
0x400d2d89: DHT::readHumidity(bool) at C:\Users\steve\Documents\Arduino\libraries\DHT_sensor_library/DHT.cpp line 84
0x400d2933: start_ssl_client(sslclient_context*, unsigned int, unsigned int, char const*, char const*, char const*) at C:\Users\steve\Documents\Arduino\hardware\espressif\esp32\libraries\WiFiClientSecure\src/ssl_client.cpp line 86
0x400d29bc: start_ssl_client(sslclient_context*, unsigned int, unsigned int, char const*, char const*, char const*) at C:\Users\steve\Documents\Arduino\hardware\espressif\esp32\libraries\WiFiClientSecure\src/ssl_client.cpp line 113
0x400d29da: start_ssl_client(sslclient_context*, unsigned int, unsigned int, char const*, char const*, char const*) at C:\Users\steve\Documents\Arduino\hardware\espressif\esp32\libraries\WiFiClientSecure\src/ssl_client.cpp line 123
0x400d20a7: publish(char const*, char const*, char const*, unsigned short, char const*) at C:\!iot\aws-esp32-dht22-bh1750-test\main/main.ino line 230
0x400d22fc: WiFiGenericClass::mode(wifi_mode_t) at c:\users\steve\documents\arduino\hardware\espressif\esp32\tools\xtensa-esp32-elf\xtensa-esp32-elf\include\c++\5.2.0\bits/stl_vector.h line 923
0x40131aef: _fopen_r at /Users/ivan/e/newlib_xtensa-2.2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/stdio/../../../.././newlib/libc/stdio/fopen.c line 170 (discriminator 2)

---Try 2 (Success)

[I][ssl_client.cpp:45] start_ssl_client(): Free heap before TLS 166924
[I][ssl_client.cpp:47] start_ssl_client(): Starting socket
[I][ssl_client.cpp:75] start_ssl_client(): Seeding the random number generator
[I][ssl_client.cpp:84] start_ssl_client(): Setting up the SSL/TLS structure...
[I][ssl_client.cpp:97] start_ssl_client(): Loading CA cert
[I][ssl_client.cpp:115] start_ssl_client(): Loading CRT cert
[I][ssl_client.cpp:122] start_ssl_client(): Loading private key
[I][ssl_client.cpp:153] start_ssl_client(): Performing the SSL/TLS handshake...
[I][ssl_client.cpp:165] start_ssl_client(): Protocol is TLSv1.2 Ciphersuite is TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
[I][ssl_client.cpp:167] start_ssl_client(): Record expansion is 29
[I][ssl_client.cpp:173] start_ssl_client(): Verifying peer X.509 certificate...
[I][ssl_client.cpp:183] start_ssl_client(): Certificate verified.
[I][ssl_client.cpp:186] start_ssl_client(): Free heap after TLS 120456
Connected to server!
a2alduq2b5zo9j.iot.eu-west-1.amazonaws.com
headers received

{"message":"OK","traceId":"eee7b8a2-21ec-c265-4915-4411716a5c5e"}[I][ssl_client.cpp:194] stop_ssl_socket(): Cleaning SSL connection.
Entering sleep mode

Starting connection to server...
[I][ssl_client.cpp:45] start_ssl_client(): Free heap before TLS 165692
[I][ssl_client.cpp:47] start_ssl_client(): Starting socket
[I][ssl_client.cpp:75] start_ssl_client(): Seeding the random number generator
[I][ssl_client.cpp:84] start_ssl_client(): Setting up the SSL/TLS structure...
[I][ssl_client.cpp:97] start_ssl_client(): Loading CA cert
[I][ssl_client.cpp:115] start_ssl_client(): Loading CRT cert
[I][ssl_client.cpp:122] start_ssl_client(): Loading private key
[I][ssl_client.cpp:153] start_ssl_client(): Performing the SSL/TLS handshake...
[I][ssl_client.cpp:165] start_ssl_client(): Protocol is TLSv1.2 Ciphersuite is TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
[I][ssl_client.cpp:167] start_ssl_client(): Record expansion is 29
[I][ssl_client.cpp:173] start_ssl_client(): Verifying peer X.509 certificate...
[I][ssl_client.cpp:183] start_ssl_client(): Certificate verified.
[I][ssl_client.cpp:186] start_ssl_client(): Free heap after TLS 120012
Connected to server!
a2alduq2b5zo9j.iot.eu-west-1.amazonaws.com
headers received

{"message":"OK","traceId":"2453ffdc-9a92-00a4-532f-5febb23e4407"}[I][ssl_client.cpp:194] stop_ssl_socket(): Cleaning SSL connection.
copercini commented 6 years ago

Debug exception reason: Stack canary watchpoint triggered

Out of stack....

try move these lines to setup():

  client.setCACert(test_ca_cert);
  client.setCertificate(certificateBuff); 
  client.setPrivateKey(privateKeyBuff);  

Edit: Let me try understand: looks like you will put it in deep sleep and wake up every 10 minutes... then read DHT sensor and send once (or how many times?) to AWS IoT via HTTPS and sleep again That's right?

steeeeeeved commented 6 years ago

Moving the three lines gives the same out of stack error.. (Thanks for identifying that for me, I'll get googling about workarounds/fixing)

steeeeeeved commented 6 years ago

Hi, Yes thats what I'm looking to do.. (Using a HiGrow temp / moisture sensor board) So tracing back, I did not have the latest checkout of arduinio-esp32 and was missing the increase in stack size: https://github.com/espressif/arduino-esp32/commit/4495659ac53bcdc4f8e59d6918b3e472903d63a7#diff-9b0caa90c75b300921be10a08d10ff95

Thanks for pointing me in the right direction.