Skip to content

BearSSL::WiFiClientSecure not working the same way axTLS did #4738

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
artua opened this issue May 17, 2018 · 34 comments
Closed

BearSSL::WiFiClientSecure not working the same way axTLS did #4738

artua opened this issue May 17, 2018 · 34 comments
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.

Comments

@artua
Copy link

artua commented May 17, 2018

Platform

  • Hardware: [ESP-12]
  • Core Version: [Mon May 14 20:46:47 2018 -0700]
  • Development Env: [Arduino IDE]
  • Operating System: [MacOS]

Settings in IDE

  • Module: [Generic ESP8266 Module]
  • Flash Mode: [qio]
  • Flash Size: [4MB]
  • lwip Variant: [v2 Lower Memory|Higher Bandwidth]
  • Reset Method: [nodemcu]
  • Flash Frequency: [40Mhz]
  • CPU Frequency: [80Mhz]
  • Upload Using: [SERIAL]
  • Upload Speed: [115200]

Problem Description

BearSSL::WiFiClientSecure does not connect. No debug messages at all (debug tls+ssl+http client). My custom logs attached.

MCVE Sketch

#include <WiFiClientSecure.h>

void setup() {
 BearSSL::WiFiClientSecure client;
 if (!client.connect(host, httpsPort)) {
    res.code = 0; res.content = F("connection failed");
    return (res); // 1 = connection failed
  }
}

void loop() {

}

Debug Messages

00:01:374 Free HEAP: 36264
00:01:375 Client Secure started 1 ms
00:01:375 Free HEAP: 31704
00:01:376 connecting to api.monitec.systems
00:01:969 Connection failed 593 ms
00:01:973 Request failed. Reason: (0) connection failed
@earlephilhower
Copy link
Collaborator

earlephilhower commented May 17, 2018

Please check the examples. You have not set any CAs, nor have you asked it to be insecure (i.e. SSL connect but w/o checking certificates). client.setInsecure() will do the latter, before any connection.

Added #4739 to track adding some debug output beyond the GetLastError API.

@earlephilhower earlephilhower added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label May 17, 2018
@artua
Copy link
Author

artua commented May 17, 2018

Thanks, I already found client.setInsecure().
But it still won't work and I can't get any debug output.

Debug Messages

0:01:284 connecting to api2.monitec.systems
00:02:235 Free HEAP: 10456
00:02:236 Connected 952 ms
00:02:237 fingerprint doesn't match
00:02:238 Certificate checked 1 ms
00:02:239 Free HEAP: 10456
00:02:240 requesting URL: /api/data with data: {"....."}
00:02:309 Request sent 14 ms
00:02:310 Free HEAP: 8840
00:07:312 Result length: 0 

MCVE Sketch (logger lines removed)

BearSSL::WiFiClientSecure client;
  client.setInsecure();
  client.setTimeout(API_TIMEOUT);
  client.connect(host, httpsPort);
  client.print(String("POST ") + url + " HTTP/1.0\r\n" +
               "Host: " + host + "\r\n"
               + "Content-Type: application/json\r\n"
               + "Content-Length: " + data.length() + "\r\n\r\n"
               + data);
  client.setTimeout(API_TIMEOUT);
  while (client.connected()) {
    String line = client.readStringUntil('\n');
    if (line.startsWith("HTTP/1.")) {
      res.code = line.substring(9, 12).toInt();
    }
    if (line.startsWith("Content-Length: ")) {
      c_len = line.substring(15).toInt();
    }
    if (line == "\r") {
      break;
    }
  }
  if (client.available()) {
    bytes = client.readBytes(c_buf, c_len);
    c_buf[c_len] = '\0';
  }
  res.content = String(c_buf);
  client.stop();
  return (res);
}

@artua artua changed the title BearSSL::WiFiClientSecure BearSSL::WiFiClientSecure not working for me May 21, 2018
@artua artua changed the title BearSSL::WiFiClientSecure not working for me BearSSL::WiFiClientSecure not working the same way axTLS did May 21, 2018
@earlephilhower
Copy link
Collaborator

@artua, can you please give a full sketch example that doesn't work and can be publicly accessible? I'm not able to reproduce it with my own testing...

@artua
Copy link
Author

artua commented May 22, 2018

Hello @earlephilhower

Here is quick sketch for you to quickly compare two clients and debug output for both.
Just comment bearssl client and uncomment axtls client if you wish to compare.

Maybe timeout is hardcoded or setTimeout() not working?

Also please note it takes almost 500ms more for BearSSL to connect. It's huge for tiny battery-powered devices.

Sketch

#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const int API_TIMEOUT = 8000;
struct Result {
  int code;
  String content;
};
  Result res;

