MQTT-TLS could use Amazon IoT

latest MQTT-TLS using mbedTLS 2.7.5, as you pointed it’s easy to customize:)

@hirotakaster I just checked your mbedtls includes in the mqtt-tls lib on github to see how it is different from what I am using. I noticed that in config.h the

#define MBEDTLS_SSL_OUT_CONTENT_LEN
#define MBEDTLS_SSL_IN_CONTENT_LEN

defines are missing (among other details). The mbedtls versioning is a little unclear to me, on the site it is referred to as 2.13, 2.7.6, 2.1.1.5 (I assume there are individual parts that are versioned) so I just checked your library for date and it states in the top comment "Copyright (C) 2006-2015, ARM Limited, All Rights Reserved", whereas the 2.12 lib I am using lists 2006-2018.

My point is mainly that with the library version that operates with the defines I listed above, one can lower the RAM footprint which in our small particle world is a big deal. Perhaps you did this in another way and I missed it?

I’m curious as to where your RAM footprints are landing with this client? I’ve used the WolfSSL TLS 1.2 client in past Particle iterations for AWS-IoT endpoints and wondered how this stacked up in comparison??

OUT_CONTENT_LEN, IN_CONTENT_LEN can use on 2.13.x version.
You could change the MBEDTLS_SSL_MAX_CONTENT_LEN on 2.7.x.

I can’t decide about the which mbedTLS version could good for this library and developers in 2.8.x/2.7.x/2.1.x version released.
But 2.8.x-2.13.x is released for latest features(IN/OUT_CONTENT_LEN…etc) version, 2.7.x/2.1.x become maintenance release version in this year.

Here is mbedTLS release comments.
Mbed TLS 2.7.6 and 2.1.15 are maintenance releases, and intentionally do not contain new features to avoid changing the library interface and allow users to change library versions easily.

So I will update MQTT-TLS/TlsTcpClient mbedTLS version to 2.13(or 2.14?) in sometime soon

@hirotakaster, re-reading the MBEDTLS release notes and your comments I understand now that 2.7.x and 2.1.x are previous versions of the library that have seen security upgrades but no feature upgrades (pretty messy to new comers).

So I upgraded MQTT-TLS to 2.12 a few months ago with minimal effort, it was really a small change with respect to a time() call if I remember correctly. It did give me the possibility to change the buffer sizes which was important in my app which uses lots of RAM for data analysis. So my input/output buffer sizes are 4K/2K which works well with AWS IoT. I also tested pretty much all options in the config.h files for the minimal set needed to connect to AWS.

Still tricky is the startup of the library during which it consumes much more memory and depletes the stack rather rapidly. But I am not going to tinker with MBEDTLS itself outside the config.h file (though my fingers are tickling to do it!). It will be good to see you upgrade to 2.13, thanks again for the nice MQTT library!

1 Like

I can second this, I had to move a lot of static variables to **const** to reduce memory footprint to get a connection/start.

eg.

const int syncTimeRate = 60*60;

Currently this allows it to boot and results in System.freeMemory() returning a value of: 48832
I was finding anything lower than 35-40k was causing instability.

I'm also using a lot of Strings (@ScruffR would say that is a no no and I should use Chars, but finding it difficult to swap between chars and strings when dealing with cloud return variables). I think the memory foot print changes quite a lot as the code goes through different functions/timed events.

image

You can almost always work with char* and never touch String.
If you have a specific example, I may be able to show you how.
e.g.

// docs for Particle.function() state the callback needs to look like
int someFn(String args);
// but this works just the same
int someFn(const char* args);
2 Likes

So I was able to reduce the RAM footprint of the library by 12KB with two config changes. The first one is a no-brainer if you have the space in ROM, as you lose absolutely nothing.

With default config:
Free RAM 16024

In ‘lib/MQTT-TLS/src/mbedtls/config.h’:

With MBEDTLS_AES_ROM_TABLES enabled (this moves an 8KB table from RAM to ROM. This pushed my code to the ROM limit, but I was able to remove some old code and get it to work)
Free RAM 24128

