Skip to content
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

BearSSL validation issues #6128

Closed
1 task
lmarmisa opened this issue May 22, 2019 · 2 comments
Closed
1 task

BearSSL validation issues #6128

lmarmisa opened this issue May 22, 2019 · 2 comments
Assignees
Labels
waiting for feedback Waiting on additional info. If it's not received, the issue may be closed.

Comments

@lmarmisa
Copy link

lmarmisa commented May 22, 2019

Basic Infos

  • [ X] This issue complies with the issue POLICY doc.
  • [X ] I have read the documentation at readthedocs and the issue is not addressed there.
  • [X ] I have tested that the issue is present in current master branch (aka latest git).
  • [X ] I have searched the issue tracker for a similar issue.
  • If there is a stack dump, I have decoded it.
  • [X ] I have filled out all fields below.

Platform

  • Hardware: [ESP-12]
  • Core Version: [2.5.2]
  • Development Env: [Arduino IDE]
  • Operating System: [Ubuntu]

Settings in IDE

  • Module: [Nodemcu]
  • Flash Mode: [qio]
  • Flash Size: [4MB]
  • lwip Variant: [v2 Lower Memory]
  • Reset Method: [none]
  • Flash Frequency: [40Mhz]
  • CPU Frequency: [160MHz]
  • Upload Using: [SERIAL]
  • Upload Speed: [115200] (serial upload only)

Problem Description

I wish to use BearSSL (TLS) for secure socket connection to servers (for example, a MQTT broker running on a RPi). I suppose a domestic LAN infrastructure based on a cheap ISP router (so, no DNS server is available for LAN hostnames).

I would like to select the best solution for that scenario. I wish server & client authentication and, of course, strong encryption with a high security level immune to MITM attacks.

I have considered three options for server authentication: 1 setTrustAnchors(), 2 setKnownKey() and 3 setFingerprint().

The option 1 setTrustAnchors() seems very promising but BearSSL implementation is not very suitable for cheap IoT solutions. If CN (x509) is not equal to hostname (client->connect(hostname, port)), the connection to the server is rejected with code 56 (Expected server name was not found in the chain). This implementation is probably 100% compliant with the TLS specification, but it requires specific certs for each installation. And this is useless for many IoT solutions. Some tools like openssl s_client or python module ssl allow connections to servers without hostname verifications (using only x509 cert). I believe that such less restrictive option would be very useful for ESP8266 too and I would like to recommend to include it in a future release.

https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/bearssl-client-secure-class.html#settrustanchors-bearssl-x509list-ta

The option 2 setKnownKey() avoids the mentioned problem. According to the comments of the example BearSSL_validation.ino, the option seems secure in relation with MITM attacks too. However no chain verification is provided using this method. So we have to store several public keys if we wish to contact different servers.

https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/bearssl-client-secure-class.html#setknownkey-const-bearssl-publickey-pk

The option 3 setFingerprint() is apparently not secure enough: "The SHA-1 fingerprint of an X.509 certificate can be used to validate it instead of the whole certificate. This is not nearly as secure as real X.509 validation, but is better than nothing". I do not know if the handshake involves the use of the private key on the server side. If so, the method could be considered secure and immune to MITM attacks, IMHO. But the lack of documentation and the comment is a serious problem here. In any case, the limitations of the setTrustAnchors() method related to hostname and CN do not apply in this case. So, if the method would be secure, this will be my preferred option. But, it is really insecure this option?.

https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/bearssl-client-secure-class.html#setfingerprint-const-uint8-t-fp-20-setfingerprint-const-char-fpstr

Summary:

  1. Could be considered a less restrictive option for setTrustAnchors() allowing connections to other hostnames not matching with CN?

  2. Is it really insecure the method setFingerprint()?

  3. Could be improved the BearSSL documentation related to ESP8266?

Thanks for you help.

MCVE Sketch

// Example of the different modes of the X.509 validation options
// in the WiFiClientBearSSL object
//
// Mar 2018 by Earle F. Philhower, III
// May 2019 by Luis Marmisa
// Released to the public domain

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

#ifndef STASSID
#define STASSID "myssid"
#define STAPSK  "mysecret"
#endif

const char *ssid = STASSID;
const char *pass = STAPSK;

const char *hostname = "server";
const char *hostip   = "192.168.1.77";
const uint16_t port = 8267;

// Set time via NTP, as required for x.509 validation

void setClock() {
  configTime(0, 0, "pool.ntp.org", "time.nist.gov");

  Serial.print("Waiting for NTP time sync: ");
  time_t now = time(nullptr);
  while (now < 2 * 3600 * 2) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  Serial.println("");
  struct tm timeinfo;
  gmtime_r(&now, &timeinfo);
  Serial.print("Current time: ");
  Serial.print(asctime(&timeinfo));
}

// Try and connect using a WiFiClientBearSSL and write a text