String line;
int myWdt;
const char* host = "el-sag.com";
const char* url = "/bearssl.txt";
const int httpsPort = 443;
long init_ms;
String data = "{}";
bool TOUT;
  int c_len;
  char c_buf[512];
  int bytes;

void setup() {
  // put your setup code here, to run once:
init_ms = millis();
  Serial.begin(74880);
  Serial.print("Connecting to Wifi");
  WiFi.begin();
  while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(200); }
  Serial.println();
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  
}

void loop() {
  myWdt = millis();
  BearSSL::WiFiClientSecure client;
  client.setInsecure();
  //WiFiClientSecure client;

  client.setTimeout(API_TIMEOUT);
  logger("Client Secure started " + String(millis() - myWdt) + " ms");
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  logger("connecting to " + String(host));
  myWdt = millis();
  if (!client.connect(host, httpsPort)) {
    logger("Connection failed " + String(millis() - myWdt) + " ms");
    res.code = 0; res.content = F("connection failed");
    return; // 1 = connection failed
  }
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  logger("Connected " + String(millis() - myWdt) + " ms");
  myWdt = millis();
  
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  logger("requesting URL: " + String(url) + " with data: " + String(data));
  myWdt = millis();
  
  client.print(String("POST ") + url + " HTTP/1.0\r\n" +
               "Host: " + host + "\r\n"
               + "Content-Type: application/json\r\n"
               + "Content-Length: " + data.length() + "\r\n\r\n"
               + data);

  logger("Request sent " + String(millis() - myWdt) + " ms");
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  client.setTimeout(API_TIMEOUT);
  
  myWdt = millis();
  TOUT=1;
  while (client.connected()) {
    line = client.readStringUntil('\n');
    if (line.startsWith("HTTP/1.")) {
      res.code = line.substring(9, 12).toInt();
      logger("Got HTTP code " + String(res.code));
    }
    if (line.startsWith("Content-Length: ")) {
      c_len = line.substring(15).toInt();
      logger("Got Content-length: " + String(c_len));
    }
    if (line == "\r") {
      logger("Headers received " + String(millis() - myWdt) + " ms");
      logger("Free HEAP: " + String(ESP.getFreeHeap()));
      TOUT=0;
      break;
    }
  }
  if(TOUT) { 
    logger("\n*** Timeout receiving headers\n");
    return;
  }
  
  myWdt = millis();
  if (client.available()) {
    TOUT=1;
    //res.content += String(client.read());
    bytes = client.readBytes(c_buf, c_len);
    c_buf[c_len] = '\0';
    TOUT=0;
  }
  if(TOUT) { 
    logger("\n*** Timeout receiving body\n" + String());
    return;
  }
  res.content = String(c_buf);
  logger("Result length: " + String(res.content.length()) + " | " + String(bytes));
  logger("Content received " + String(millis() - myWdt) + " ms");
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  myWdt = millis();
  client.stop();
  logger("Client stop " + String(millis() - myWdt) + " ms");
  logger("Free HEAP: " + String(ESP.getFreeHeap()));
  logger("\n*** Result: " + res.content);
}

void printTS() {
  long ts = millis() - init_ms;
  int m = ts / 60000;
  int s = (ts) / 1000 - m * 60;
  int ms = ts - m * 60000 - s * 1000;
  Serial.printf("%02d:%02d:%03d ", m, s, ms);
}

void logger(String str) {
  printTS();
  Serial.println(str);
}

Debug (BearSSL)

SDK:2.2.1(cfd48f3)/Core:2.4.1-84-g83ab0343/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-7-g2b827f8)
Connecting to Wifiscandone
.scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 2
cnt 

connected with G14, channel 11
dhcp client start...
ip:192.168.3.43,mask:255.255.255.0,gw:192.168.3.200

00:00:205 Free HEAP: 46392
00:00:206 Client Secure started 0 ms
00:00:206 Free HEAP: 41832
00:00:207 connecting to el-sag.com
00:01:593 Free HEAP: 19160
00:01:594 Connected 1387 ms
00:01:595 Free HEAP: 19160
00:01:595 requesting URL: /bearssl.txt with data: {}
00:01:610 Request sent 14 ms
00:01:611 Free HEAP: 17544
00:01:734 Got HTTP code 200
00:01:742 Got Content-length: 21
00:01:745 Headers received 134 ms
00:01:746 Free HEAP: 19096
00:01:747 Result length: 21 | 21
00:01:747 Content received 1 ms
00:01:748 Free HEAP: 19080
00:01:754 Client stop 3 ms
00:01:755 Free HEAP: 41408
00:01:759 
*** Result: {"result":"success"}