Reducing MBEDTLS_SSL_MAX_CONTENT_LEN (With MBEDTLS_SSL_MAX_FRAGMENT_LENGTH enabled per default) to 6144 (4096 couldn’t connect for me). Handshake seems to work fine, was able to send ~2k payload (my max Len) over the connection with no issues. Keep in mind that this can be server dependent, so this may not apply outside of aws iot, and this may eventually not be sufficient if they change something on the server side.
Free RAM 28248

Thanks to @joost for the suggestion to mess with the buffer, and to @hirotakaster for making and maintaining this library - it’s been super helpful!

Edit: With these changes, the total approximate observed continuous RAM impact of the library is 24KB instead of 28.5KB ( though still requiring an extra 7KB dynamically during handshake per my previous post so requiring a minimum of ~30KB instead of 35KB Free RAM for a handshake ).

Edit 2: Here’s a post with some deeper optimization possibilities, though these may be risky on AWS IOT since we don’t really control the server here. Someone with more knowledge of TLS / SSL may be able use these to optimize though. https://tls.mbed.org/kb/how-to/reduce-mbedtls-memory-and-storage-footprint

4 Likes

hi guys,

Now I update MQTT-TLS/TlsTcpClient to mbedTLS 2.16.0(latest version).
It can use OUT_CONTENT_LEN/IN_CONTENT_LEN parameters in config.h.

Thank you

3 Likes

@hirotakaster Thanks for creating this library. We currently have it working with AWS IOT without issue.

1 Like

this is an awesome library and i really want to use it. can someone explain to me where i’m supposed to download my Cert files from amazon iot to? where should these be saved (in particle device im assuming?) but how do you do this?

@blksheephw, for the moment we stored the the certs in the firmware but are investing other ways of storing the certs as we scale out. Here is an example https://github.com/hirotakaster/MQTT-TLS/blob/master/examples/a1-example/a1-example.ino

FYI - A potentially easy way to reduce ROM Flash usage by 10672 Bytes when using MQTT-TLS.

in mbedtls config.h, comment out the following two lines:

#define MBEDTLS_SHA1_C
...
#define MBEDTLS_SHA512_C

MQTT-TLS seems to only use SHA256, so removing the other two hash libraries saves a ton of ROM flash space. @hirotakaster, might be worth adding this config change to your next release. There are some static function instances that are created with this define, so they don’t get compiler-optimized away, seemingly.

1 Like

I think SHA algorithm is a little hard problem. Some servers use SHA-1 fingerprint certificate, and some servers use SHA-384 fingerprint certificate. So I set SHA-[1|256|512] algorithm on MQTT-TLS.
As you comments mbedTLS can easy to change cipher algorithm on the config.h, I hope it’s better that developer change the cipher algorithm by own server&client environments.

3 Likes

Interesting, commenting out the SHA-256 algorithm results in a compile error, but neither of the other two do. Regardless, looks like I misinterpreted how those were getting called. Thanks for clarifying!

Either way, for AWS-IOT use cases specifically, removing support for SHA-1 & SHA-512/384 appears to be fine. Per their docs

To use AWS IoT certificates, clients must support all of the following in their TLS implementation:
TLS 1.2.
SHA-256 RSA certificate signature validation.
One of the cipher suites from the TLS cipher suite support section.

1 Like

Hi,

Is this still working with the latest stable firmware on Photon (1.1) ?

I am using the a2-example.ino, replacing the certificates, private key, and aws endpoint, but it doesn’t get passed ‘tls enable’ in the serial monitor - ie there is no connection to the broker.

I tried using the certificates, private key, and endpoint in a separate mqtt client, and it work s fine for both publishing and subscribing to AWS. This suggests that my AWS policy (basically wildcards for any topic) is fine, and so are my credentials.

Anyone have any suggestions please !?

thanks

I’ve yet only barely to get AWS Iot to work on the photon due to memory constraints.

That said, that Serial Print line should always get executed within a few seconds even if something fails. Either something is crashing or you aren’t connected to wifi. Try with System Thread enabled? Can you describe the state of your device in general?

Thanks for this. I have tried now with several photons and also an argon, using both the build portal and also the workbench IDE.

All of them connect to wifi (I can still flash them remotely + they have breathing cyan).

If memory constraint was an issue, I’d have thought that the Argon would have been ok ?

Here is my code
(I left the library untouched) -