void writeMsg(BearSSL::WiFiClientSecure *client, const char *host, const uint16_t port, const char *text) {
  Serial.printf("Trying: %s:%d...\n", host, port);
  client->connect(host, port);
  if (!client->connected()) {
    int  errorcode;
    char errortext[128];
    if (client) {
      errorcode = client->getLastSSLError(errortext, 128);
      Serial.printf("SSL error %d -> %s\n", errorcode, errortext);
      Serial.println("----- Can't connect -----");
    }
    return;
  }
  Serial.println("+++++ Connected! +++++");
  client->write(text);
  client->write("\r\n");
  client->stop();
}

void FingerprintValidation(const char *host) {
  Serial.println("1: sha-1 fingerprint validation");

  BearSSL::WiFiClientSecure client;
  static const char fp[] PROGMEM = "DB:90:C1:20:00:B0:45:19:FB:B4:01:D1:11:34:A8:7A:B0:6E:CE:1C";
  client.setFingerprint(fp);
  writeMsg(&client, host, port, "1 -> fingerprint");
}

void KnownKeyValidation(const char *host) {
  // Extracted by: openssl x509 -pubkey -noout -in servercert.pem
  static const char pubkey[] PROGMEM = R"KEY(
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu08Sl7TtqbMQYoJgAjoA
I0eUlP9iXj5MRnMX1nmJFA8t5bXUsX+hVdlqjKk+JWCOQgQjeRI/HyPm7h/MT+gg
W+JogwdGqwYMUqxOig/59/xbA6R3Y2533QQjv6ukURUKXcCPcagVS1CHVC6bjO8T
EIQbdVHbO2ghP7Lf82nGhCN0Ve8yMyRqWG5joX+t5UXiEK2zhoAjZFVqWVv2kxcc
enZNtxmdhcw/HOpCmAj+iTTfgbOkzNRHDZ1jwUj676ZswF1A6yAxdoBQJFwZ8AK5
koJg5WVNZa5XyTpKt7v5JGCj9wtB1axXW83jT0kgLFfjyrpxMkdhEODCwO02Ws/I
NQIDAQAB
-----END PUBLIC KEY-----
)KEY";

  Serial.println("2: Public key validation");

  BearSSL::WiFiClientSecure client;
  BearSSL::PublicKey key(pubkey);
  client.setKnownKey(&key);
  writeMsg(&client, host, port, "2 -> public key");
}

void ServerCertValidation(const char *host) {

  static const char server_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIICnjCCAYYCCQCpQhZsdVjqPjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdj
dWNhX2NhMB4XDTE5MDUyMjA4MDkwMFoXDTMwMDUwNDA4MDkwMFowEDEOMAwGA1UE
AwwFamF2ZWEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC7TxKXtO2p
sxBigmACOgAjR5SU/2JePkxGcxfWeYkUDy3ltdSxf6FV2WqMqT4lYI5CBCN5Ej8f
I+buH8xP6CBb4miDB0arBgxSrE6KD/n3/FsDpHdjbnfdBCO/q6RRFQpdwI9xqBVL
UIdULpuM7xMQhBt1Uds7aCE/st/zacaEI3RV7zIzJGpYbmOhf63lReIQrbOGgCNk
VWpZW/aTFxx6dk23GZ2FzD8c6kKYCP6JNN+Bs6TM1EcNnWPBSPrvpmzAXUDrIDF2
gFAkXBnwArmSgmDlZU1lrlfJOkq3u/kkYKP3C0HVrFdbzeNPSSAsV+PKunEyR2EQ
4MLA7TZaz8g1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAEXsr+e3D7gmrjt+GyrO
WXo2y7vxp5tW4P0jfHymMVycEYR5bBW9Tg5Q31e4yqWTmhki+IBTKvNlJVu9CcBN
odrGi6eda6a+wY4rUR1oOxjbCHcid7yxz+H8cOGEdnplXNaS/UpJfM5lwVULpRZf
r68Zs/zRF5twQdP11GwMMRWvIsdIfEK8Er3BRDjdzdDLIBSQDC82iEXOkabVIXey
LhMtdiH6z8EeiAoz6XF/MTlL+T1m/qYDGzAqUqvqqR5hj8iRFfyfE+RnIz3koROx
l1TiD4zAfWCQKEyo1JIae9/qEzWnyIE6TmTLrebt8dMN9maNylU3v6x4c9XBc/XP
ooo=
-----END CERTIFICATE-----
)EOF";

  Serial.println("3: Server cert validation");
  
  BearSSL::WiFiClientSecure client;
  BearSSL::X509List cert(server_cert);
  client.setTrustAnchors(&cert);
  writeMsg(&client, host, port, "3 -> server_cert");
}