00:01:765 Client Secure started 0 ms
00:01:770 Free HEAP: 41720
00:01:774 connecting to el-sag.com
00:03:125 Free HEAP: 19080
00:03:126 Connected 1348 ms
00:03:127 Free HEAP: 19080
00:03:127 requesting URL: /bearssl.txt with data: {}
00:03:142 Request sent 14 ms
00:03:143 Free HEAP: 17464
00:03:262 Got HTTP code 200
00:03:269 Got Content-length: 21
00:03:272 Headers received 129 ms
00:03:273 Free HEAP: 19080
00:03:274 Result length: 21 | 21
00:03:275 Content received 1 ms
00:03:275 Free HEAP: 19080
00:03:281 Client stop 3 ms
00:03:282 Free HEAP: 41408
00:03:286 
*** Result: {"result":"success"}

00:03:292 Client Secure started 0 ms
00:03:297 Free HEAP: 41720
00:03:301 connecting to el-sag.com
00:04:647 Free HEAP: 19080
00:04:648 Connected 1343 ms
00:04:649 Free HEAP: 19080
00:04:649 requesting URL: /bearssl.txt with data: {}
00:04:664 Request sent 13 ms
00:04:664 Free HEAP: 17464
00:04:785 Got HTTP code 200
00:04:793 Got Content-length: 21
00:04:796 Headers received 131 ms
00:04:797 Free HEAP: 19080
00:04:798 Result length: 21 | 21
00:04:798 Content received 1 ms
00:04:799 Free HEAP: 19080
00:04:806 Client stop 3 ms
00:04:806 Free HEAP: 41408
00:04:810 
*** Result: {"result":"success"}

00:04:816 Client Secure started 0 ms
00:04:821 Free HEAP: 41720
00:04:825 connecting to el-sag.com
00:06:175 Free HEAP: 19080
00:06:175 Connected 1346 ms
00:06:176 Free HEAP: 19080
00:06:177 requesting URL: /bearssl.txt with data: {}
00:06:191 Request sent 14 ms
00:06:192 Free HEAP: 17464
00:06:313 Got HTTP code 200
00:06:314 
*** Timeout receiving headers

00:06:315 Client Secure started 0 ms
00:06:316 Free HEAP: 41720
00:06:316 connecting to el-sag.com
00:07:716 Free HEAP: 19080
00:07:717 Connected 1397 ms
00:07:718 Free HEAP: 19080
00:07:719 requesting URL: /bearssl.txt with data: {}
00:07:733 Request sent 14 ms
00:07:734 Free HEAP: 17464
00:07:869 Got HTTP code 200
00:07:869 
*** Timeout receiving headers

00:07:871 Client Secure started 0 ms
00:07:872 Free HEAP: 41720
00:07:872 connecting to el-sag.com
00:09:239 Free HEAP: 19080
00:09:240 Connected 1364 ms
00:09:240 Free HEAP: 19080
00:09:241 requesting URL: /bearssl.txt with data: {}
00:09:256 Request sent 15 ms
00:09:257 Free HEAP: 17464
00:09:382 Got HTTP code 200
00:09:382 
*** Timeout receiving headers

00:09:384 Client Secure started 0 ms
00:09:385 Free HEAP: 41720
00:09:385 connecting to el-sag.com
pm open,type:2 0
00:10:805 Free HEAP: 18560
00:10:806 Connected 1418 ms
00:10:807 Free HEAP: 18560
00:10:808 requesting URL: /bearssl.txt with data: {}
00:10:822 Request sent 14 ms
00:10:823 Free HEAP: 16944
00:10:949 Got HTTP code 200
00:10:950 
*** Timeout receiving headers

00:10:952 Client Secure started 0 ms
00:10:952 Free HEAP: 41200
00:10:953 connecting to el-sag.com
00:12:304 Free HEAP: 18560
00:12:305 Connected 1349 ms
00:12:306 Free HEAP: 18560
00:12:306 requesting URL: /bearssl.txt with data: {}
00:12:321 Request sent 14 ms
00:12:322 Free HEAP: 16944
00:12:442 Got HTTP code 200
00:12:450 Got Content-length: 21
00:12:453 Headers received 130 ms
00:12:453 Free HEAP: 18560
00:12:454 Result length: 21 | 21
00:12:455 Content received 1 ms
00:12:456 Free HEAP: 18560
00:12:462 Client stop 3 ms
00:12:463 Free HEAP: 40760
00:12:466 
*** Result: {"result":"success"}

00:12:473 Client Secure started 0 ms
00:12:478 Free HEAP: 41072
00:12:481 connecting to el-sag.com
00:13:828 Free HEAP: 18560
00:13:829 Connected 1342 ms
00:13:829 Free HEAP: 18560
00:13:830 requesting URL: /bearssl.txt with data: {}
00:13:845 Request sent 14 ms
00:13:846 Free HEAP: 16944
00:13:969 Got HTTP code 200
00:13:970 
*** Timeout receiving headers

