Close

An incredibly weird issue with ESP32 SSL

A project log for Internet Connected Boot Dryer

Modifying a Boot Dryer to be controllable via the internet!

skellyskelly 06/09/2019 at 05:031 Comment

I write this in the hopes that if anyone else has this problem, they might stumble upon this post (or the StackOverflow question I made, or the GitHub issue I created...) to help them since it's so bizarre and incredibly hard to tell what the problem is.

My situation is as such: I have my Mosquitto instance secured with a LetsEncrypt certificate. I want to connect my ESP32 to this instance, so I need the CA root certificate to send along using `WiFiSecureClient`. There are two options.

  1. Hardcode the certificate into the microcontroller code.
  2. Use SPIFFS to put the certificate file on the ESP32 and load it dynamically.

I had done the first route successfully, but I really dislike hardcoding things, so I wanted to do the latter.

This ended up being much more difficult than I assumed it would be.

My original code was as such:

if(!SPIFFS.begin(true)) {
  Serial.println("Error mounting SPIFFS.");
}

File file = SPIFFS.open("/root.cer");
  
if(!file) {
  Serial.println("Error opening the file.");
}
    
String ca_cert = file.readString();
    
Serial.println("CA Root certificate: ");
Serial.print(ca_cert);
  
espClient.setCACert(ca_cert.c_str());
file.close();

Seems simple enough.

Nope.

It didn't work.

I performed every check in the book to see if the hardcoded certificate and the one loaded as a file were the same thing, and they were. Every single equality test I could think of returned that they were, in fact, the exact same string.

However, there's one little note in the Arduino documentation for .c_str():

Note that this gives direct access to the internal String buffer and should be used with care.

Now normally "used with care" applies to situations where you are modifying this string buffer, or trying to use it after the string has been destroyed, etc. Apparently this warning applies to this case as well.

For some reason, using the following code fixed things:

char *dest;
  
dest = (char *)malloc(sizeof(char) * (ca_cert.length()+1));
strcpy(dest, ca_cert.c_str());
  
espClient.setCACert(dest);
 

 I have no idea why this works, unless for some reason setCACert tries to modify the pointer it's provided.

But hey, it works. And I can finally stop ripping my hair out over this.

Discussions

Ken Yap wrote 06/09/2019 at 05:59 point

As the doco says it's unsafe to assume that the String will not use a different string buffer later so the best thing is to make a copy of it right away. You might prefer the C++ copy routines as in the example here as that will do automatic storage reclamation when the new String goes out of scope.

http://www.cplusplus.com/reference/string/string/c_str/

On thinking about it this is probably what happens:

* You hand setCACert() a pointer to the cert.

* It saves the pointer somewhere.

* The original String goes out of scope (is it function scope?) and the storage is reclaimed and probably overwritten by some other data.

* espClient tries to use the string at the pointer it saved. It's garbage.

I suspect if the ca_cert String had program lifetime (outermost scope) this would not happen.

  Are you sure? yes | no