void SecurityChainValidation(const char *host) {

  static const char ca_cert[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIICtzCCAZ+gAwIBAgIJAPKVT+1I8DbHMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV
BAMMB2N1Y2FfY2EwHhcNMTkwNTIxMjAyNzAxWhcNMzAwODA3MjAyNzAxWjASMRAw
DgYDVQQDDAdjdWNhX2NhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA
rQDBuVPT89reR4MCkxJG4jryCbJnPmNZR4SKtWONQ1S2MGBsbteQrDHLOQ/QBL7R
zb/zmOtEuBmfFe0glOz0lza573WttbKlneZ1GjRS934l5Oi+KqzgF1JeA+XaZo4A
ZaMBkYrtUrX/MZA7Eww0JprYIAN+b/vQwVsmKOsi/wUG3Jpe8sKoSsZHXt4uVOK5
DSCM31wEVSYVzVyQyNUCYyZ5RmBXNwcFARqygLwRbOgOsnNNBAX6FgIHi9WtpzLe
G0ikQ+mwRMpm9rb0cpFKOVIq/ALIiN3dD7PllGIAEZqCDqGujXCIabmanvDIxAvk
0V2Zz2HkK2eWSu9+9s0SXQIDAQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3
DQEBCwUAA4IBAQAbuPMlLtG/GkNQ/l1DbtotlZ6pdLlSN7SiJMqMfbAFoyHS6Wv6
YkhLSvKiB8Ys3EqaJJWPl9eRm2407WwMfJp805fglZULFcNeBo0nus87EFjCzJyn
NcUFgHSw2U0/2WgBylsugz389/2/qUCM5WsfMPu7FIkG335AXzocX+dUIh69xEsJ
vRe6EjKltYoDKARlKnXMwWIDHwbVFpKUOeHlMT/gag303Q/u20EqiSIUx4g5FIYe
qIMNJnHOSpmyynIZzFK706gmiRWUDb3dFVW5L/qGPl2i09JzWZH/Eb0rikFzM5bD
0svfc0CPfqlzgPXX6WF4H3k4338ZiyT/a7BI
-----END CERTIFICATE-----
)EOF";

  Serial.println("4: CA cert validation");

  BearSSL::WiFiClientSecure client;
  BearSSL::X509List cert(ca_cert);
  client.setTrustAnchors(&cert);
  writeMsg(&client, host, port, "4 -> CA");
}

void setup() {
  Serial.begin(115200);
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, pass);
  while (WiFi.status() != WL_CONNECTED) delay(500);
  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());
  Serial.println("");
  setClock();
  
  FingerprintValidation(hostname);
  FingerprintValidation(hostip);
  
  KnownKeyValidation(hostname);
  KnownKeyValidation(hostip);

  ServerCertValidation(hostname);
  ServerCertValidation(hostip);

  SecurityChainValidation(hostname);
  SecurityChainValidation(hostip);
}

void loop() {
  // Nothing to do here
}

### Debug Messages

1: sha-1 fingerprint validation
Trying: server:8267...
+++++ Connected! +++++
1: sha-1 fingerprint validation
Trying: 192.168.1.77:8267...
+++++ Connected! +++++
2: Public key validation
Trying: server:8267...
+++++ Connected! +++++
2: Public key validation
Trying: 192.168.1.77:8267...
+++++ Connected! +++++
3: Server cert validation
Trying: server:8267...
+++++ Connected! +++++
3: Server cert validation
Trying: 192.168.1.77:8267...
SSL error 56 -> Expected server name was not found in the chain.
----- Can't connect -----
4: CA cert validation
Trying: server:8267...
+++++ Connected! +++++
4: CA cert validation
Trying: 192.168.1.77:8267...
SSL error 56 -> Expected server name was not found in the chain.
----- Can't connect -----


@earlephilhower
Copy link
Collaborator

The comments and what you choose to do all depend on your threat model. If you're worried about your neighbor seeing your traffic, even setInsecure is probably good enough. If you're worried about political or industrial espionage, even the default validation may not be enough for you.

Could be considered a less restrictive option for setTrustAnchors() allowing connections to other hostnames not matching with CN?

This is a BearSSL thing. @pornin is absolutely focused on security and allowing hostnames that don't match the common name seems like it throws away the actual validation work he's doing. You could drop him a line (www.bearssl.org) and see what his thoughts are. You can also put in a pull against my port of his library her on github with some hacks.

Is it really insecure the method setFingerprint()?

It's less secure than a cert validation. It's just checking the hash of the cert matches a known value, vs. actually parsing and validating it (i.e. is it still expired?, is this signed by who I think it is? it it even a valid certificate or just random data with the same hash?). You could in theory generate another cert with the same fingerprint and have no way of telling using this.

The absolute "I just want to encrypt" is setInsecure() which checks absolutely nothing about the peer.

Could be improved the BearSSL documentation related to ESP8266?

PRs are always welcome, but in this case the docs are correct. You are not validating the peer.

@earlephilhower earlephilhower self-assigned this May 22, 2019
@earlephilhower earlephilhower added the waiting for feedback Waiting on additional info. If it's not received, the issue may be closed. label May 22, 2019
@devyte
Copy link
Collaborator

devyte commented May 22, 2019

This is a "how do I..." type question.
Closing due to not an issue.

@devyte devyte closed this as completed May 22, 2019
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

3 participants