Added a MQTT pub/sub example of using AWS IoT (#173)

* Added a MQTT pub/sub example of using AWS IoT (via ECC based TLS1.2 connection).

* Fixed a buffer overflow issue when receiving large MQTT packet.

* Reset TLS connection on read/write errors.
This commit is contained in:
rongsaws 2016-08-29 10:55:32 -07:00 committed by Johan Kanflo
parent c9851e9253
commit 7041c014bb
9 changed files with 725 additions and 2 deletions

View file

@ -0,0 +1,3 @@
PROGRAM=aws_iot
EXTRA_COMPONENTS = extras/paho_mqtt_c extras/mbedtls
include ../../common.mk

View file

@ -0,0 +1,60 @@
Please follow the steps below to build and run the example on your ESP8266.
1. Modify client_config.c to provide your own account-specific AWS IoT
endpoint, ECC-based client certificate, and private key.
Your endpoint is in the form of ```<prefix>.iot.<region>.amazonaws.com```.
It can be retrieved using the following command:
```sh
$ aws iot describe-endpoint
```
Your ECC-based certificate and private key can be generated by using
the following commands:
```sh
$ openssl ecparam -out ecckey.key -name prime256v1 -genkey
$ openssl req -new -sha256 -key ecckey.key -nodes -out eccCsr.csr
$ aws iot create-certificate-from-csr --certificate-signing-request file://eccCsr.csr --certificate-pem-outfile eccCert.crt --set-as-active
```
To convert the certificate or key file into C string, you could try
the following example:
```sh
$ cat ecckey.key | sed -e 's/^/"/g' | sed -e 's/$/\\r\\n"/g'
```
*Note, more information about using ECC-based certificate with AWS IoT
can be found in the following blog*
https://aws.amazon.com/blogs/iot/elliptic-curve-cryptography-and-forward-secrecy-support-in-aws-iot-3/
2. Create and attach AWS IoT access policy to the certificate
```sh
$ aws iot create-policy --policy-name test-thing-policy --policy-document '{ "Version": "2012-10-17", "Statement": [{"Action": ["iot:*"], "Resource": ["*"], "Effect": "Allow" }] }'
$ aws iot attach-principal-policy --policy-name test-thing-policy --principal "arn:aws:iot:eu-west-1:892804553548:cert/2d9c2da32a95b5e95a277c3b8f7af40869727f5259dc2e907fc8aba916c857e"
```
*Note, the 'principal' argument is the certificate ARN generated from the
pervious command 'aws iot create-certificate-from-csr'.*
3. Modify include/ssid_config.h with your Wifi access Id and credential.
4. Build and flash the example firmware to the device using the command
below:
```sh
$ make flash -C examples/aws_iot ESPPORT=/dev/ttyUSB0
```
*Note, it assumes your ESP8266 is connected through USB and exposed under
your Linux host as /dev/ttyUSB0.*
5. Once the ESP8266 is connected to AWS IoT, you can use the MQTT client
on the AWS IoT console to receive the messages published by the ESP8266
to topic 'esp8266/status'. You could also publish 'on' or 'off' message
to topic 'esp8266/control' to toggle the GPIO/LED (GPIO2 is used by the
example).

280
examples/aws_iot/aws_iot.c Normal file
View file

@ -0,0 +1,280 @@
/*
* Derived from examples/mqtt_client/mqtt_client.c - added TLS1.2 support and some minor modifications.
*/
#include "espressif/esp_common.h"
#include "esp/uart.h"
#include <string.h>
#include <FreeRTOS.h>
#include <task.h>
#include <queue.h>
#include <ssid_config.h>
#include <espressif/esp_sta.h>
#include <espressif/esp_wifi.h>
#include <paho_mqtt_c/MQTTESP8266.h>
#include <paho_mqtt_c/MQTTClient.h>
// this must be ahead of any mbedtls header files so the local mbedtls/config.h can be properly referenced
#include "ssl_connection.h"
#define MQTT_PUB_TOPIC "esp8266/status"
#define MQTT_SUB_TOPIC "esp8266/control"
#define GPIO_LED 2
#define MQTT_PORT 8883
/* certs, key, and endpoint */
extern char *ca_cert, *client_endpoint, *client_cert, *client_key;
static int wifi_alive = 0;
static int ssl_reset;
static SSLConnection *ssl_conn;
static xQueueHandle publish_queue;
static void beat_task(void *pvParameters) {
char msg[16];
int count = 0;
while (1) {
if (!wifi_alive) {
vTaskDelay(1000 / portTICK_RATE_MS);
continue;
}
printf("Schedule to publish\r\n");
snprintf(msg, sizeof(msg), "%d", count);
if (xQueueSend(publish_queue, (void *) msg, 0) == pdFALSE) {
printf("Publish queue overflow\r\n");
}
vTaskDelay(10000 / portTICK_RATE_MS);
}
}
static void topic_received(MessageData *md) {
MQTTMessage *message = md->message;
int i;
printf("Received: ");
for (i = 0; i < md->topic->lenstring.len; ++i)
printf("%c", md->topic->lenstring.data[i]);
printf(" = ");
for (i = 0; i < (int) message->payloadlen; ++i)
printf("%c", ((char *) (message->payload))[i]);
printf("\r\n");
if (!strncmp(message->payload, "on", 2)) {
printf("Turning on LED\r\n");
gpio_write(GPIO_LED, 0);
} else if (!strncmp(message->payload, "off", 3)) {
printf("Turning off LED\r\n");
gpio_write(GPIO_LED, 1);
}
}
static const char *get_my_id(void) {
// Use MAC address for Station as unique ID
static char my_id[13];
static bool my_id_done = false;
int8_t i;
uint8_t x;
if (my_id_done)
return my_id;
if (!sdk_wifi_get_macaddr(STATION_IF, (uint8_t *) my_id))
return NULL;
for (i = 5; i >= 0; --i) {
x = my_id[i] & 0x0F;
if (x > 9)
x += 7;
my_id[i * 2 + 1] = x + '0';
x = my_id[i] >> 4;
if (x > 9)
x += 7;
my_id[i * 2] = x + '0';
}
my_id[12] = '\0';
my_id_done = true;
return my_id;
}
static int mqtt_ssl_read(Network* n, unsigned char* buffer, int len,
int timeout_ms) {
int r = ssl_read(ssl_conn, buffer, len, timeout_ms);
if (r <= 0
&& (r != MBEDTLS_ERR_SSL_WANT_READ
&& r != MBEDTLS_ERR_SSL_WANT_WRITE
&& r != MBEDTLS_ERR_SSL_TIMEOUT)) {
printf("%s: TLS read error (%d), resetting\n\r", __func__, r);
ssl_reset = 1;
};
return r;
}
static int mqtt_ssl_write(Network* n, unsigned char* buffer, int len,
int timeout_ms) {
int r = ssl_write(ssl_conn, buffer, len, timeout_ms);
if (r <= 0
&& (r != MBEDTLS_ERR_SSL_WANT_READ
&& r != MBEDTLS_ERR_SSL_WANT_WRITE)) {
printf("%s: TLS write error (%d), resetting\n\r", __func__, r);
ssl_reset = 1;
}
return r;
}
static void mqtt_task(void *pvParameters) {
int ret = 0;
struct Network network;
MQTTClient client = DefaultClient;
char mqtt_client_id[20];
uint8_t mqtt_buf[100];
uint8_t mqtt_readbuf[100];
MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
memset(mqtt_client_id, 0, sizeof(mqtt_client_id));
strcpy(mqtt_client_id, "ESP-");
strcat(mqtt_client_id, get_my_id());
ssl_conn = (SSLConnection *) malloc(sizeof(SSLConnection));
while (1) {
if (!wifi_alive) {
vTaskDelay(1000 / portTICK_RATE_MS);
continue;
}
printf("%s: started\n\r", __func__);
ssl_reset = 0;
ssl_init(ssl_conn);
ssl_conn->ca_cert_str = ca_cert;
ssl_conn->client_cert_str = client_cert;
ssl_conn->client_key_str = client_key;
NewNetwork(&network);
network.mqttread = mqtt_ssl_read;
network.mqttwrite = mqtt_ssl_write;
printf("%s: connecting to MQTT server %s ... ", __func__,
client_endpoint);
ret = ssl_connect(ssl_conn, client_endpoint, MQTT_PORT);
if (ret) {
printf("error: %d\n\r", ret);
ssl_destroy(ssl_conn);
continue;
}
printf("done\n\r");
NewMQTTClient(&client, &network, 5000, mqtt_buf, 100, mqtt_readbuf,
100);
data.willFlag = 0;
data.MQTTVersion = 4;
data.cleansession = 1;
data.clientID.cstring = mqtt_client_id;
data.username.cstring = NULL;
data.password.cstring = NULL;
data.keepAliveInterval = 1000;
printf("Send MQTT connect ... ");
ret = MQTTConnect(&client, &data);
if (ret) {
printf("error: %d\n\r", ret);
ssl_destroy(ssl_conn);
continue;
}
printf("done\r\n");
MQTTSubscribe(&client, MQTT_SUB_TOPIC, QOS1, topic_received);
xQueueReset(publish_queue);
while (wifi_alive && !ssl_reset) {
char msg[64];
while (xQueueReceive(publish_queue, (void *) msg, 0) == pdTRUE) {
portTickType task_tick = xTaskGetTickCount();
uint32_t free_heap = xPortGetFreeHeapSize();
uint32_t free_stack = uxTaskGetStackHighWaterMark(NULL);
snprintf(msg, sizeof(msg), "%u: free heap %u, free stack %u",
task_tick, free_heap, free_stack * 4);
printf("Publishing: %s\r\n", msg);
MQTTMessage message;
message.payload = msg;
message.payloadlen = strlen(msg);
message.dup = 0;
message.qos = QOS1;
message.retained = 0;
ret = MQTTPublish(&client, MQTT_PUB_TOPIC, &message);
if (ret != SUCCESS) {
printf("error while publishing message: %d\n", ret);
break;
}
}
ret = MQTTYield(&client, 1000);
if (ret == DISCONNECTED)
break;
}
printf("Connection dropped, request restart\n\r");
ssl_destroy(ssl_conn);
}
}
static void wifi_task(void *pvParameters) {
uint8_t status = 0;
uint8_t retries = 30;
struct sdk_station_config config = { .ssid = WIFI_SSID, .password =
WIFI_PASS, };
printf("%s: Connecting to WiFi\n\r", __func__);
sdk_wifi_set_opmode (STATION_MODE);
sdk_wifi_station_set_config(&config);
while (1) {
wifi_alive = 0;
while ((status != STATION_GOT_IP) && (retries)) {
status = sdk_wifi_station_get_connect_status();
printf("%s: status = %d\n\r", __func__, status);
if (status == STATION_WRONG_PASSWORD) {
printf("WiFi: wrong password\n\r");
break;
} else if (status == STATION_NO_AP_FOUND) {
printf("WiFi: AP not found\n\r");
break;
} else if (status == STATION_CONNECT_FAIL) {
printf("WiFi: connection failed\r\n");
break;
}
vTaskDelay(1000 / portTICK_RATE_MS);
--retries;
}
while ((status = sdk_wifi_station_get_connect_status())
== STATION_GOT_IP) {
if (wifi_alive == 0) {
printf("WiFi: Connected\n\r");
wifi_alive = 1;
}
vTaskDelay(500 / portTICK_RATE_MS);
}
wifi_alive = 0;
printf("WiFi: disconnected\n\r");
vTaskDelay(1000 / portTICK_RATE_MS);
}
}
void user_init(void) {
uart_set_baud(0, 115200);
printf("SDK version: %s, free heap %u\n", sdk_system_get_sdk_version(),
xPortGetFreeHeapSize());
gpio_enable(GPIO_LED, GPIO_OUTPUT);
gpio_write(GPIO_LED, 1);
publish_queue = xQueueCreate(3, 16);
xTaskCreate(&wifi_task, (int8_t *) "wifi_task", 256, NULL, 2, NULL);
xTaskCreate(&beat_task, (int8_t *) "beat_task", 256, NULL, 2, NULL);
xTaskCreate(&mqtt_task, (int8_t *) "mqtt_task", 2048, NULL, 2, NULL);
}

View file

@ -0,0 +1,29 @@
// trusted root CA certificate - https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem
const char *ca_cert = "-----BEGIN CERTIFICATE-----\r\n"
"MIIE0zCCA7ugAwIBAgIQGNrRniZ96LtKIVjNzGs7SjANBgkqhkiG9w0BAQUFADCB\r\n"
"yjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL\r\n"
"ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJp\r\n"
"U2lnbiwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxW\r\n"
"ZXJpU2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0\r\n"
"aG9yaXR5IC0gRzUwHhcNMDYxMTA4MDAwMDAwWhcNMzYwNzE2MjM1OTU5WjCByjEL\r\n"
"MAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQLExZW\r\n"
"ZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTowOAYDVQQLEzEoYykgMjAwNiBWZXJpU2ln\r\n"
"biwgSW5jLiAtIEZvciBhdXRob3JpemVkIHVzZSBvbmx5MUUwQwYDVQQDEzxWZXJp\r\n"
"U2lnbiBDbGFzcyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9y\r\n"
"aXR5IC0gRzUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCvJAgIKXo1\r\n"
"nmAMqudLO07cfLw8RRy7K+D+KQL5VwijZIUVJ/XxrcgxiV0i6CqqpkKzj/i5Vbex\r\n"
"t0uz/o9+B1fs70PbZmIVYc9gDaTY3vjgw2IIPVQT60nKWVSFJuUrjxuf6/WhkcIz\r\n"
"SdhDY2pSS9KP6HBRTdGJaXvHcPaz3BJ023tdS1bTlr8Vd6Gw9KIl8q8ckmcY5fQG\r\n"
"BO+QueQA5N06tRn/Arr0PO7gi+s3i+z016zy9vA9r911kTMZHRxAy3QkGSGT2RT+\r\n"
"rCpSx4/VBEnkjWNHiDxpg8v+R70rfk/Fla4OndTRQ8Bnc+MUCH7lP59zuDMKz10/\r\n"
"NIeWiu5T6CUVAgMBAAGjgbIwga8wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8E\r\n"
"BAMCAQYwbQYIKwYBBQUHAQwEYTBfoV2gWzBZMFcwVRYJaW1hZ2UvZ2lmMCEwHzAH\r\n"
"BgUrDgMCGgQUj+XTGoasjY5rw8+AatRIGCx7GS4wJRYjaHR0cDovL2xvZ28udmVy\r\n"
"aXNpZ24uY29tL3ZzbG9nby5naWYwHQYDVR0OBBYEFH/TZafC3ey78DAJ80M5+gKv\r\n"
"MzEzMA0GCSqGSIb3DQEBBQUAA4IBAQCTJEowX2LP2BqYLz3q3JktvXf2pXkiOOzE\r\n"
"p6B4Eq1iDkVwZMXnl2YtmAl+X6/WzChl8gGqCBpH3vn5fJJaCGkgDdk+bW48DW7Y\r\n"
"5gaRQBi5+MHt39tBquCWIMnNZBU4gcmU7qKEKQsTb47bDN0lAtukixlE0kF6BWlK\r\n"
"WE9gyn6CagsCqiUXObXbf+eEZSqVir2G3l6BFoMtEMze/aiCKm0oHw0LxOXnGiYZ\r\n"
"4fQRbxC1lfznQgUy286dUV4otp6F01vvpX1FQHKOtw5rDgb7MzVIcbidJ4vEZV8N\r\n"
"hnacRHr2lVz2XTIIM6RUthg/aFzyQkqFOFSDX9HoLPKsEdao7WNq\r\n"
"-----END CERTIFICATE-----\r\n";

View file

@ -0,0 +1,17 @@
// AWS IoT client endpoint
const char *client_endpoint = "<your-prefix>.iot.<aws-region>.amazonaws.com";
// AWS IoT device certificate (ECC)
const char *client_cert =
"-----BEGIN CERTIFICATE-----\r\n"
"------------------ <your client certificate> -------------------\r\n"
"-----END CERTIFICATE-----\r\n";
// AWS IoT device private key (ECC)
const char *client_key =
"-----BEGIN EC PARAMETERS-----\r\n"
"BggqhkjOPQMBBw==\r\n"
"-----END EC PARAMETERS-----\r\n"
"-----BEGIN EC PRIVATE KEY-----\r\n"
"------------------ <your client private key> -------------------\r\n"
"-----END EC PRIVATE KEY-----\r\n";

View file

@ -0,0 +1,119 @@
/**
* \file config.h
*
* \brief Configuration options (set of defines)
*
* Copyright (C) 2006-2015, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* This file is part of mbed TLS (https://tls.mbed.org)
*/
/*
* This set of compile-time options may be used to enable
* or disable features selectively, and reduce the global
* memory footprint.
*/
#ifndef MBEDTLS_CONFIG_H
#define MBEDTLS_CONFIG_H
/* System support */
#define MBEDTLS_HAVE_ASM
#define MBEDTLS_DEPRECATED_WARNING
#define MBEDTLS_ENTROPY_HARDWARE_ALT
#define MBEDTLS_NO_PLATFORM_ENTROPY
/* mbed TLS feature support */
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_SSL_PROTO_TLS1_2
/* Debug support (optional) */
// #define MBEDTLS_ERROR_C
// #define MBEDTLS_DEBUG_C
/* mbed TLS modules */
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_ASN1_WRITE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_GCM_C
#define MBEDTLS_MD_C
#define MBEDTLS_NET_C
#define MBEDTLS_OID_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
/* For verify RSA based root CA certificate (optional if root CA cert is signed using ECC) */
#define MBEDTLS_RSA_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_PKCS1_V15
#define MBEDTLS_PKCS1_V21
/* For test certificates */
#define MBEDTLS_BASE64_C
#define MBEDTLS_CERTS_C
#define MBEDTLS_PEM_PARSE_C
/* Save RAM at the expense of ROM */
#define MBEDTLS_AES_ROM_TABLES
/* Save RAM by adjusting to our exact needs */
#define MBEDTLS_ECP_MAX_BITS 384
#define MBEDTLS_MPI_MAX_SIZE 256 // 2048 bits
/* Save RAM at the expense of speed, see ecp.h */
#define MBEDTLS_ECP_WINDOW_SIZE 2
#define MBEDTLS_ECP_FIXED_POINT_OPTIM 0
/* Significant speed benefit at the expense of some ROM */
#define MBEDTLS_ECP_NIST_OPTIM
/*
* You should adjust this to the exact number of sources you're using: default
* is the "mbedtls_platform_entropy_poll" source, but you may want to add other ones.
* Minimum is 2 for the entropy test suite.
*/
#define MBEDTLS_ENTROPY_MAX_SOURCES 2
/* Save ROM and a few bytes of RAM by specifying our own ciphersuite list */
#define MBEDTLS_SSL_CIPHERSUITES \
MBEDTLS_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
/*
* Save RAM at the expense of interoperability: do this only if you control
* both ends of the connection! (See coments in "mbedtls/ssl.h".)
* The minimum size here depends on the certificate chain used as well as the
* typical size of records.
*/
#define MBEDTLS_SSL_MAX_CONTENT_LEN 4096
#include "mbedtls/check_config.h"
#endif /* MBEDTLS_CONFIG_H */

View file

@ -0,0 +1,170 @@
#include <espressif/esp_common.h>
#include <lwip/sockets.h>
#include <lwip/inet.h>
#include <lwip/netdb.h>
#include <lwip/sys.h>
#include <string.h>
// this must be ahead of any mbedtls header files so the local mbedtls/config.h can be properly referenced
#include "ssl_connection.h"
#define SSL_READ_TIMEOUT_MS 2000
const char *pers = "esp-tls";
static int handle_error(int err) {
#ifdef MBEDTLS_ERROR_C
char error_buf[100];
mbedtls_strerror(err, error_buf, 100);
printf("%s\n", error_buf);
#endif
printf("Error: %d\n", err);
return err;
}
#ifdef MBEDTLS_DEBUG_C
static void my_debug(void *ctx, int level, const char *file, int line,
const char *str) {
((void) level);
fprintf((FILE *) ctx, "%s:%04d: %s", file, line, str);
fflush((FILE *) ctx);
}
#endif
void ssl_init(SSLConnection* conn) {
/*
* Initialize the RNG and the session data
*/
mbedtls_net_init(&conn->net_ctx);
mbedtls_ssl_init(&conn->ssl_ctx);
mbedtls_ssl_config_init(&conn->ssl_conf);
mbedtls_x509_crt_init(&conn->ca_cert);
mbedtls_x509_crt_init(&conn->client_cert);
mbedtls_pk_init(&conn->client_key);
mbedtls_ctr_drbg_init(&conn->drbg_ctx);
mbedtls_entropy_init(&conn->entropy_ctx);
}
int ssl_connect(SSLConnection* conn, const char* host, int port) {
int ret;
char buffer[8];
ret = mbedtls_ctr_drbg_seed(&conn->drbg_ctx, mbedtls_entropy_func,
&conn->entropy_ctx, (const unsigned char *) pers, strlen(pers));
if (ret < 0) {
return -1;
}
ret = mbedtls_x509_crt_parse(&conn->ca_cert,
(const unsigned char *) conn->ca_cert_str,
strlen(conn->ca_cert_str) + 1);
if (ret < 0) {
return handle_error(ret);
}
ret = mbedtls_x509_crt_parse(&conn->client_cert,
(const unsigned char *) conn->client_cert_str,
strlen(conn->client_cert_str) + 1);
if (ret < 0) {
return handle_error(ret);
}
ret = mbedtls_pk_parse_key(&conn->client_key,
(const unsigned char *) conn->client_key_str,
strlen(conn->client_key_str) + 1, NULL, 0);
if (ret != 0) {
return handle_error(ret);
}
snprintf(buffer, sizeof(buffer), "%d", port);
ret = mbedtls_net_connect(&conn->net_ctx, host, buffer,
MBEDTLS_NET_PROTO_TCP);
if (ret != 0) {
return handle_error(ret);
}
ret = mbedtls_ssl_config_defaults(&conn->ssl_conf, MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
if (ret != 0) {
return handle_error(ret);
}
#ifdef MBEDTLS_DEBUG_C
mbedtls_ssl_conf_dbg(&conn->ssl_conf, my_debug, stdout);
mbedtls_debug_set_threshold(5);
#endif
mbedtls_ssl_conf_authmode(&conn->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_rng(&conn->ssl_conf, mbedtls_ctr_drbg_random,
&conn->drbg_ctx);
mbedtls_ssl_conf_read_timeout(&conn->ssl_conf, SSL_READ_TIMEOUT_MS);
mbedtls_ssl_conf_ca_chain(&conn->ssl_conf, &conn->ca_cert, NULL);
ret = mbedtls_ssl_conf_own_cert(&conn->ssl_conf, &conn->client_cert,
&conn->client_key);
if (ret != 0) {
return handle_error(ret);
}
ret = mbedtls_ssl_setup(&conn->ssl_ctx, &conn->ssl_conf);
if (ret != 0) {
return handle_error(ret);
}
ret = mbedtls_ssl_set_hostname(&conn->ssl_ctx, host);
if (ret != 0) {
return handle_error(ret);
}
mbedtls_ssl_set_bio(&conn->ssl_ctx, &conn->net_ctx, mbedtls_net_send, NULL,
mbedtls_net_recv_timeout);
while ((ret = mbedtls_ssl_handshake(&conn->ssl_ctx)) != 0) {
if (ret != MBEDTLS_ERR_SSL_WANT_READ
&& ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
if (ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) {
return handle_error(ret);
}
}
handle_error(ret);
vTaskDelay(5000 / portTICK_RATE_MS);
}
mbedtls_ssl_get_record_expansion(&conn->ssl_ctx);
ret = mbedtls_ssl_get_verify_result(&conn->ssl_ctx);
if (ret != 0) {
return handle_error(ret);
}
return ret;
}
int ssl_destroy(SSLConnection* conn) {
mbedtls_net_free(&conn->net_ctx);
mbedtls_ssl_free(&conn->ssl_ctx);
mbedtls_ssl_config_free(&conn->ssl_conf);
mbedtls_ctr_drbg_free(&conn->drbg_ctx);
mbedtls_entropy_free(&conn->entropy_ctx);
mbedtls_x509_crt_free(&conn->ca_cert);
mbedtls_x509_crt_free(&conn->client_cert);
mbedtls_pk_free(&conn->client_key);
return 0;
}
int ssl_read(SSLConnection* n, unsigned char* buffer, int len, int timeout_ms) {
// NB: timeout_ms is ignored, so blocking read will timeout after SSL_READ_TIMEOUT_MS
return mbedtls_ssl_read(&n->ssl_ctx, buffer, len);
}
int ssl_write(SSLConnection* n, unsigned char* buffer, int len,
int timeout_ms) {
// NB: timeout_ms is ignored, so write is always block write
return mbedtls_ssl_write(&n->ssl_ctx, buffer, len);
}

View file

@ -0,0 +1,40 @@
#ifndef _SSL_CONNECTION_H_
#define _SSL_CONNECTION_H_
// this must be ahead of any mbedtls header files so the local mbedtls/config.h can be properly referenced
#include "mbedtls/config.h"
#include "mbedtls/net.h"
#include "mbedtls/debug.h"
#include "mbedtls/ssl.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/error.h"
#include "mbedtls/certs.h"
typedef struct SSLConnection {
mbedtls_net_context net_ctx;
mbedtls_ssl_context ssl_ctx;
mbedtls_ssl_config ssl_conf;
mbedtls_ctr_drbg_context drbg_ctx;
mbedtls_entropy_context entropy_ctx;
mbedtls_x509_crt ca_cert;
mbedtls_x509_crt client_cert;
mbedtls_pk_context client_key;
char *ca_cert_str;
char *client_cert_str;
char *client_key_str;
} SSLConnection;
extern void ssl_init(SSLConnection* n);
extern int ssl_connect(SSLConnection* n, const char* host, int port);
extern int ssl_destroy(SSLConnection* n);
extern int ssl_read(SSLConnection* n, unsigned char* buffer, int len,
int timeout_ms);
extern int ssl_write(SSLConnection* n, unsigned char* buffer, int len,
int timeout_ms);
#endif /* _SSL_CONNECTION_H_ */

View file

@ -94,8 +94,13 @@ int readPacket(MQTTClient* c, Timer* timer)
goto exit;
len = 1;
/* 2. read the remaining length. This is variable in itself */
decodePacket(c, &rem_len, left_ms(timer));
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
len += decodePacket(c, &rem_len, left_ms(timer));
if (len <= 1 || len + rem_len > c->readbuf_size) /* if packet is too big to fit in our readbuf, abort */
{
rc = READ_ERROR;
goto exit;
}
MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
/* 3. read the rest of the buffer using a callback to supply the rest of the data */
if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, left_ms(timer)) != rem_len))
{