ESP32 arduino x509 format is invalid loading from SPIFFS - aws-iot

I'm trying to fetch 3 keys from SPIFFS that I stored from a string to a file (when fetched from an API endpoint).
I am able to read the files using the SPIFFS library and print the values out in the serial console. When I check the contents, they are just fine. When I use the contents in the secureClient.setCACert() function, they "break" and throw an exception; E][ssl_client.cpp:36] _handle_error(): [start_ssl_client():138]: (-8576) X509 - The CRT/CRL/CSR format is invalid, e.g. different type expected
My code looks like the following:
#include <WiFiClientSecure.h>
#include <FS.h>
#include <SPIFFS.h>
WiFiClientSecure net = WiFiClientSecure();
String read2String(const char * path) {
String output = "";
File file = SPIFFS.open(path, "r");
if (!file || file.isDirectory()) {
return "";
}
while (file.available()) {
char c = file.read();
output.concat(c);
}
file.close();
return output;
}
void readFilesToVariables() {
String awsRootCa = read2String("/AmazonRootCA1.pem");
String privateKey = read2String("/private.pem.key");
String certificate = read2String("/certificate.pem.crt");
net.setCACert(awsRootCa.c_str());
net.setCertificate(certificate.c_str());
net.setPrivateKey(privateKey.c_str());
}
void setup() {
Serial.begin(115200);
SPIFFS.begin();
readFilesToVariables();
mqttClient.begin(AWS_IOT_ENDPOINT, AWS_IOT_ENDPOINT_PORT, net); // crashes here (I think)
...
}
I have on the mqttClient.begin(...) line (the mqtt client and all the variables except for the net var) a couple of variables that are defined in my script but not shared here.
Anyone any idea what I'm doing wrong here?
the exact same code works when I define the certificates and private key using the following methodology:
static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
certificate_content_here
-----END CERTIFICATE-----
)EOF";
What am I doing wrong?

The same thing drove me crazy for the last few days.
Then I found a solution, but only supposed the problem.
I think that this issue has something to do with memory handling and where and how you store the certificates after you read it from flash.
Here's how I read the certificates. Using calloc I allocate the memory and never free it, so that the certificate will always be available to the MQTT client
char* getFileAsString(String path) {
File file = SPIFFS.open(path);
if (!file) {
Serial.println("Failed to open " + path);
return NULL;
}
char* buffer = (char*)calloc(file.size(), sizeof(char));
file.readBytes(buffer, file.size());
file.close();
return buffer;
}
Then I just use it in the config struct
const esp_mqtt_client_config_t mqttConf = {
.uri = MQTT_BROKER,
.client_id = MQTT_CLIENT_ID,
.cert_pem = getFileAsString("/caFile"),
.client_cert_pem = getFileAsString("/certificateFile"),
.client_key_pem = getFileAsString("/privateFile"),
};
Note: I did't put the *_len attributes, because I stored my certificates with a leadinig null character:
dd if=/dev/zero bs=1 count=1 >> [my cert name].pem
You can use regular certificates, but you must set the certificates length or adding 1 byte to the calloc instruction (calloc will set all the allocated memory to 0x00, so there will be a leading null character):
char* buffer = (char*)calloc(file.size() + 1, sizeof(char));

Related

I have problem showing a float variable in an Arduino webserver

I am using Arduino Uno, ENC28j60. using rbbb_server example.
the library I am using: <EtherCard.h>
as can be seen in the code below I am trying to convert my Float var to Str using dtostrf() in the homepage function since emit_p() can not accept float vars. although Serial.println(temp1) is working correctly, but in the browser, I have a problem showing the variable(pic added). I've been looking for a solution but couldn't find any, unfortunately. would appreciate any suggestion.
#include <EtherCard.h>
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
static byte myip[] = { 192,168,1,203 };
byte Ethernet::buffer[500];
BufferFiller bfill;
static word homePage() {
String temp1;
float val = 311.322;
char s1[10];
temp1 = dtostrf(val,3,2,s1);
bfill = ether.tcpOffset();
bfill.emit_p(PSTR(
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/html\r\n"
"Pragma: no-cache\r\n"
"\r\n"
"<meta http-equiv='refresh' content='1'/>"
"<title>RBBB server</title>"
"<h1>my val:$S</h1>"),
temp1);
Serial.println(temp1);
return bfill.position();
}
void setup () {
Serial.begin(57600);
if (ether.begin(sizeof Ethernet::buffer, mymac, SS) == 0)
Serial.println(F("Failed to access Ethernet controller"));
ether.staticSetup(myip);
}
void loop () {
word len = ether.packetReceive();
word pos = ether.packetLoop(len);
if (pos)
ether.httpServerReply(homePage());
}
output in browser
I think I see your problem, but I can't test it.
dtostrf returns a char * pointer to the string you want to print here's the docs
It looks like that function is being called and assigning that string to the temp variable which is a String type (idk your experience, but working with text in Arduino can be very confusing check out this thread ).
Try changing temp1 to be a char * like this:
char * temp1;

Using function inside a call back in different function