#include "MQTT-TLS.h"

void callback(char* topic, byte* payload, unsigned int length);

#define AMAZON_IOT_ROOT_CA_PEM                                          \
"-----BEGIN CERTIFICATE----- \r\n"                                      \
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\r\n"  \
"ADA5MQswCQYDVQQGEwJVUzEPMA0GA1UEChMGQW1hem9uMRkwFwYDVQQDExBBbWF6\r\n"  \
"b24gUm9vdCBDQSAxMB4XDTE1MDUyNjAwMDAwMFoXDTM4MDExNzAwMDAwMFowOTEL\r\n"  \
"MAkGA1UEBhMCVVMxDzANBgNVBAoTBkFtYXpvbjEZMBcGA1UEAxMQQW1hem9uIFJv\r\n"  \
"b3QgQ0EgMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJ4gHHKeNXj\r\n"  \
"ca9HgFB0fW7Y14h29Jlo91ghYPl0hAEvrAIthtOgQ3pOsqTQNroBvo3bSMgHFzZM\r\n"  \
"9O6II8c+6zf1tRn4SWiw3te5djgdYZ6k/oI2peVKVuRF4fn9tBb6dNqcmzU5L/qw\r\n"  \
"IFAGbHrQgLKm+a/sRxmPUDgH3KKHOVj4utWp+UhnMJbulHheb4mjUcAwhmahRWa6\r\n"  \
"VOujw5H5SNz/0egwLX0tdHA114gk957EWW67c4cX8jJGKLhD+rcdqsq08p8kDi1L\r\n"  \
"93FcXmn/6pUCyziKrlA4b9v7LWIbxcceVOF34GfID5yHI9Y/QCB/IIDEgEw+OyQm\r\n"  \
"jgSubJrIqg0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\r\n"  \
"AYYwHQYDVR0OBBYEFIQYzIU07LwMlJQuCFmcx7IQTgoIMA0GCSqGSIb3DQEBCwUA\r\n"  \
"A4IBAQCY8jdaQZChGsV2USggNiMOruYou6r4lK5IpDB/G/wkjUu0yKGX9rbxenDI\r\n"  \
"U5PMCCjjmCXPI6T53iHTfIUJrU6adTrCC2qJeHZERxhlbI1Bjjt/msv0tadQ1wUs\r\n"  \
"N+gDS63pYaACbvXy8MWy7Vu33PqUXHeeE6V/Uq2V8viTO96LXFvKWlJbYK8U90vv\r\n"  \
"o/ufQJVtMVT8QtPHRh8jrdkPSHCa2XV4cdFyQzR1bldZwgJcJmApzyMZFo6IQ6XU\r\n"  \
"5MsI+yMRQ+hDKXJioaldXgjUkK642M4UwtBV8ob2xJNDd2ZhwLnoQdeXeGADbkpy\r\n"  \
"rqXRfboQnoZsG4q5WTP468SQvvG5\r\n"  \
"-----END CERTIFICATE----- "
const char amazonIoTRootCaPem[] = AMAZON_IOT_ROOT_CA_PEM;

#define CELINT_KEY_CRT_PEM                                              \
"-----BEGIN CERTIFICATE----- \r\n"                                      \
"MIIDWTCCAkGgAwIBAgIUX/FQ91puqmivOcJAZpxTj2MxA+wwDQYJKoZIhvcNAQEL\r\n"  \
...
"KZ2fGETRiC3HqU/i/ePJcJ7KOqy2aalY7aRe+RdmfeWYL/DfsHfmC/7cjbqi\r\n"  \
"-----END CERTIFICATE----- "
const char clientKeyCrtPem[] = CELINT_KEY_CRT_PEM;

#define CELINT_KEY_PEM                                                  \
"-----BEGIN RSA PRIVATE KEY-----\r\n"                                   \
"MIIEowIBAAKCAQEAz3CAXF00A2YTbDqA9ONHwEAg79A4iy3j7ZiYvkhZh74Lr6jC\r\n"  \
 ...
"lrvUaLUUdzm/bFw5+mYa9mRzQfz3DxsOD160E4iLRC5TTMNR7yxXKAzXzvTdsKRj\r\n"  \
"57m/RonRW09+OOlQOYVn504bETnwzNmMN/Gu46OoUsSY3uPJJrNC\r\n"  \
"-----END RSA PRIVATE KEY----- "
const char clientKeyPem[] = CELINT_KEY_PEM;

MQTT client("a1w6cl7csd6w46p-ats.iot.us-east-1.amazonaws.com", 8883, callback);

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;
    String message(p);

    if (message.equals("RED"))
        Serial.println("this is red");
        //RGB.color(255, 0, 0);
    else if (message.equals("GREEN"))
        RGB.color(0, 255, 0);
    else if (message.equals("BLUE"))
        RGB.color(0, 0, 255);
    else
        RGB.color(255, 255, 255);
    delay(1000);
}

#define ONE_DAY_MILLIS (24 * 60 * 60 * 1000)
unsigned long lastSync = millis();
int counter = 0;

void setup() {
    if (millis() - lastSync > ONE_DAY_MILLIS) {
        Particle.syncTime();
        lastSync = millis();
    }

    // RGB.control(true);

    // enable tls. set Root CA pem, private key file.
    client.enableTls(amazonIoTRootCaPem, sizeof(amazonIoTRootCaPem),
                     clientKeyCrtPem, sizeof(clientKeyCrtPem),
                     clientKeyPem, sizeof(clientKeyPem));
    Serial.println("tls enable");

    // connect to the server
    client.connect("testparticle");

    // publish/subscribe
    if (client.isConnected()) {
        Serial.println("client connected");
        client.publish("home/garden/fountain", "hello world");
        client.subscribe("home/garden/fountain");
    }
}

void loop() {
    if (client.isConnected()) {
        client.loop();
    }
    delay(200);
}

So a few things could be going on.

  1. Is the certificate arn associated with your private key and public cert attached to your thing? Unless you gave your device blanket permissions in an unusual fashion, your thing name should usually be the same as your clientid, testparticle. Your certificate needs to be attached and a policy with appropriate permissions also needs to be attached.

  2. Let’s triple check that you are connected to the network before attempting to connect. if (!WiFi.ready()) Serial.println("Error, wifi not connected");

  3. Make sure enabling TLS succeeds, aka the formatting of your pkey and certs are correct. Also check to see if your connect attempt succeeds and if not what the error code is.

            int ret;
            if ( (ret = client.enableTls(AUTH::amazonIoTRootCaPem, strlen(AUTH::amazonIoTRootCaPem)+1,
                             AUTH::clientKeyCrtPem, strlen(AUTH::clientKeyCrtPem)+1,
                             AUTH::clientKeyPem, strlen(AUTH::clientKeyPem)+1)) < 0) {
                             Serial.printlnf("client.enableTls failed with code: %d", ret);
            }

            if ( (ret >= 0) && ((ret = client.connect(MQTT_ID)) < 0) ) {    // if client enable tls succeeded, attempt connect.  Can block for up to 60 seconds
                Serial.printlnf("client.connect failed with code: %d", ret);
            }

If you get an error code, convert it to a signed hex value and search in the mbedtls library for it, or just post it here and I’ll tell you what it means.

2 Likes

Thanks very much for all your help. I am really struggling here…

I confirm that the certifcate arn is associated with the private key and cert. I also confirm that the cert is attached with a policy that has appropriate permissions. I am quite confident that all this works because I tested with a windows based Mqtt client and it can pub/sub to AWS IoT (using client id, CA cert, client cert, priv key, aws endpoint). I also managed to get a huzzah esp32 to connect using these credentials!

I tried your recommendations and confirm that Wifi is working and the device remains connected.

I also tried printing out the pkey and certs in serial,

Serial.println(amazonIoTRootCaPem);
Serial.println(clientKeyCrtPem);
Serial.println(clientKeyPem);

and they appear exactly as they do in the files.

Finally, I tried your suggestion of printing out the TLS connection code, but only got 0.

I am out of ideas because I know the certs and pkey work with other clients, I know the photon is connected to wifi, I have tried other photons and argons…

Would you mind if I sent you my .ino file? perhaps there is something very obvious I am doing wrong… I want to make this work so we can use to make alarms for our earthquake early-warning system !

Thank you!

1 Like