00:13:971 Client Secure started 0 ms
00:13:972 Free HEAP: 41200
00:13:972 connecting to el-sag.com
00:15:452 Free HEAP: 18560
00:15:453 Connected 1478 ms
00:15:454 Free HEAP: 18560
00:15:454 requesting URL: /bearssl.txt with data: {}
00:15:469 Request sent 13 ms
00:15:470 Free HEAP: 16944
00:15:651 Got HTTP code 200
00:15:652 
*** Timeout receiving headers

00:15:654 Client Secure started 1 ms
00:15:654 Free HEAP: 41200
00:15:655 connecting to el-sag.com
00:17:121 Free HEAP: 18560
00:17:122 Connected 1464 ms
00:17:123 Free HEAP: 18560
00:17:123 requesting URL: /bearssl.txt with data: {}
00:17:138 Request sent 14 ms
00:17:139 Free HEAP: 16944
00:17:392 Got HTTP code 200
00:17:393 
*** Timeout receiving headers

00:17:394 Client Secure started 0 ms
00:17:395 Free HEAP: 41200
00:17:396 connecting to el-sag.com
00:18:850 Free HEAP: 18560
00:18:850 Connected 1451 ms
00:18:851 Free HEAP: 18560
00:18:852 requesting URL: /bearssl.txt with data: {}
00:18:867 Request sent 14 ms
00:18:867 Free HEAP: 16944
00:18:994 Got HTTP code 200
00:18:995 
*** Timeout receiving headers

Debug (axTLS)

SDK:2.2.1(cfd48f3)/Core:2.4.1-84-g83ab0343/lwIP:2.0.3(STABLE-2_0_3_RELEASE/glue:arduino-2.4.1-7-g2b827f8)
Connecting to Wifiscandone
.scandone
state: 0 -> 2 (b0)
state: 2 -> 3 (0)
state: 3 -> 5 (10)
add 0
aid 2
cnt 

connected with G14, channel 11
dhcp client start...
ip:192.168.3.43,mask:255.255.255.0,gw:192.168.3.200

00:00:204 Free HEAP: 45896
00:00:205 Client Secure started 0 ms
00:00:206 Free HEAP: 45896
00:00:206 connecting to el-sag.com
00:01:094 Free HEAP: 31432
00:01:095 Connected 886 ms
00:01:096 Free HEAP: 31432
00:01:098 requesting URL: /bearssl.txt with data: {}
00:01:104 Request sent 4 ms
00:01:105 Free HEAP: 29816
00:01:245 Got HTTP code 200
00:01:255 Got Content-length: 21
00:01:260 Headers received 153 ms
00:01:261 Free HEAP: 31496
00:01:266 Result length: 21 | 21
00:01:268 Content received 6 ms
00:01:269 Free HEAP: 31368
00:01:270 Client stop 1 ms
00:01:271 Free HEAP: 31368
00:01:272 
*** Result: {"result":"success"}

00:01:288 Client Secure started 0 ms
00:01:289 Free HEAP: 46080
00:01:289 connecting to el-sag.com
00:02:146 Free HEAP: 31352
00:02:147 Connected 856 ms
00:02:148 Free HEAP: 31352
00:02:150 requesting URL: /bearssl.txt with data: {}
00:02:156 Request sent 5 ms
00:02:157 Free HEAP: 29736
00:02:295 Got HTTP code 200
00:02:306 Got Content-length: 21
00:02:311 Headers received 152 ms
00:02:312 Free HEAP: 31368
00:02:314 Result length: 21 | 21
00:02:315 Content received 2 ms
00:02:317 Free HEAP: 31496
00:02:318 Client stop 0 ms
00:02:320 Free HEAP: 31496
00:02:323 
*** Result: {"result":"success"}

00:02:339 Client Secure started 0 ms
00:02:339 Free HEAP: 46080
00:02:340 connecting to el-sag.com
00:03:213 Free HEAP: 31352
00:03:214 Connected 872 ms
00:03:215 Free HEAP: 31352
00:03:217 requesting URL: /bearssl.txt with data: {}
00:03:223 Request sent 4 ms
00:03:224 Free HEAP: 29736
00:03:361 Got HTTP code 200
00:03:372 Got Content-length: 21
00:03:377 Headers received 151 ms
00:03:378 Free HEAP: 31368
00:03:380 Result length: 21 | 21
00:03:381 Content received 2 ms
00:03:383 Free HEAP: 31528
00:03:384 Client stop 0 ms
00:03:386 Free HEAP: 31528
00:03:389 
*** Result: {"result":"success"}

00:03:405 Client Secure started 0 ms
00:03:405 Free HEAP: 46112
00:03:406 connecting to el-sag.com
00:04:376 Free HEAP: 31352
00:04:378 Connected 969 ms
00:04:379 Free HEAP: 31352
00:04:380 requesting URL: /bearssl.txt with data: {}
00:04:386 Request sent 5 ms
00:04:388 Free HEAP: 29736
00:04:525 Got HTTP code 200
00:04:535 Got Content-length: 21
00:04:540 Headers received 152 ms
00:04:541 Free HEAP: 31480
00:04:546 Result length: 21 | 21
00:04:548 Content received 5 ms
00:04:549 Free HEAP: 31240
00:04:550 Client stop 0 ms
00:04:552 Free HEAP: 31240
00:04:553 
*** Result: {"result":"success"}

00:04:569 Client Secure started 0 ms
00:04:569 Free HEAP: 45952
00:04:570 connecting to el-sag.com
00:05:465 Free HEAP: 31352
00:05:466 Connected 895 ms
00:05:468 Free HEAP: 31352
00:05:469 requesting URL: /bearssl.txt with data: {}
00:05:475 Request sent 5 ms
00:05:476 Free HEAP: 29736
00:05:620 Got HTTP code 200
00:05:630 Got Content-length: 21
00:05:635 Headers received 158 ms
00:05:636 Free HEAP: 31368
00:05:638 Result length: 21 | 21
00:05:640 Content received 2 ms
00:05:641 Free HEAP: 31496
00:05:642 Client stop 0 ms
00:05:644 Free HEAP: 31496
00:05:647 
*** Result: {"result":"success"}

@earlephilhower
Copy link
Collaborator

Looks like a bug in client.connected() in BearSSL. If you change that while (client.connected()) to while (TOUT) it always succeeds. Probably reporting client.connected() relative to the TCP link, which may go down before the entire SSL data has been read out. Let me take a gander...

earlephilhower added a commit to earlephilhower/Arduino that referenced this issue May 22, 2018
The SSL pipeline is multi-stage, and the TCP connection can go down
even though there is still data waiting to be decrypted or in the
decryption buffer.

Explicitly check that there if there can be any data made available
to the app, and if so report that we are still connected().  When
there is no data and there is no TCP connection, report disconnected.

Fixes esp8266#4738
@earlephilhower
Copy link
Collaborator

earlephilhower commented May 22, 2018

@artua, can you give the patch I referenced below a try (#4754)? I get full success with your example code on it. It was a race condition between the SSL fragmentation, the TCP connection, and the readout from the buffer which I didn't notice before. Very simple fix.

@artua
Copy link
Author

artua commented May 22, 2018

@earlephilhower When it will be merged to master so I can check?

@earlephilhower
Copy link
Collaborator

Please make a branch that has this PR in your own repo and try it out with your test...I'll be waiting on your response before it gets into master. :)

https://help.github.com/articles/checking-out-pull-requests-locally/

@artua
Copy link
Author

artua commented May 22, 2018

@earlephilhower yes, now it's working.
But still takes 500ms more to connect, so it is unusable on battery-powered devices. :(

Update: now it gives me timeouts with another host (powered by cloudflare, so using modern ciphers).
Try https://www.artua.com/bearssl.txt

01:22:644 Client Secure started 0 ms
01:22:649 Free HEAP: 40720
01:22:652 connecting to www.artua.com
01:23:986 Free HEAP: 18464
01:23:987 Connected 1330 ms
01:23:988 Free HEAP: 18464
01:23:988 requesting URL: /bearssl.txt with data: {}
01:24:001 Request sent 12 ms
01:24:002 Free HEAP: 16848
01:24:409 Got HTTP code 200
01:24:413 Got Content-length: 21
01:24:426 Headers received 423 ms
01:24:427 Free HEAP: 18464
01:24:428 Result length: 21 | 21
01:24:428 Content received 1 ms
01:24:429 Free HEAP: 18464
01:24:432 Client stop 2 ms
01:24:434 Free HEAP: 40912
01:24:438 
*** Result: {"result":"success"}

01:24:444 Client Secure started 0 ms
01:24:449 Free HEAP: 40976
01:24:453 connecting to www.artua.com
01:25:632 Free HEAP: 18464
01:25:633 Connected 1175 ms
01:25:634 Free HEAP: 18464
01:25:635 requesting URL: /bearssl.txt with data: {}
01:25:648 Request sent 12 ms
01:25:649 Free HEAP: 16848
01:40:649 
*** Timeout receiving headers

^^^ here I set timeout to 15000ms

@earlephilhower
Copy link
Collaborator

Can you leave that URL accessible for a while? I'll try tonight (12hrs or so). In the meantime, can you add a call to the client.getLastSSLError() and dump that on "timeout" as well as the state of connected()/etc. The code I saw kind of mushed up socket timeout with !connected() so it's hard to say what you're seeing from the dump.

int getLastSSLError(char *dest = NULL, size_t len = 0);

Best I can about the speed is tell you to use 160MHz, not 80MHz (avail from the menus). There's no HW acceleration of any kind in the system. Even 32-bit multiply is emulated. For EC key exchange @Adam5Wu and I have seen multi-second handshakes even at 160MHz.

@artua
Copy link
Author

artua commented May 22, 2018

Sure, it will be accessible.
For your request:

*** Timeout receiving headers. SSLError: "0" Connected: 0

If I understood it right:

    logger("\n*** Timeout receiving headers. SSLError: \"" + String(client.getLastSSLError()) + "\" Connected: " + String(client.connected()));

@earlephilhower
Copy link
Collaborator

That'll do. Can you also run using axTLS and verify that it works? Might be something related to other bits, that'd passing would help ensure it's related to BSSL. Thx!

@earlephilhower
Copy link
Collaborator

Thought deleting the fixed bit would make this not close. Guess not! Re-opening.

@earlephilhower
Copy link
Collaborator

Try pull #4756 . You found two bugs in the code! This second one had to do with the way partial reads from the SSL buffer were handled when the server responds fast and closes down the connection.

I've run against your server and it seems to work all the time.

@artua
Copy link
Author

artua commented May 24, 2018

Now working perfectly. Thanks!

01:38:003 Cycles: 91 Head TOUTs: 0 Body TOUTs: 0
Conn ave: 514
Req ave: 11
Head ave: 513
Body ave: 0
Stop ave: 0
Overall ave: 1038

@earlephilhower
Copy link
Collaborator

@artua, if you have a fixed system you're connecting to, you can use the client.setKnownKey(key) to hardcode the public key of the other system. That should take connection time down dramatically as it ignores the x509 certificate completely and uses the server public key you code to encrypt things.

Check the BearSSL_Validation example for more info.

Closing this for now.

@artua
Copy link
Author

artua commented May 24, 2018 via email

@artua
Copy link
Author

artua commented Jun 3, 2018

Hi @earlephilhower
I was testing performance and found no difference between
client.setInsecure();
and
client.setKnownKey(&key);

Is it ok? Because you told me the last method should dramatically increase TLS handshake...

setInsecure()

01:03:582 Cycles: 94 Head TOUTs: 0 Body TOUTs: 0
Conn ave: 567
Req ave: 12
Head ave: 34
Body ave: 1
Stop ave: 1

setKnownKey()

01:15:466 Cycles: 103 Head TOUTs: 0 Body TOUTs: 0
Conn ave: 565
Req ave: 12
Head ave: 34
Body ave: 1
Stop ave: 1

For example, the same timings for the same server via HTTP:

00:03:001 Cycles: 14 Head TOUTs: 0 Body TOUTs: 0
Conn ave: 54
Req ave: 0
Head ave: 39
Body ave: 3
Stop ave: 0

Any ideas how can I find TLS timings breakdown?

@earlephilhower
Copy link
Collaborator

@artua There's no way in BearSSL of getting a detailed breakdown, but I misspoke so this is expected, actually. The way a TLS handshake works is that you check the x509 and signing up through the cert chain until you find one you trust, then you use the public key to do some exchange of entropy. So you still need to do the EC stuff if the public key is EC.

@artua
Copy link
Author

artua commented Jun 3, 2018 via email

@Adam5Wu
Copy link
Contributor

Adam5Wu commented Jun 3, 2018

I recall that SSL do have some part of the protocol for fast reconnection, if the client can remember something, subsequent connection can be much faster than the first one. Is that supported/implemented with BearSSL?

@earlephilhower
Copy link
Collaborator

They're SSL sessions, like HTTP. It's dependant on the server and client, of course. As of now the BearSSL::WiFiClientSecure does NOT enable them, as they take some amount of RAM and we're not exactly drowning in available heap. The session ID is stored as well as the last encryption params by the client, and then it sends it back to the server on a reconnect sometime later who either accepts it (and uses the last settings) or starts a new negotiation.

If there's a real demand I (or someone else!) can add a patch that adds a "bool enableSessions(uint count)" method. Feel free to open an issue or pull request. :) First, though, I want to get the CertStore changes accepted, though, as that's a more common issue since PIO's default is unsafe but common and tripping up folks.

I think axTLS also has the option, but it's also not implemented in the wrapper class.

@iamneo2416
Copy link

iamneo2416 commented Jun 12, 2018

@earlephilhower

I also met same error with client.connect, but I use connect(IPAddress, port).
Whenever I change connect to connect(host_name, port), it works fine! Switch back, it failed again.

host_name = "google.com", "api.github.com", that works, but when I change to 192.168.1.210, it always connect fail !!

I followed following link to create a web server (apache2) + php + mysql + phpMyAdmin, I can use browser to login https://192.168.1.210/phpMyAdmin correctly, but I cannot use sketch to access it with WiFiClientSecure client.

https://www.digitalocean.com/community/tutorials/how-to-create-a-self-signed-ssl-certificate-for-apache-in-ubuntu-16-04

I use dos command line to check port 443 with following
C:> telnet 192.168.1.210 443 <== it works. means 443 is listening !

here is my sketch.


#include <ESP8266WiFi.h>
#include <WiFiClientSecure.h>

const char* ssid = "mywifissid";
const char* password = "1234";

//const char* host = "api.github.com";  **<== marked out and change to following line.**
const char* host = "192.168.1.210";
const int httpsPort = 443;

// Use web browser to view and copy
// SHA1 fingerprint of the certificate
const char* fingerprint = "CF 05 98 89 CA FF 8E D8 5E 5C E0 C2 E4 F7 E6 C3 C7 50 DD 5C";

void setup()
{
  Serial.begin(115200);
  Serial.println();
  Serial.print("connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Use WiFiClientSecure class to create TLS connection
  WiFiClientSecure client;
   
  IPAddress host_ipv4(ipStrToNum(host)); **<== change ip string to IPAddress object.**
  Serial.print("connecting to ");
  Serial.println(host_ipv4); **<== this line output 192.168.1.210**

  client.setInsecure();
  if (!client.connect(host_ipv4, httpsPort))  <== this line always fail, it seems bug here !
  {
    Serial.println(">>> connection failed");
    return;
  }
**..... program does not run after this line .....**

  if (client.verify(fingerprint, host))
  {
    Serial.println("certificate matches");
  }
  else
  {
    Serial.println("certificate doesn't match");
  }

  String url = "/repos/esp8266/Arduino/commits/esp8266/status";
  Serial.print("requesting URL: ");
  Serial.println(url);

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" +
               "User-Agent: BuildFailureDetectorESP8266\r\n" +
               "Connection: close\r\n\r\n");

  Serial.println("request sent");
  while (client.connected())
  {
    String line = client.readStringUntil('\n');
    if (line == "\r")
    {
      Serial.println("headers received");
      break;
    }
  }
  String line = client.readStringUntil('\n');
  if (line.startsWith("{\"state\":\"success\""))
  {
    Serial.println("esp8266/Arduino CI successfull!");
  }
  else
  {
    Serial.println("esp8266/Arduino CI has failed");
  }
  Serial.println("reply was:");
  Serial.println("==========");
  Serial.println(line);
  Serial.println("==========");
  Serial.println("closing connection");
}

uint32_t ipStrToNum(const char* ipStr)
{
  const int SIZE_OF_NUMS = 4;
  union
  {
    uint8_t bytes[SIZE_OF_NUMS];  // IPv4 address
    uint32_t dword;
  } _address;
  _address.dword = 0; // clear return

  int i = 0;
  uint8_t num = 0; // start with 0
  while ((*ipStr) != '\0')
  {
    // while not end of string
    if ((*ipStr == '.') || (*ipStr == ','))
    {
      // store num and move on to next position
      _address.bytes[i++] = num;
      num = 0;
      if (i>=SIZE_OF_NUMS)
      {
        break; // filled array
      }
    }
    else
    {  
      if (*ipStr != ' ')
      {      // skip blanks
        num = (num << 3) + (num << 1); // *10 = *8+*2
        num = num +  (*ipStr - '0');
      }  
    }  
    ipStr++;
  }  
  if (i<SIZE_OF_NUMS)
  {
    // store last num
    _address.bytes[i++] = num;
  }
  return _address.dword; 
}  

void loop()
{
}

==============================

@chegewara
Copy link

@devyte
Hi, ive been told to use bearssl instead axtls, and here is a problem. BearSSL_Validation example is not connecting with certificates, only works insecure and fingerprints. I am assuming that certificates included should be valid for api.github.com (example host):

ip:192.168.43.11,mask:255.255.255.0,gw:192.168.43.1
.
WiFi connected
IP address: 
192.168.43.11

If there are no CAs or insecure options specified, BearSSL will not connect.
Expect the following call to fail as none have been configured.
Trying: api.github.com:443...*** Can't connect. ***
-------

This is absolutely *insecure*, but you can tell BearSSL not to check the
certificate of the server.  In this mode it will accept ANY certificate,
which is subject to man-in-the-middle (MITM) attacks.
Trying: api.github.com:443...Connected!
-------
HTTP/1.1 200 OK
-------


The SHA-1 fingerprint of an X.509 certificate can be used to validate it
instead of the while certificate.  This is not nearly as secure as real
X.509 validation, but is better than nothing.
Trying: api.github.com:443...Connected!
-------
HTTP/1.1 200 OK
-------


It is also possible to accept *any* self-signed certificate.  This is
absolutely insecure as anyone can make a self-signed certificate.
First, try and connect to a badssl.com self-signed website (will fail):
Trying: self-signed.badssl.com:443...*** Can't connect. ***
-------
Now we'll enable self-signed certs (will pass)
Trying: self-signed.badssl.com:443...*** Can't connect. ***
-------

The server certificate can be completely ignored and its public key
hardcoded in your application. This should be secure as the public key
needs to be paired with the private key of the site, which is obviously
private and not shared.  A MITM without the private key would not be
able to establish communications.
Trying: api.github.com:443...*** Can't connect. ***
-------

A specific certification authority can be passed in and used to validate
a chain of certificates from a given server.  These will be validated
using BearSSL's rules, which do NOT include certificate revocation lists.
A specific server's certificate, or your own self-signed root certificate
can also be used.  ESP8266 time needs to be valid for checks to pass as
BearSSL does verify the notValidBefore/After fields.
Try validating without setting the time (should fail)
Trying: api.github.com:443...*** Can't connect. ***
-------
Try again after setting NTP time (should pass)
Waiting for NTP time sync: .
Current time: Tue Aug 28 05:38:00 2018
Trying: api.github.com:443...*** Can't connect. ***
-------
pm open,type:2 0

@marcelstoer
Copy link
Contributor

@artua thank you for a great performance testing blueprint, much appreciated! While investigating https://stackoverflow.com/q/52143894 I found this issue and tinkered with your sketch.

It's not the scope of this issue but I can confirm the devastating (performance) findings for BearSSL. I was only testing insecure SSL:

  1. axTLS: median 368ms, average 390ms
  2. BearSSL 80MHz: 888ms, 891ms -> over 500ms increase (140% extra)
  3. BearSSL 160MHz: 568ms, 538ms -> 50% extra

=> no-go for battery powered devices

Side note: BearSSL is also consuming considerably more heap.

@devyte
Copy link
Collaborator

devyte commented Sep 3, 2018

@marcelstoer @artua please open a new issue to discuss axtls vs. bearssl performance, and add the perf numbers shown here.

@marcelstoer
Copy link
Contributor

Yep, wanted to do that anyhow.

@earlephilhower
Copy link
Collaborator

Interesting notes, @marcelstoer! Please make sure both are using the exact same handshake and encryption methods, as BearSSL often negotiates a more secure but slower running cypher. You can do so by limiting the receiving end's connection capabilities in your SSL configuration.

@d-a-v
Copy link
Collaborator

d-a-v commented Sep 3, 2018

@earlephilhower Does "larger bandwidth" make a difference ?

@artua
Copy link
Author

artua commented Sep 5, 2018 via email

@earlephilhower
Copy link
Collaborator

@d-a-v Are you suggesting BW tests, too? Sounds like a good idea. But it's not really actionable (nor is the connect time) as I'm simply calling the BearSSL internal code to do the real work.

Also, @marcelstoer, it may look like BearSSL is using more heap as it allocates everything all at once. But, in reality, axTLS does lots of heap allocations while it's working on a function call. So your getHeapFree() before and after a write() may say report only ~20KB is used, but in reality there may be 4-5KB (I don't know exactly, just examples here) of heap allocation/deallocation going on during the write(). That's the cause of a bunch of crashes w/axTLS, it does not handle OOM conditions (well or at all).

@d-a-v
Copy link
Collaborator

d-a-v commented Sep 5, 2018

@earlephilhower No, only wondering if you checked the impact of MSS.

@JeanVanDenderFlume
Copy link

Please check the examples. You have not set any CAs, nor have you asked it to be insecure (i.e. SSL connect but w/o checking certificates). client.setInsecure() will do the latter, before any connection.

Added #4739 to track adding some debug output beyond the GetLastError API.

Do you know or could you say if the line is secure encrypted if you use setInsecure to a backend with a not expited certificate? I understand setInsecure will not validate the certtifcate at the backend but is it correct the data cannot be read by anybody? Second how do i read the attributes from the backend certificate, i would like to check the common name and domeinname.

@JiriBilek
Copy link
Contributor

JiriBilek commented Apr 18, 2020

As to your first question: without checking a certificate the communication is vulnerable to MITM attack. It is encrypted, obviously, but the MITM is able to decrypt it.

Edit:
And the second question: checking only the CN and domain name is totally insecure. The attacker can make himself a self signed certificate with the required CN and domain name.

Let me add that TLS without checking the certificate (the setInsecure option) is secure against eavesdroppers though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.
Projects
None yet
Development

No branches or pull requests

10 participants