I'm working on USB CDC on ATSAMD21. The code which i'm using is ATMEL START example for USB CD Echo on D21. I'm working on atmel studio.
Requirement:
In my application the host send data to the device and i need to read that data and send back different data to the host from different function.
Here in echo example the data reception and transfer is using the call back. I'm not aware how to use a write call inside another function. Here i'm attaching the code below
Read & Echo the data:
Here in this function it is for read the data and echo it back to the host.
static bool usb_device_cb_state_c(usb_cdc_control_signal_t state)
{
if (state.rs232.DTR ) {
/* Callbacks must be registered after endpoint allocation */
cdcdf_acm_register_callback(CDCDF_ACM_CB_READ, (FUNC_PTR)usb_device_cb_bulk_out);
cdcdf_acm_register_callback(CDCDF_ACM_CB_WRITE, (FUNC_PTR)usb_device_cb_bulk_in);
/* Start Rx */
cdcdf_acm_read((uint8_t *)usbd_cdc_buffer, sizeof(usbd_cdc_buffer));
}
/* No error. */
return false;
}
/////////////////////////////////////
Read the data from host:
static bool usb_device_cb_bulk_out(const uint8_t ep, const enum
usb_xfer_code rc, const uint32_t count)
{
cdcdf_acm_write((uint8_t *)usbd_cdc_buffer, count);
return false;
}
Write back the data:
static bool usb_device_cb_bulk_in(const uint8_t ep, const enum usb_xfer_code rc, const uint32_t count)
{
/* Echo data. */
cdcdf_acm_read((uint8_t *)usbd_cdc_buffer, sizeof(usbd_cdc_buffer));
/* No error. */
return false;
}
I need to use this read call inside another function outside. I used the read call directly in another function, it was not able to send the data.
How can i make this call use in another function.Any help will be appreciated.
Here the callback will direct to the read and write functions.
This is a fun one!
From the main loop, using a stack allocated char buffer:
const char hello[] = "Hi There!\r\n";
cdcdf_acm_write((void*)hello, 11);
works for me, but curiously using a pointer to a string constant:
const char* hello = "Hi There!\r\n";
cdcdf_acm_write((void*)hello, 11);
does not output anything! Similarly, a static const buffer
const char hello[] = "Hi There!\r\n";
int main(void)
{
// init ...
while (1)
{
delay_ms(200);
cdcdf_acm_write((void*)hello, 11);
}
}
doesn't work either, but a non-const static buffer:
char hello[] = "Hi There!\r\n";
int main(void)
{
// init ...
while (1)
{
delay_ms(200);
cdcdf_acm_write((void*)hello, 11);
}
}
does!
My hypothesis is that cdcdf_acm_write() has a problem somewhere in the USB stack that cannot read data straight from flash, which seems like a very unexpected pitfall, especially as outputting a string constant is probably the first thing one does while implementing the communication functionality!

Write Cert to DER

I'm trying to write a X509 Cert to DER format in memory.
Writing it to a file works perfectly.
I need the Cert in PEM format without the "-----BEGIN PRIVATE KEY-----" header, footer or newlines. I can't figure out how to do it directly so...
I'm outputting to der and base64 encoding.
THIS WORKS.
int X509_to_DER_file(X509 *cert) {
int res=0;
out = BIO_new(BIO_s_file());
if (NULL != out) {
if(BIO_write_filename(out, "my.der") > 0) {
res = i2d_X509_bio(out, cert);
}
BIO_free_all(out);
}
return (tres);
}
THIS DOES NOT.
It returns and mallocs the correct number of bytes and appears to write out to memory correctly but the resulting string is incorrect (the first 15 or so positions are correct).
char *X509_to_DER_mem(X509 *cert) {
char *der = NULL;
bio = BIO_new(BIO_s_mem());
if (NULL != bio) {
//load cert into bio
if (0 == i2d_X509_bio(bio, cert)) {
BIO_flush(bio);
BIO_free(bio);
return NULL;
}
der = (char *) malloc(bio->num_write + 1);
if (NULL == der) {
BIO_free(bio);
return NULL;
}
memset(der, 0, bio->num_write + 1);
BIO_read(bio, der, bio->num_write);
// Appears to work put "der" is incomplete.
BIO_free(bio);
}
return der;
}
It returns and mallocs the correct number of bytes and appears to
write out to memory correctly but the resulting string is incorrect
The result of i2d_X509_bio() is not a (zero-terminated) string, but a bunch of bytes. If you try to write it to a file as a string, it might look incomplete because you might encounter a 0-byte at a location before you reach the end. So in addition to the char * result, your function X509_to_DER_mem() will have to return the number of bytes that make up the result.
With regard to the memory BIO, another way of obtaining its data is with the BIO_get_mem_data() function. Something like this:
char *ptr = NULL;
long len = BIO_get_mem_data(bio, &ptr);
der = malloc(len);
memcpy(der, ptr, len);
Finally, your actual question is
I need the Cert in PEM format without the "-----BEGIN PRIVATE
KEY-----" header, footer or newlines.
Writing the certificate in DER format does not seem to give you what you need. This answer to another SO question explains how you could use the function PEM_read_bio() in combination with EVP_EncodeBlock() for that purpose.

