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:
parent
c9851e9253
commit
7041c014bb
9 changed files with 725 additions and 2 deletions
3
examples/aws_iot/Makefile
Normal file
3
examples/aws_iot/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
PROGRAM=aws_iot
|
||||||
|
EXTRA_COMPONENTS = extras/paho_mqtt_c extras/mbedtls
|
||||||
|
include ../../common.mk
|
60
examples/aws_iot/README.md
Normal file
60
examples/aws_iot/README.md
Normal 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
280
examples/aws_iot/aws_iot.c
Normal 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);
|
||||||
|
}
|
29
examples/aws_iot/ca_cert.c
Normal file
29
examples/aws_iot/ca_cert.c
Normal 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";
|
17
examples/aws_iot/client_config.c
Normal file
17
examples/aws_iot/client_config.c
Normal 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";
|
119
examples/aws_iot/mbedtls/config.h
Normal file
119
examples/aws_iot/mbedtls/config.h
Normal 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 */
|
170
examples/aws_iot/ssl_connection.c
Normal file
170
examples/aws_iot/ssl_connection.c
Normal 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);
|
||||||
|
}
|
40
examples/aws_iot/ssl_connection.h
Normal file
40
examples/aws_iot/ssl_connection.h
Normal 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_ */
|
|
@ -94,8 +94,13 @@ int readPacket(MQTTClient* c, Timer* timer)
|
||||||
goto exit;
|
goto exit;
|
||||||
len = 1;
|
len = 1;
|
||||||
/* 2. read the remaining length. This is variable in itself */
|
/* 2. read the remaining length. This is variable in itself */
|
||||||
decodePacket(c, &rem_len, left_ms(timer));
|
len += decodePacket(c, &rem_len, left_ms(timer));
|
||||||
len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
|
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 */
|
/* 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))
|
if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, left_ms(timer)) != rem_len))
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue