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

Uable to connect mqtt broker #63

Closed
TONYCHOU81905 opened this issue Dec 8, 2023 · 17 comments
Closed

Uable to connect mqtt broker #63

TONYCHOU81905 opened this issue Dec 8, 2023 · 17 comments
Labels
bug Something isn't working

Comments

@TONYCHOU81905
Copy link

TONYCHOU81905 commented Dec 8, 2023

Hi,
I am using A7600 with esp32 to connect AWS mqtt!
But I got the error message below:
[ 26256][E][ssl_client.cpp:728] perform_ssl_handshake(): mbedtls_ssl_get_record_expansion returned -0xffffffe3
[ 26263][E][ssl_client.cpp:45] _handle_error(): [start_ssl_client():353]: (29) UNKNOWN ERROR CODE (001D)
[ 26264][E][SSLClient.cpp:235] connect(): start_ssl_client failed: 0

This is my code

`TinyGsmClient base_client(modem, 0);
SSLClient secure_layer(&base_client);
PubSubClient mqtt(secure_layer);

while (!mqtt.connect(mqttid.c_str()))
{
mqtt.setServer(broker, 8883);
mqtt.setCallback(mqttCallback);
mqtt.setKeepAlive(60);
mqtt.setBufferSize(2048);
Serial.println("Connecting to AWS IoT...");
Serial.println("AWS2_FreeHeap : " + String(ESP.getFreeHeap()));
if (mqtt.connect(String(THINGNAME).c_str())) {
Serial.println("Connected to AWS IoT!");
Serial.println("Topic:");
Serial.println(THINGNAME);
} else {
Serial.print("Failed to connect to AWS IoT, rc=");
Serial.println(mqtt.state());
delay(2000);
}
}`

This is my certificate's format
圖片1

@RobertByrnes
Copy link
Collaborator

Hi, I don't have the sim module you are using but if you post your full code I can try it later against my aws account.

@ghost
Copy link

ghost commented Jan 9, 2024

Hi @RobertByrnes , I'm having the exact same issue.

I can get the entire pipeline to work when I'm leveraging alkonosst/SSLClientESP32, although it's incredibly slow (4s for publish, 6s for loop). But I'd like to use govorox's as it's more widely used and hopefully is faster.

I too am getting :

Initializing modem...
Modem Info: Manufacturer: SIMCOM INCORPORATED Model: SIMCOM_SIM7600G-H Revision: SIM7600G_V2.0.2 IMEI: 862636052660071 +GCAP: +CGSM
Waiting for network... Network connected
Connecting to id success
GPRS connected
GPS enabled
Connecting to AWS IOT: [ 21347][E][ssl_client.cpp:728] perform_ssl_handshake(): mbedtls_ssl_get_record_expansion returned -0xffffffe3
[ 21358][E][ssl_client.cpp:45] _handle_error(): [start_ssl_client():353]: (29) UNKNOWN ERROR CODE (001D)
[ 21358][E][SSLClient.cpp:235] connect(): start_ssl_client failed: 0
[ 33401][E][ssl_client.cpp:728] perform_ssl_handshake(): mbedtls_ssl_get_record_expansion returned -0xffffffe3
[ 33412][E][ssl_client.cpp:45] _handle_error(): [start_ssl_client():353]: (29) UNKNOWN ERROR CODE (001D)
[ 33412][E][SSLClient.cpp:235] connect(): start_ssl_client failed: 0
... still not connected

My main file is:

#include <configs/certificate.h>

// bricks
#include <bricks/SIMCOM.hpp>

// libs
#include <Arduino.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <SSLClient.h>

SSLClient secure(&SIMCOM::client);
PubSubClient pubsub(secure);

void publishMessage()
{
    StaticJsonDocument<200> doc;
    doc["humidity"] = "hello there!";
    doc["temperature"] = 123;
    char jsonBuffer[256];
    serializeJson(doc, jsonBuffer); // print to pubsub
    pubsub.publish(AWS_IOT_PUBLISH_TOPIC, jsonBuffer);
}

void messageHandler(char *topic, byte *payload, unsigned int length)
{
    Serial.print("incoming: ");
    Serial.println(topic);

    StaticJsonDocument<200> doc;
    deserializeJson(doc, payload);
    const char *message = doc["message"];
    Serial.println(message);
}

void setup()
{
    Serial.begin(115200);
    
    secure.setCACert(AWS_CERT_CA);
    secure.setCertificate(AWS_CERT_CRT);
    secure.setPrivateKey(AWS_CERT_PRIVATE);

    Serial.println("Started...");
    SIMCOM::init();

    // Connect to the MQTT broker on the AWS endpoint we defined earlier
    pubsub.setServer(AWS_IOT_ENDPOINT, 8883);

    // Create a message handler
    pubsub.setCallback(messageHandler);

    Serial.print("Connecting to AWS IOT: ");

    if (pubsub.connect(THINGNAME)) {
        // Subscribe to a topic

        Serial.println("AWS IoT Connected!");
    } else {
        while (!pubsub.connected()) {
            pubsub.connect(THINGNAME);
            Serial.println("... still not connected");
        }
    }
    pubsub.subscribe(AWS_IOT_SUBSCRIBE_TOPIC, MQTTQOS1);
}

void loop()
{
    publishMessage();
    pubsub.loop();
    delay(1000);
}

and, yep this works with alkonosst/SSLClientESP32, so I know the URL, port, and certs are okay...
Certs are in the format of:

static const char AWS_CERT_CA[] PROGMEM = 
"-----BEGIN CERTIFICATE-----\r\n"
"MIIDQTCCAimgAwIBAgITBmyfz5m/jAo54vB4ikPmljZbyjANBgkqhkiG9w0BAQsF\r\n"
...
"-----END CERTIFICATE-----\r\n";


static const char AWS_CERT_CRT[] PROGMEM =
"-----BEGIN CERTIFICATE-----""\n"
"MIIDWjCCAkKgAwIBAgIVAKPQbOtfpI4/8pcCYPJVZ8/pN2zSMA0GCSqGSIb3DQEB""\n"
...
"-----END CERTIFICATE-----";

static const char AWS_CERT_PRIVATE[] PROGMEM =
"-----BEGIN RSA PRIVATE KEY-----""\n"
"MIIEpgIBAAKCAQEA10ESwd+HUTP8y1aQVcApc4Bjk9DQ+UejnNMrqtfZD5zoDKKI""\n"
...
"-----END RSA PRIVATE KEY-----";