Regarding FOPEN in C

I am having a problem regarding FOPEN in C.
I have this code which reads a particular file from a directory
FILE *ifp ;
char directoryname[50];
char result[100];
char *rpath = "/home/kamal/samples/pipe26/divpipe0.f00001";
char *mode = "r";
ifp = fopen("director.in",mode); %director file contains path of directory
while (fscanf(ifp, "%s", directoname) != EOF)
{
strcpy(result,directoname); /* Path of diretory /home/kamal/samples/pipe26 */
strcat(result,"/"); /* front slash for path */
strcat(result,name); /* name of the file divpipe0.f00001*/
}
Till this point my code works perfectly creating a string which looks " /home/kamal/samples/pipe26/divpipe0.f00001 ".
The problem arises when I try to use the 'result' to open a file, It gives me error. Instead if I use 'rpath' it works fine even though both strings contain same information.
if (!(fp=fopen(rpath,"rb"))) /* This one works fine */
{
printf(fopen failure2!\n");
return;
}
if (!(fp=fopen(result,"rb"))) /* This does not work */
{
printf(fopen failure2!\n");
return;
}
Could some one please tell why I am getting this error ?
I think you mean char result[100];; i.e. without the asterisk. (Ditto for directoryname.)
You're currently stack-allocating an array of 100 pointers. This will not end well.
Note that rpath and mode point to read-only memory. Really you should use const char* for those two literals.
The error is the array 'char* result[100]', here you are allocating an array of 100 pointers to strings, not 100 bytes / characters, which was your intent.

Splitting a comma-delimited string of integers

My background is not in C (it's in Real Studio - similar to VB) and I'm really struggling to split a comma-delimited string since I'm not used to low-level string handling.
I'm sending strings to an Arduino over serial. These strings are commands in a certain format. For instance:
#20,2000,5!
#10,423,0!
'#' is the header indicating a new command and '!' is the terminating footer marking the end of a command. The first integer after '#' is the command id and the remaining integers are data (the number of integers passed as data may be anywhere from 0 - 10 integers).
I've written a sketch that gets the command (stripped of the '#' and '!') and calls a function called handleCommand() when there is a command to handle. The problem is, I really don't know how to split this command up to handle it!
Here's the sketch code:
String command; // a string to hold the incoming command
boolean commandReceived = false; // whether the command has been received in full
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop() {
// main loop
handleCommand();
}
void serialEvent(){
while (Serial.available()) {
// all we do is construct the incoming command to be handled in the main loop
// get the incoming byte from the serial stream
char incomingByte = (char)Serial.read();
if (incomingByte == '!')
{
// marks the end of a command
commandReceived = true;
return;
}
else if (incomingByte == '#')
{
// marks the start of a new command
command = "";
commandReceived = false;
return;
}
else
{
command += incomingByte;
return;
}
}
}
void handleCommand() {
if (!commandReceived) return; // no command to handle
// variables to hold the command id and the command data
int id;
int data[9];
// NOT SURE WHAT TO DO HERE!!
// flag that we've handled the command
commandReceived = false;
}
Say my PC sends the Arduino the string "#20,2000,5!". My sketch ends up with a String variable (called command) that contains "20,2000,5" and the commandRecieved boolean variable is set to True so the handleCommand() function is called.
What I would like to do in the (currently useless) handleCommand() function is assign 20 to a variable called id and 2000 and 5 to an array of integers called data, i.e: data[0] = 2000, data[1] = 5, etc.
I've read about strtok() and atoi() but frankly I just can't get my head around them and the concept of pointers. I'm sure my Arduino sketch could be optimised too.
Since you're using the Arduino core String type, strtok and other string.h functions aren't appropriate. Note that you can change your code to use standard C null-terminated strings instead, but using Arduino String will let you do this without using pointers.
The String type gives you indexOf and substring.
Assuming a String with the # and ! stripped off, finding your command and arguments would look something like this:
// given: String command
int data[MAX_ARGS];
int numArgs = 0;
int beginIdx = 0;
int idx = command.indexOf(",");
String arg;
char charBuffer[16];
while (idx != -1)
{
arg = command.substring(beginIdx, idx);
arg.toCharArray(charBuffer, 16);
// add error handling for atoi:
data[numArgs++] = atoi(charBuffer);
beginIdx = idx + 1;
idx = command.indexOf(",", beginIdx);
}
data[numArgs++] = command.substring(beginIdx);
This will give you your entire command in the data array, including the command number at data[0], while you've specified that only the args should be in data. But the necessary changes are minor.
seems to work, could be buggy:
#include<stdio.h>
#include <string.h>
int main(){
char string[]="20,2000,5";
int a,b,c;
sscanf(string,"%i,%i,%i",&a,&b,&c);
printf("%i %i %i\n",a,b,c);
a=b=c=0;
a=atoi(strtok(string,","));
b=atoi(strtok(0,","));
c=atoi(strtok(0,","));
printf("%i %i %i\n",a,b,c);
return 0;
}

Resources