My SIMCOM init is just a wrapper around the normal SIMCOM initializers (I'm using TinyGSM on a SIM7600G)

...
    TinyGsm modem(SerialAT);
    TinyGsmClient client(modem, 0);
...
    /**
     * @brief Initialize the SIMCOM module against the configs, and connect to network.
     *
     * @returns true if successful, otherwise false
     */
    bool init()
    {
        SerialAT.begin(AT_BAUD, SERIAL_8N1, UART_RX, UART_TX);

        delay(6000);

        // This can take quite some time
        Serial.println("Initializing modem... ");
        while (!modem.init())
        {
        }

        String modemInfo = modem.getModemInfo();
        Serial.print("Modem Info: ");
        Serial.println(modemInfo);

        Serial.print("Waiting for network... ");
        if (!modem.waitForNetwork())
        {
            Serial.println(" fail");
            return false;
        }

        if (modem.isNetworkConnected())
        {
            Serial.println("Network connected");
        }

        // Enable full functionality
        modem.setPhoneFunctionality(1);

        // Turn on the GPRS
        Serial.print(F("Connecting to "));
        Serial.print(APN_4G);
        if (!modem.gprsConnect(APN_4G))
        {
            Serial.println(" fail");
            return false;
        }
        Serial.println(" success");
        if (modem.isGprsConnected())
        {
            Serial.println("GPRS connected");
        }

        // Update the internal clock (good for SSL)
        modem.NTPServerSync();

        // Turn on the GPS
        modem.disableGPS();
        delay(500);
        if (modem.enableGPS())
        {
            Serial.println("GPS enabled");
        }
        return true;
    }

Have you got any ideas?
I was also trying to build a TinyGsmClientSecure client for SIM7600G in TinyGsm, but I was getting a few errors on MQTT (worked wonderfully on HTTPS), so I thought I'd try an SSL layer first instead...

Edit:
I'm on an ESP32 S3 R3 (the Pico ESP32 board from Waveshare. This has worked wonders for a range of https related stuff with tinygsm+sim7600!)

@WebDust21
Copy link

WebDust21 commented Feb 18, 2024

So I'm not completely alone in experiencing literally the exact same error as posted in the very beginning of this issue, namely:

[  6623][V][ssl_client.cpp:722] perform_ssl_handshake(): Protocol is TLSv1.2 Ciphersuite is TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384
[  6634][E][ssl_client.cpp:728] perform_ssl_handshake(): mbedtls_ssl_get_record_expansion returned -0xffffffe3
....a lot of connection closing messages...
[  6737][E][ssl_client.cpp:45] _handle_error(): [start_ssl_client():353]: (29) UNKNOWN ERROR CODE (001D)
[  6746][V][SSLClient.cpp:232] connect(): Return value from start_ssl_client: 0
[  6753][E][SSLClient.cpp:235] connect(): start_ssl_client failed: 0
[  6759][D][SSLClient.cpp:90] stop(): Stopping ssl client

It appears to be directly caused by use of a client certificate/key, as the server logs indicate the SSL CA is complete by this point. Heck, the function failing is literally the one that uses the client cert/key!

int perform_ssl_handshake(sslclient_context *ssl_client, const char *cli_cert, const char *cli_key) {
if (ssl_client == nullptr) {
log_e("Uninitialised context!");
return -1;
}
int ret = 0;
bool breakBothLoops = false;
log_v("Performing the SSL/TLS handshake, timeout %lu ms", ssl_client->handshake_timeout);
unsigned long handshake_start_time = millis();
log_d("calling mbedtls_ssl_handshake with ssl_ctx address %p", (void *)&ssl_client->ssl_ctx);
while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
break;
}
if ((millis()-handshake_start_time) > ssl_client->handshake_timeout) {
log_e("SSL handshake timeout");
breakBothLoops = true;
break;
}
vTaskDelay(10 / portTICK_PERIOD_MS);
}
if (breakBothLoops) {
return -1;
}
if (cli_cert != NULL && cli_key != NULL) {
log_v("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx));
ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx);
if (ret != 0) {
if (ret == MBEDTLS_ERR_SSL_FEATURE_UNAVAILABLE) {
log_w("Record expansion is not available (compression)");
} else {
log_e("mbedtls_ssl_get_record_expansion returned -0x%x", -ret);
}
} else {
log_w("Record expansion is unknown (compression)");
}
}
return ret;
}

It fails on both WiFiClient and TinyGsmClient (as I've made my code able to switch between the two at will) exactly the same.

So far, ArduinoBearSSL also fails.

And yes, after running dangerously low on hair, I did finally try SSLClientESP32 whose successful connection was the most beautiful sight I've seen in over two weeks at this point. I literally just imported the library, changed the #include and the define, and compiled the project--to success.

I don't understand the difference, as the SSLClientESP32 repo was forked from SSLClient here. Both use mbedTLS under the hood.

The only potential clue I see is the commonality with the only other SSL library that has worked: the default WiFiClientSecure on the Arduino-ESP32 core. Apart from both using mbedTLS, both also come with certificate packs. I sincerely hope there isn't anything to that--because if so, then the certs we provide are being ignored or are insufficient in some way. I shall have to disable the cert packs in that library and see if success turns into failure. For science.

@WebDust21
Copy link

WebDust21 commented Feb 18, 2024

Whoa, wait a minute....!

A significant amount of code is missing in the above "perform_ssl_handshake" routine.
Notice the "cli_cert" and "cli_key" parameters? Apart from a null-check at line 721, they are not used anywhere in the routine!

if (cli_cert != NULL && cli_key != NULL) {

In the (working) SSLClientESP32, there are numerous search results for "cli_cert", including mbedTLS conversion and processing routines, as seen here:

https://github.com/alkonosst/SSLClientESP32/blob/7906f386a8f7db88ae89824c1fe058cef62869e2/src/ssl_lib_client.cpp#L256-L278

I have to conclude that the client cert/key functionality was never tested here with SSLClient; the code here required to support said functionality is completely missing!

@WebDust21
Copy link

OK, so looking closer...maybe everything's not missing.
The client cert/key conversion routines are further up, and present:

int auth_client_cert_key(sslclient_context *ssl_client, const char *cli_cert, const char *cli_key, bool *client_cert_initialized, bool *client_key_initialized) {
int ret = 0;
// Step 4 route b - Set up required auth mode cli_cert and cli_key
if (cli_cert != NULL && cli_key != NULL) {
mbedtls_x509_crt_init(&ssl_client->client_cert);
mbedtls_pk_init(&ssl_client->client_key);
log_v("Loading CRT cert");
ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1);
if (ret != 0) {
// if ret > 0 n certs failed, ret < 0 pem or x509 error code.
return ret;
} else {
*client_cert_initialized = true;
}
log_v("Loading private key");
ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0);
if (ret != 0) { // PK or PEM non-zero error codes
mbedtls_x509_crt_free(&ssl_client->client_cert); // cert+key are free'd in pair
return ret;
} else {
*client_key_initialized = true;
}
ret = mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key);
}
return ret;
}

But something else is missing/wrong, as the SSLClientESP32 uses the same "mbedtls_ssl_get_record_expansion" call that fails here in SSLClient--but it doesn't fail there.
And the "perform_ssl_handshake" is apparently only using the "cli_cert" and "cli_key" to indicate whether they were used? It doesn't make any sense; SSLClientESP32 uses flags for this (to determine whether to free the memory consumed by converting the certs).

I'm not much help with this SSL stuff--it's gargantuan complicated. But something's definitely wrong here.

@WebDust21
Copy link

@Matt-Stedman-HardStuff

I can get the entire pipeline to work when I'm leveraging alkonosst/SSLClientESP32, although it's incredibly slow (4s for publish, 6s for loop). But I'd like to use govorox's as it's more widely used and hopefully is faster.

I am suspecting part of the problem here to be how the libraries interact with each other. I'm similarly stacking an SSLClient on top of TinyGSM, but utilizing the StreamDebugger to see what's going on.
For whatever reason, all of the SSL libraries I've thus far tried read data from the modem through an eyedropper--almost literally. Regardless of the amount of data available to read from the modem (i.e. 1460 bytes), they read it 63 bytes at a time, incurring a horrendous speed penalty by way of the transport methodology.

I'm running the modem serial at 115200, but the above problem results in molasses speeds. (Just to read the SSL handshake in takes ~30 seconds, and then the server times out and closes the connection.) Gonna have to figure out where this nonsensical bottleneck is, and fix it...!
Over WiFi, the SSL handshake takes ~2 seconds with SSLClientESP32.

Oh, and a size comparison...SSLClientESP32 is definitely the biggest of all of the ones so far. Size of compressed firmware with the entire rest of my application stack in it, so the size is not as important as the difference:

  • ~550KB: no SSL Stack
  • 647.3KB: ArduinoBearSSL
  • 650.6KB: WiFiClientSecure (NOT usable with TinyGSM)
  • 661.2KB: SSLClientESP32

@deeja
Copy link

deeja commented Feb 21, 2024

@WebDust21 @TONYCHOU81905 @Matt-Stedman-HardStuff - digitaldragon/[email protected] works for me, and it also fixed a buffer issue that was present in previous versions.

@RobertByrnes - I get the same issue as above for digitaldragon/[email protected]

[ 33274][E][ssl_client.cpp:728] perform_ssl_handshake(): mbedtls_ssl_get_record_expansion returned -0xffffffe3
[ 33282][E][ssl_client.cpp:45] _handle_error(): [start_ssl_client():353]: (29) UNKNOWN ERROR CODE (001D)
[ 33282][E][SSLClient.cpp:235] connect(): start_ssl_client failed: 0

@deeja
Copy link

deeja commented Feb 21, 2024

@WebDust21 - I use this library with AWS IoT and TinyGSM as it was the least painful one. I haven't tested the speed though as I'm only using this for low speed IoT communications

@WebDust21
Copy link

@WebDust21 - I use this library with AWS IoT and TinyGSM as it was the least painful one. I haven't tested the speed though as I'm only using this for low speed IoT communications

My findings with alkonosst/SSLClientESP32@^2.0.3: it actually leverages the mbedTLS implementation provided in the Arduino-ESP32 core, instead of bringing an entirely separate one in. That ends up not helping the total binary size, as it's still the largest of all of the available options...but it's the simplest wrapper.

The main huge speed bottleneck I encountered (initial testing was >30 seconds for SSL handshake--which would fail because the server would abort the connection) was due to the default TinyGSM buffer size of 64 bytes. When you're transferring 4KB+ for the SSL handshake, it takes an extremely long time to do it 63 bytes at a time.

After setting the TinyGSM default buffer to 1436 bytes (i.e. a typical MTU) with

-DTINY_GSM_RX_BUFFER=1436

...I promptly ran into another issue: the default Arduino-ESP32 serial FIFO buffer size of 256 bytes, resulting in most of the data being lost. (Plus a TinyGSM bug that would cause it to wait for nonexistent data for close to 2 minutes at a time.) Setting the serial FIFO buffer size to 1436 bytes * 1.2 (for some extra headroom)...

Serial.setRxBufferSize(TINY_GSM_RX_BUFFER * 1.2);

...solved the issue; now the SSL handshake takes ~5 seconds tops.

@deeja Very good info to know...now I want to diff the two versions and see what changed to break it!

@RobertByrnes
Copy link
Collaborator

This is great info in this thread! Haven't had a lot of time recently but feeling a pulling it all together update very soon.

Please consider adding PR for any changes - even if it is just in the README.md to document best practice based on your findings.

Cheers....

@deeja @WebDust21 @TONYCHOU81905 @Matt-Stedman-HardStuff

@deeja
Copy link

deeja commented Feb 22, 2024

@WebDust21 Fair enough. This is roughly how I wrapped my call.

#define TINY_GSM_RX_BUFFER 1024
TinyGsmClient networkClient(modem);
SSLClient sslClient(&networkClient);
PubSubClient mqttClient(AWS_IOT_ENDPOINT, AWS_IOT_PORT, provisionCallback, &sslClient, Serial);
// Wrapper
SSLClient sslClient(&networkClient); 
sslClient->setCACert(AWS_CA_CERTIFICATE);
sslClient->setCertificate(AWS_CERTIFICATE);
sslClient->setPrivateKey(AWS_PRIVATEKEY);

@deeja
Copy link

deeja commented Feb 22, 2024

Interestingly I am getting this in 1.1.16 but the connection still works :

[ 29129][E][ssl_client.cpp:431] start_ssl_client():  mbedtls_ssl_get_record_expansion returned -0xffffffe3
[ 29129][I][SSLClient.cpp:241] connect(): SSL connection established

@ghost
Copy link

ghost commented Feb 23, 2024

Just a note to everyone that might be trying to get SSL working via a SIM7600 and leveraging TinyGSM, @floBik and I (though mostly @floBik) managed to leverage the SIM7600's in-built SSL functionality, which seems to work for HTTPS and MQTTS.

Currently the PR is still awaiting approval, but you can reference or fork via https://github.com/Matt-Stedman-HardStuff/TinyGSM

This means you can use the SIMCOM module and SSL without needing this govorox/SSLClient (sorry @govorox 😂)

@deeja
Copy link

deeja commented Feb 23, 2024

@Matt-Stedman-HardStuff - I'll keep an eye on that PR

@WebDust21
Copy link

Just a note to everyone that might be trying to get SSL working via a SIM7600 and leveraging TinyGSM, @floBik and I (though mostly @floBik) managed to leverage the SIM7600's in-built SSL functionality, which seems to work for HTTPS and MQTTS.

Currently the PR is still awaiting approval, but you can reference or fork via https://github.com/Matt-Stedman-HardStuff/TinyGSM

This means you can use the SIMCOM module and SSL without needing this govorox/SSLClient (sorry @govorox 😂)

Problem for me is that the SIM7080 modules I have, cannot successfully complete an SSL connection over either SSL TCP or MQTT. (The latter is unsurprising, as it runs over a TCP connection.)
I'm also using TinyGSM--but I've also pulled it into the project as a direct import, and have made quite a few modifications to TinyGSM. (Including other modifications, I cobbled in client cert/key functionality.) The modem's SSL functionality works to a part, as it is is able to connect to "easy" servers (with client cert/key), but not the one server I need it to. (Which most ESP32 SSL libraries fail to connect to as well.)

@RobertByrnes
Copy link
Collaborator

#78 @deeja @WebDust21 @TONYCHOU81905 Please see this PR and the comments in issue #71 This may now be fixed

@RobertByrnes RobertByrnes added the bug Something isn't working label Apr 29, 2024
@RobertByrnes RobertByrnes mentioned this issue May 1, 2024
@RobertByrnes
Copy link
Collaborator

closed as solved by v1.2.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants