diff --git a/libraries/AWS_IOT_TRUSTX/README.md b/libraries/AWS_IOT_TRUSTX/README.md new file mode 100644 index 00000000000..c21247d810d --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/README.md @@ -0,0 +1,33 @@ +# Arduino-esp32-aws-iot +Arduini ESP32 library for AWS IoT + +Please make sure you have read the [getting started guide](https://aws.amazon.com/iot/getting-started/) before trying to do anyting with this template. + +This template is based on and contains the code from the [aws-iot-device-sdk-embedded-C](https://github.com/aws/aws-iot-device-sdk-embedded-C) project with an ESP32 port. It may help to review that project before attempting to use this template. + +Define the below parameters in the sketch: +``` +WIFI_SSID +WIFI_PASSWORD +HOST_ADDRESS +CLIENT_ID +TOPIC_NAME +``` + + +*Certificates* +``` + root-CA.crt + certificate.pem.crt + private.pem.key +``` +Above certificates needs to be stored in the file aws_iot_certificates.c as arrays. Check the file for more information. + +![](https://exploreembedded.com/wiki/images/b/b9/ESP32_AWS_IOT_Certificates.png) + + +Please check this tutorial for setting up [AWS IOT on Amazon] (https://www.exploreembedded.com/wiki/Secure_IOT_with_AWS_and_Hornbill_ESP32) + +Follow this tutorial for using the [Arduio Esp32 AWS libaray] (https://exploreembedded.com/wiki/AWS_IOT_with_Arduino_ESP32) + + diff --git a/libraries/AWS_IOT_TRUSTX/examples/DHT11_Logger/DHT11_Logger.ino b/libraries/AWS_IOT_TRUSTX/examples/DHT11_Logger/DHT11_Logger.ino new file mode 100644 index 00000000000..e024651d31d --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/examples/DHT11_Logger/DHT11_Logger.ino @@ -0,0 +1,96 @@ + +#include +#include + +#include "DHT.h" + +#define DHTPIN 4 // what digital pin we're connected to + +// Uncomment whatever type you're using! +#define DHTTYPE DHT11 // DHT 11 +//#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321 +//#define DHTTYPE DHT21 // DHT 21 (AM2301) + +DHT dht(DHTPIN, DHTTYPE); + +AWS_IOT hornbill; // AWS_IOT instance + +char WIFI_SSID[]="your Wifi SSID"; +char WIFI_PASSWORD[]="Wifi Password"; +char HOST_ADDRESS[]="AWS host address"; +char CLIENT_ID[]= "client id"; +char TOPIC_NAME[]= "your thing/topic name"; + + +int status = WL_IDLE_STATUS; +int tick=0,msgCount=0,msgReceived = 0; +char payload[512]; +char rcvdPayload[512]; + + +void setup() { + + Serial.begin(115200); + delay(2000); + + while (status != WL_CONNECTED) + { + Serial.print("Attempting to connect to SSID: "); + Serial.println(WIFI_SSID); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + // wait 5 seconds for connection: + delay(5000); + } + + Serial.println("Connected to wifi"); + + if(hornbill.connect(HOST_ADDRESS,CLIENT_ID)== 0) // Connect to AWS using Host Address and Cliend ID + { + Serial.println("Connected to AWS"); + delay(1000); + } + else + { + Serial.println("AWS connection failed, Check the HOST Address"); + while(1); + } + + delay(2000); + + dht.begin(); //Initialize the DHT11 sensor +} + + +void loop() { + + // Reading temperature or humidity takes about 250 milliseconds! + // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor) + float h = dht.readHumidity(); + // Read temperature as Celsius (the default) + float t = dht.readTemperature(); + // Read temperature as Fahrenheit (isFahrenheit = true) + float f = dht.readTemperature(true); + + // Check if any reads failed and exit early (to try again). + if (isnan(h) || isnan(t) || isnan(f)) { + Serial.println("Failed to read from DHT sensor!"); + } + else + { + sprintf(payload,"Humidity:%f Temperature:%f'C",h,t); // Create the payload for publishing + + if(hornbill.publish(TOPIC_NAME,payload) == 0) // Publish the message(Temp and humidity) + { + Serial.print("Publish Message:"); + Serial.println(payload); + } + else + { + Serial.println("Publish failed"); + } + // publish the temp and humidity every 5 seconds. + vTaskDelay(5000 / portTICK_RATE_MS); + } +} diff --git a/libraries/AWS_IOT_TRUSTX/examples/Frivolous_Internet_Connected_Display/Frivolous_Internet_Connected_Display.ino b/libraries/AWS_IOT_TRUSTX/examples/Frivolous_Internet_Connected_Display/Frivolous_Internet_Connected_Display.ino new file mode 100644 index 00000000000..082af96f9d2 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/examples/Frivolous_Internet_Connected_Display/Frivolous_Internet_Connected_Display.ino @@ -0,0 +1,362 @@ +/* + * For detailed tutorial on building the project visit: + * https://exploreembedded.com/wiki/Building_a_Frivolous_Internet_Connected_Display_with_ESP32 + * Install the libraries listed in the tutorial before compiling the code. + + * Reference links: + + https://exploreembedded.com/wiki/Secure_IOT_with_AWS_and_Hornbill_ESP32 + https://exploreembedded.com/wiki/AWS_IOT_with_Arduino_ESP32 + + * Library Links: + https://github.com/ExploreEmbedded/Hornbill-Examples/tree/master/arduino-esp32/AWS_IOT/examples + https://github.com/adafruit/Adafruit_SSD1306 + https://github.com/adafruit/Adafruit-GFX-Library + */ + +#include +#include +#include +#include +#include +#include + + +AWS_IOT AWS_CLIENT; +/************************************************************************ + AWS Configuration +/************************************************************************/ +char WIFI_SSID[]="your Wifi SSID"; +char WIFI_PASSWORD[]="Wifi Password"; +char HOST_ADDRESS[]="AWS host address"; +char CLIENT_ID[]= "client id"; +char TOPIC_NAME[]= "your thing/topic name"; +/***********************************************************************/ + + + +/************************************************************************* + OLED + *************************************************************************/ +#define OLED_RESET 0xFF +Adafruit_SSD1306 display(OLED_RESET); + +#if (SSD1306_LCDHEIGHT != 64) +#error("Height incorrect, please fix Adafruit_SSD1306.h!"); +#endif +/************************************************************************/ + + + + +/********************************************************************************************************** + 32x32 Horizontal Logos + ***********************************************************************************************************/ +const uint8_t logoTwitter PROGMEM []= { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, + 0x00, 0x00, 0x3F, 0xCE, 0x30, 0x00, 0x7F, 0xFC, 0x38, 0x00, 0xFF, 0xF8, 0x7C, 0x00, 0xFF, 0xFE, + 0x3E, 0x00, 0xFF, 0xFC, 0x3F, 0x80, 0xFF, 0xF8, 0x3F, 0xF0, 0xFF, 0xF8, 0x1F, 0xFF, 0xFF, 0xF8, + 0x2F, 0xFF, 0xFF, 0xF8, 0x3F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xF0, + 0x1F, 0xFF, 0xFF, 0xF0, 0x0F, 0xFF, 0xFF, 0xE0, 0x03, 0xFF, 0xFF, 0xE0, 0x0F, 0xFF, 0xFF, 0xC0, + 0x07, 0xFF, 0xFF, 0xC0, 0x07, 0xFF, 0xFF, 0x80, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x7F, 0xFE, 0x00, + 0x00, 0xFF, 0xFC, 0x00, 0x07, 0xFF, 0xF8, 0x00, 0x7F, 0xFF, 0xE0, 0x00, 0x1F, 0xFF, 0x80, 0x00, + 0x01, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + + +const uint8_t logoFacebook PROGMEM [] = { + 0x0F, 0xFF, 0xFF, 0xF0, 0x3F, 0xFF, 0xFF, 0xFC, 0x7F, 0xFF, 0xFF, 0xFE, 0x7F, 0xFF, 0xF8, 0x1E, + 0xFF, 0xFF, 0xF0, 0x1E, 0xFF, 0xFF, 0xE0, 0x1F, 0xFF, 0xFF, 0xC0, 0x1F, 0xFF, 0xFF, 0xC0, 0x1F, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0x00, 0x1F, + 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x1F, 0xFF, 0xFE, 0x00, 0x1F, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, + 0xFF, 0xFF, 0xC1, 0xFF, 0xFF, 0xFF, 0xC1, 0xFF, 0x7F, 0xFF, 0xC1, 0xFE, 0x7F, 0xFF, 0xC1, 0xFE, + 0x3F, 0xFF, 0xFF, 0xFC, 0x1F, 0xFF, 0xFF, 0xF8, 0x07, 0xFF, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t logoTime PROGMEM [] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0x00, 0x00, 0x01, 0x00, 0x60, 0x00, 0x04, 0x08, 0x10, 0x00, + 0x08, 0x00, 0x08, 0x00, 0x10, 0x08, 0x04, 0x00, 0x00, 0x08, 0x02, 0x00, 0x20, 0x08, 0x00, 0x00, + 0x00, 0x08, 0x01, 0x00, 0x40, 0x08, 0x01, 0x80, 0x40, 0x08, 0x00, 0x80, 0x40, 0x08, 0x00, 0x80, + 0x42, 0x0F, 0xF2, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x80, 0x40, 0x00, 0x00, 0x00, + 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0x00, 0x20, 0x00, 0x03, 0x80, 0x10, 0x08, 0x02, 0x80, + 0x08, 0x00, 0x04, 0x80, 0x04, 0x00, 0x3C, 0x78, 0x02, 0x00, 0x20, 0x08, 0x00, 0x80, 0x10, 0x10, + 0x00, 0x3E, 0x08, 0x20, 0x00, 0x00, 0x08, 0x20, 0x00, 0x00, 0x11, 0x20, 0x00, 0x00, 0x14, 0xE0, + 0x00, 0x00, 0x18, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const uint8_t logoTemperature PROGMEM [] = { + 0x06, 0x00, 0x0F, 0x00, 0x06, 0x00, 0x1F, 0x80, 0x1F, 0x87, 0xD9, 0x80, 0x1F, 0x80, 0x19, 0x80, + 0x06, 0x00, 0x19, 0x80, 0x06, 0x01, 0xD9, 0x80, 0x00, 0x00, 0x19, 0x80, 0x00, 0x07, 0xF9, 0x80, + 0x00, 0x00, 0x19, 0x80, 0x00, 0x01, 0xD9, 0x80, 0x00, 0x01, 0xDF, 0x80, 0x00, 0x00, 0x1F, 0x80, + 0x00, 0x07, 0xFF, 0x80, 0x00, 0x00, 0x1F, 0x80, 0x00, 0x01, 0xDF, 0x80, 0x00, 0x00, 0x1F, 0x80, + 0x1F, 0x80, 0x1F, 0x80, 0x1F, 0x87, 0xDF, 0x80, 0x00, 0x00, 0x3F, 0xC0, 0x00, 0x00, 0x76, 0xE0, + 0x00, 0x00, 0xE6, 0xF0, 0x00, 0x01, 0xDF, 0xB8, 0x00, 0x01, 0xBF, 0xF8, 0x00, 0x01, 0xFF, 0xF8, + 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x01, 0xFF, 0xF8, 0x00, 0x01, 0xBF, 0xD8, 0x00, 0x01, 0xBF, 0xD8, + 0x00, 0x00, 0xDF, 0xB0, 0x00, 0x00, 0xE0, 0x70, 0x00, 0x00, 0x7F, 0xE0, 0x00, 0x00, 0x1F, 0x80 +}; + +const uint8_t logoHornbill PROGMEM [] = { + 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0x80, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFE, 0x00, 0x00, 0x00, 0x00, + 0xFC, 0xFC, 0x0F, 0xF0, 0x00, 0xC0, 0x31, 0xC6, 0x78, 0x70, 0x03, 0xE0, 0x01, 0xC0, 0x33, 0xCE, + 0x30, 0x30, 0x01, 0xC0, 0x00, 0xC0, 0x01, 0xC6, 0x30, 0x30, 0x00, 0x00, 0x00, 0xC0, 0x01, 0xC6, + 0x30, 0x31, 0xFD, 0xFF, 0xFC, 0xFE, 0x71, 0xC6, 0x3F, 0xF1, 0x8C, 0xED, 0x8C, 0xE7, 0x31, 0xC6, + 0x30, 0x73, 0x0E, 0xE1, 0x8C, 0xC3, 0x31, 0xC6, 0x30, 0x33, 0x06, 0xE1, 0x8C, 0xC3, 0x31, 0xC6, + 0x30, 0x33, 0x86, 0xE1, 0x8C, 0xC3, 0x31, 0xC6, 0x30, 0x33, 0x8C, 0xE1, 0x8C, 0xC3, 0x31, 0xC6, + 0x78, 0x79, 0xDC, 0xE3, 0x9C, 0xEE, 0x39, 0xCE, 0x00, 0x08, 0x70, 0x00, 0x00, 0x38, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0xDF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xFF, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0xFF, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1F, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3D, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +/*******************************************************************************************************/ + +typedef enum{ + SERVICE_TWITTER, + SERVICE_FACEBOOK, + SERVICE_TIME, + SERVICE_TEMPERATURE, + SERVICE_MAX_OPTIONS +}; + + +jsmntok_t token[512]; +char rcvdPayload[512]; +char msgReceived=0; +int serviceNum = 0; + +char supportedServices[SERVICE_MAX_OPTIONS][20]= +{ + "twitter", + "facebook", + "time", + "temperature" +}; + +typedef enum{ + VALUE_NUM, + VALUE_FLOAT, + VALUE_STRING +}valueType_t; + +typedef struct{ + char Tag[20]; + char value[20]; + const uint8_t *logo; +}serviceData_t; + +serviceData_t service[SERVICE_MAX_OPTIONS]={ + /*serviceTag value LogoArray */ + {"tweets", "12345", logoTwitter}, + {"likes", "5678", logoFacebook}, + {"Time", "9:30", logoTime}, + {"Temp", "35", logoTemperature} +}; + +void processReceivedMsg(); +void wifiInit(); +void connectToAWS(); + +void mySubCallBackHandler (char *topicName, int payloadLen, char *payLoad) +{ + if(msgReceived==0) + { + strncpy(rcvdPayload,payLoad,payloadLen); + rcvdPayload[payloadLen] = 0; + msgReceived = 1; + } +} + + +void setup() { + + Serial.begin(115200); + display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64) + + wifiInit(); + connectToAWS(); +} + + + +void loop() { + + display.clearDisplay(); + if(service[serviceNum].logo != 0); // Display Logo if available + display.drawBitmap(0,0, service[serviceNum].logo, 32, 32, WHITE); // Display the Logo + display.setTextColor(WHITE); + display.setTextSize(2); + display.setCursor(36,0); + display.print(service[serviceNum].Tag); + display.setTextSize(3); + display.setCursor(32,32); + display.print(service[serviceNum].value); + + display.setTextSize(1); + display.setCursor(64-(SERVICE_MAX_OPTIONS*3),56); + for(int i=0;i<=SERVICE_MAX_OPTIONS;i++) + { + if(i==serviceNum) + { + int x0,y0,x1,y1,x2,y2; + x0 = display.getCursorX()+3; + y0 = display.getCursorY()+3; + display.fillCircle(x0,y0,3,WHITE); + } + else + { + display.write('.'); + } + } + + if(serviceNum<(SERVICE_MAX_OPTIONS-1)) // Ensure the state machine is within the limits + { + serviceNum++; // Go to next state + } + else + { + serviceNum = 0; + } + display.display(); + + if(msgReceived == 1) + { + processReceivedMsg(); + msgReceived = 0; + } + + if (WiFi.status() != WL_CONNECTED) + { + hornbillInitMsgs("Wifi Disconnected, Reconnecting after 1sec"); + vTaskDelay(1000/portTICK_PERIOD_MS); + wifiInit(); + connectToAWS(); + } + + vTaskDelay(2000/portTICK_PERIOD_MS); +} + + + +void hornbillInitMsgs(char *msg) +{ + display.clearDisplay(); + display.setTextColor(WHITE); + display.drawBitmap(0,0, logoHornbill, 64, 32, WHITE); + display.setTextSize(1); + display.setCursor(0,40); + display.print(msg); + display.display(); +} + + + + +void wifiInit() +{ + int status = WL_IDLE_STATUS; + while (status != WL_CONNECTED) + { + sprintf(rcvdPayload,"Reconnicting to SSID:%s",WIFI_SSID); + hornbillInitMsgs(rcvdPayload); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + // wait 5 seconds for connection: + vTaskDelay(2000/portTICK_PERIOD_MS); + } +} + + +void connectToAWS() +{ + int rc,reconnectCnt=0; + hornbillInitMsgs("Connected to wifi"); + + if(AWS_CLIENT.connect(HOST_ADDRESS,CLIENT_ID)== 0) + { + hornbillInitMsgs("Connected to AWS"); + vTaskDelay(1000/portTICK_PERIOD_MS); + + do{ + rc = AWS_CLIENT.subscribe(TOPIC_NAME,mySubCallBackHandler); + if(rc == 0) + { + hornbillInitMsgs("Subscribe Successfull"); + vTaskDelay(1000/portTICK_PERIOD_MS); + } + else if(reconnectCnt++>5) + { + hornbillInitMsgs("Subscribe Failed, Check the Thing Name and Certificates"); + while(1); + } + }while(rc != 0); + } + else + { + hornbillInitMsgs("AWS connection failed, Check the HOST Address"); + while(1); + } +} + + +char ser[20], serTag[20],serVal[20]; +void processReceivedMsg() +{ + int i,r,index; + jsmn_parser_t tokenParser; + + + jsmn.Init(&tokenParser); + r = jsmn.parse(&tokenParser, rcvdPayload, strlen(rcvdPayload), token, sizeof(token)/sizeof(token[0])); + if (r < 0) { + return;// HORNBILL_IO_REQUEST_JSON_INVALID; + } + + /* Assume the top-level element is an object */ + if (r < 1 || token[0].type != JSMN_OBJECT) { + return;// HORNBILL_IO_REQUEST_JSON_INVALID; + } + + /* Loop over all keys of the root object */ + for (i = 1; i < r; i++) { + if (jsmn.equate(rcvdPayload, &token[i], "reported") == 0) { + /* We may use strndup() to fetch string value */ + return ; + }else if (jsmn.equate(rcvdPayload, &token[i], "desired") == 0) { + /* We may use strndup() to fetch string value */ + i++; + }else if (jsmn.equate(rcvdPayload, &token[i], "service") == 0) { + /* We may use strndup() to fetch string value */ + sprintf(ser,"%.*s", token[i+1].end-token[i+1].start,rcvdPayload + token[i+1].start); + i++; + } else if (jsmn.equate(rcvdPayload, &token[i], "serviceTag") == 0) { + /* We may want to do strtol() here to get numeric value */ + sprintf(serTag,"%.*s", token[i+1].end-token[i+1].start,rcvdPayload + token[i+1].start); + i++; + } else if (jsmn.equate(rcvdPayload, &token[i], "value") == 0) { + /* We may use strndup() to fetch string value */ + sprintf(serVal,"%.*s", token[i+1].end-token[i+1].start,rcvdPayload + token[i+1].start); + i++; + + for(index=0;index +#include +#include +#include + + +/************************************************************************ + AWS Configuration +*************************************************************************/ +extern char WIFI_SSID[]; +extern char WIFI_PASSWORD[]; +extern char HOST_ADDRESS[]; +extern char CLIENT_ID[]; +extern char TOPIC_NAME[]; +/***********************************************************************/ + + +/********************************************************** + Pin Mapping +**********************************************************/ +int thermoDO = 27; +int thermoCS = 14; +int thermoCLK = 12; +int currentPin = 36; +/*********************************************************/ + + +/********************************************************* + Create instances +*********************************************************/ +AWS_IOT AWS_CLIENT; +MAX6675 thermoCouple(thermoCLK, thermoCS, thermoDO); +EnergyMonitor emon; +/*********************************************************/ + +int status = WL_IDLE_STATUS; +int tick=0, publishMsg=0; +char payload[512]; +const char payloadFormat[] = "{\"state\":{\"desired\":{\"temperature\":%f,\"irms\":%f,\"power\":%f}}}"; + +void setup() { + + Serial.begin(115200); + delay(2000); + + while (status != WL_CONNECTED) + { + Serial.print("Attempting to connect to SSID: "); + Serial.println(WIFI_SSID); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + // wait 5 seconds for connection: + delay(5000); + } + + Serial.println("Connected to wifi"); + + if(AWS_CLIENT.connect(HOST_ADDRESS,CLIENT_ID)== 0) + { + Serial.println("Connected to AWS"); + delay(1000); + } + else + { + Serial.println("AWS connection failed, Check the HOST Address"); + while(1); + } + + delay(2000); + + emon.current(currentPin, 111.1); // Current: input pin, calibration. +} + + + +void loop() { + + double Irms,power,tempCelcius; + + if(tick >= 5) // Publish every 5secs + { + tempCelcius = thermoCouple.readCelsius(); + Irms = emon.calcIrms(1480); // Calculate Irms only + power = 230 * Irms; + sprintf(payload,payloadFormat,tempCelcius,Irms,power); + if(AWS_CLIENT.publish(TOPIC_NAME,payload) == 0) + { + Serial.println(payload); + tick = 0; // Publish successfull, wait for 5more seconds + } + else + { + Serial.println("Publish failed, Will try again after 1sec"); + } + } + else + { + tick++; + } + + vTaskDelay(1000 / portTICK_RATE_MS); +} diff --git a/libraries/AWS_IOT_TRUSTX/examples/SerialPrintTempPower/SerialPrintTempPower.ino b/libraries/AWS_IOT_TRUSTX/examples/SerialPrintTempPower/SerialPrintTempPower.ino new file mode 100644 index 00000000000..04956151f1a --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/examples/SerialPrintTempPower/SerialPrintTempPower.ino @@ -0,0 +1,55 @@ +/* + * Tutorial : Hornbill Industrial Data logger (Temp and Current ) + * Sensor: Max6675 Temp Sensor, non-invasive Current Sensor sensor + + * Reference links: + https://exploreembedded.com/wiki/Hornbill_Industrial_Data_Logger + https://exploreembedded.com/wiki/Secure_IOT_with_AWS_and_Hornbill_ESP32 + https://exploreembedded.com/wiki/AWS_IOT_with_Arduino_ESP32 + + * Library Links: + https://github.com/ExploreEmbedded/Hornbill-Examples/tree/master/arduino-esp32/AWS_IOT/examples + https://github.com/openenergymonitor/EmonLib + https://github.com/adafruit/MAX6675-library + */ +#include +#include + + +/********************************************************** + Pin Mapping +**********************************************************/ +int thermoDO = 27; +int thermoCS = 14; +int thermoCLK = 12; +int currentPin = 36; +/*********************************************************/ + + +/********************************************************* + Create instances +*********************************************************/ +MAX6675 thermoCouple(thermoCLK, thermoCS, thermoDO); +EnergyMonitor emon; +/*********************************************************/ +char buff[100]; + +void setup() { + + Serial.begin(115200); + emon.current(currentPin, 111.1); // Current: input pin, calibration. +} + + +void loop() { + + double Irms,power,tempCelcius; + + tempCelcius = thermoCouple.readCelsius(); + Irms = emon.calcIrms(1480); // Calculate Irms only + power = 230 * Irms; + sprintf(buff,"tempCelcius:%f Irms:%f power:%f",tempCelcius,Irms,power); + Serial.println(buff); + + vTaskDelay(1000 / portTICK_RATE_MS); +} diff --git a/libraries/AWS_IOT_TRUSTX/examples/pubSubTest/pubSubTest.ino b/libraries/AWS_IOT_TRUSTX/examples/pubSubTest/pubSubTest.ino new file mode 100644 index 00000000000..6611a7f4e28 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/examples/pubSubTest/pubSubTest.ino @@ -0,0 +1,93 @@ +#include +#include + +AWS_IOT hornbill; + +char WIFI_SSID[]="your Wifi SSID"; +char WIFI_PASSWORD[]="Wifi Password"; +char HOST_ADDRESS[]="AWS host address"; +char CLIENT_ID[]= "client id"; +char TOPIC_NAME[]= "your thing/topic name"; + + +int status = WL_IDLE_STATUS; +int tick=0,msgCount=0,msgReceived = 0; +char payload[512]; +char rcvdPayload[512]; + +void mySubCallBackHandler (char *topicName, int payloadLen, char *payLoad) +{ + strncpy(rcvdPayload,payLoad,payloadLen); + rcvdPayload[payloadLen] = 0; + msgReceived = 1; +} + + + +void setup() { + Serial.begin(115200); + delay(2000); + + while (status != WL_CONNECTED) + { + Serial.print("Attempting to connect to SSID: "); + Serial.println(WIFI_SSID); + // Connect to WPA/WPA2 network. Change this line if using open or WEP network: + status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD); + + // wait 5 seconds for connection: + delay(5000); + } + + Serial.println("Connected to wifi"); + + if(hornbill.connect(HOST_ADDRESS,CLIENT_ID)== 0) + { + Serial.println("Connected to AWS"); + delay(1000); + + if(0==hornbill.subscribe(TOPIC_NAME,mySubCallBackHandler)) + { + Serial.println("Subscribe Successfull"); + } + else + { + Serial.println("Subscribe Failed, Check the Thing Name and Certificates"); + while(1); + } + } + else + { + Serial.println("AWS connection failed, Check the HOST Address"); + while(1); + } + + delay(2000); + +} + +void loop() { + + if(msgReceived == 1) + { + msgReceived = 0; + Serial.print("Received Message:"); + Serial.println(rcvdPayload); + } + if(tick >= 5) // publish to topic every 5seconds + { + tick=0; + sprintf(payload,"Hello from hornbill ESP32 : %d",msgCount++); + if(hornbill.publish(TOPIC_NAME,payload) == 0) + { + Serial.print("Publish Message:"); + Serial.println(payload); + } + else + { + Serial.println("Publish failed"); + } + } + vTaskDelay(1000 / portTICK_RATE_MS); + tick++; +} \ No newline at end of file diff --git a/libraries/AWS_IOT_TRUSTX/library.properties b/libraries/AWS_IOT_TRUSTX/library.properties new file mode 100644 index 00000000000..8367c1963db --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/library.properties @@ -0,0 +1,9 @@ +name=AWS_IOT_TRUSTX +version=1.0 +author=ExploreEmbedded.com +maintainer=ExploreEmbedded.com +sentence=aws IOT test with OPTIGA(TM) Trust X +paragraph= +category=Communication +url=https://exploreembedded.com/wiki/AWS_IOT_with_Arduino_ESP32 +architectures=esp32 diff --git a/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.cpp b/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.cpp new file mode 100644 index 00000000000..ae0bf91ba5e --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.cpp @@ -0,0 +1,345 @@ +/*************************************************************************************************** + ExploreEmbedded Copyright Notice +**************************************************************************************************** + * File: AWS_IOT.cpp + * Version: 1.0 + * Author: ExploreEmbedded + * Website: http://www.exploreembedded.com/wiki + * Description: ESP32 Arduino library for AWS IOT. + +This code has been developed and tested on ExploreEmbedded boards. +We strongly believe that the library works on any of development boards for respective controllers. +Check this link http://www.exploreembedded.com/wiki for awesome tutorials on 8051,PIC,AVR,ARM,Robotics,RTOS,IOT. +ExploreEmbedded invests substantial time and effort developing open source HW and SW tools, to support consider buying the ExploreEmbedded boards. + +The ExploreEmbedded libraries and examples are licensed under the terms of the new-bsd license(two-clause bsd license). +See also: http://www.opensource.org/licenses/bsd-license.php + +EXPLOREEMBEDDED DISCLAIMS ANY KIND OF HARDWARE FAILURE RESULTING OUT OF USAGE OF LIBRARIES, DIRECTLY OR +INDIRECTLY. FILES MAY BE SUBJECT TO CHANGE WITHOUT PRIOR NOTICE. THE REVISION HISTORY CONTAINS THE INFORMATION +RELATED TO UPDATES. + + +Permission to use, copy, modify, and distribute this software and its documentation for any purpose +and without fee is hereby granted, provided that this copyright notices appear in all copies +and that both those copyright notices and this permission notice appear in supporting documentation. +**************************************************************************************************/ +#include +#include +#include +#include +#include +#include + +#include "AWS_IOT.h" +#include "aws_iot_config.h" +#include "aws_iot_log.h" +#include "aws_iot_version.h" + +#include "aws_iot_mqtt_client.h" +#include "aws_iot_mqtt_client_interface.h" + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_system.h" +#include "esp_wifi.h" +#include "esp_event_loop.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "driver/sdmmc_host.h" + +#include "optiga/optiga_crypt.h" +#include "optiga/optiga_util.h" +#include "optiga/ifx_i2c/ifx_i2c.h" +#include "optiga/pal/pal.h" +#include "optiga/pal/pal_os_event.h" + +static const char *TAG = "AWS_IOT"; +char AWS_IOT_HOST_ADDRESS[128]; + +char cPayload[512]; +AWS_IoT_Client client; +IoT_Publish_Message_Params paramsQOS0; +pSubCallBackHandler_t subApplCallBackHandler = 0; + +optiga_comms_t optiga_comms = {(void*)&ifx_i2c_context_0, NULL, NULL, 0}; +static host_lib_status_t optiga_comms_status; + + + +/* CA Root certificate, device ("Thing") certificate and device + * ("Thing") key. + + "Embedded Certs" are stored in the file aws_iot_certificates.c as arrays +*/ +extern const char aws_root_ca_pem[]; +extern const char certificate_pem_crt[]; +extern const char private_pem_key[]; + + + +void aws_iot_task(void *param); + +static void read_ifx_cert(void) +{ + uint8_t ifx_cert_hex[512]; + uint16_t ifx_cert_hex_len = sizeof(ifx_cert_hex); + size_t ifx_cert_b64_len = 0; + uint8_t ifx_cert_b64_temp[768]; + uint16_t offset_to_read = 0; + uint16_t offset_to_write = 0; + uint16_t size_to_copy = 0; + ESP_ERROR_CHECK( optiga_util_read_data(eDEVICE_PUBKEY_CERT_PRJSPC_1, 0, ifx_cert_hex, &ifx_cert_hex_len) ); + mbedtls_base64_encode((unsigned char *)ifx_cert_b64_temp, sizeof(ifx_cert_b64_temp), + &ifx_cert_b64_len, + ifx_cert_hex + 9, ifx_cert_hex_len - 9); + + // esp_log_buffer_hex("main", ifx_cert_b64_temp, ifx_cert_b64_len); + + memcpy(certificate_pem_crt, "-----BEGIN CERTIFICATE-----\n", 28); + offset_to_write += 28; + + //Properly copy certificate and format it as pkcs expects + for (offset_to_read = 0; offset_to_read < ifx_cert_b64_len;) + { + // The last block of data usually is less than 64, thus we need to find the leftover + if ((offset_to_read + 64) >= ifx_cert_b64_len) + size_to_copy = ifx_cert_b64_len - offset_to_read; + else + size_to_copy = 64; + memcpy(certificate_pem_crt + offset_to_write, ifx_cert_b64_temp + offset_to_read, size_to_copy); + offset_to_write += size_to_copy; + offset_to_read += size_to_copy; + certificate_pem_crt[offset_to_write] = '\n'; + offset_to_write++; + } + + memcpy(certificate_pem_crt + offset_to_write, "-----END CERTIFICATE-----\n\0", 27); + + ESP_LOGI("main", "End Device Certificate:\n\r %s", certificate_pem_crt); +} + +static void optiga_comms_event_handler(void* upper_layer_ctx, host_lib_status_t event) +{ + optiga_comms_status = event; +} + +static int32_t optiga_init(void) +{ + int32_t status = (int32_t) OPTIGA_LIB_ERROR; + + do + { + if (pal_os_event_init() == PAL_STATUS_FAILURE) + { + return OPTIGA_COMMS_BUSY; + } + + // OPTIGA Initialisation phase + //Invoke optiga_comms_open to initialize the IFX I2C Protocol and security chip + optiga_comms_status = OPTIGA_COMMS_BUSY; + optiga_comms.upper_layer_handler = optiga_comms_event_handler; + status = optiga_comms_open(&optiga_comms); + if(E_COMMS_SUCCESS != status) + { + configPRINTF( ("Failure: optiga_comms_open(): 0x%04X\n\r", status) ); + break; + } + + //Wait until IFX I2C initialization is complete + while(optiga_comms_status == OPTIGA_COMMS_BUSY) + { + pal_os_timer_delay_in_milliseconds(1); + } + + if((OPTIGA_COMMS_SUCCESS != status) || (optiga_comms_status == OPTIGA_COMMS_ERROR)) + { + configPRINTF( ("Failure: optiga_comms_status(): status = 0x%04X, comms_status = 0x%04X\n\r", status, optiga_comms_status) ); + break; + } + + status = optiga_util_open_application(&optiga_comms); + if(OPTIGA_LIB_SUCCESS != status) + { + configPRINTF( ("Failure: CmdLib_OpenApplication(): 0x%04X\n\r", status) ); + break; + } + + status = OPTIGA_LIB_SUCCESS; + } while(0); + + return status; +} + +void iot_subscribe_callback_handler(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) +{ + if(subApplCallBackHandler != 0) //User call back if configured + subApplCallBackHandler(topicName,params->payloadLen,(char *)params->payload); +} + + + +void disconnectCallbackHandler(AWS_IoT_Client *pClient, void *data) +{ + ESP_LOGW(TAG, "MQTT Disconnect"); + IoT_Error_t rc = FAILURE; + + if(NULL == pClient) + { + return; + } + + if(aws_iot_is_autoreconnect_enabled(pClient)) { + ESP_LOGI(TAG, "Auto Reconnect is enabled, Reconnecting attempt will start now"); + } + else + { + ESP_LOGW(TAG, "Auto Reconnect not enabled. Starting manual reconnect..."); + // rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + ESP_LOGW(TAG, "Manual Reconnect Successful"); + } + else { + ESP_LOGW(TAG, "Manual Reconnect Failed - %d", rc); + } + } +} + +int AWS_IOT::connect(char *hostAddress, char *clientID) +{ + const size_t stack_size = 36*1024; + + strcpy(AWS_IOT_HOST_ADDRESS,hostAddress); + IoT_Error_t rc = FAILURE; + + + IoT_Client_Init_Params mqttInitParams = iotClientInitParamsDefault; + IoT_Client_Connect_Params connectParams = iotClientConnectParamsDefault; + + printf("Init OPTIGA Trust X\r\n"); + optiga_init(); + read_ifx_cert(); + + ESP_LOGI(TAG, "AWS IoT SDK Version %d.%d.%d-%s", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, VERSION_TAG); + + mqttInitParams.enableAutoReconnect = false; // We enable this later below + mqttInitParams.pHostURL = AWS_IOT_HOST_ADDRESS; + mqttInitParams.port = CONFIG_AWS_IOT_MQTT_PORT; + + + mqttInitParams.pRootCALocation = (const char *)aws_root_ca_pem; + mqttInitParams.pDeviceCertLocation = (const char *)certificate_pem_crt; + mqttInitParams.pDevicePrivateKeyLocation = (const char *)private_pem_key; + + + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = disconnectCallbackHandler; + mqttInitParams.disconnectHandlerData = NULL; + + + rc = aws_iot_mqtt_init(&client, &mqttInitParams); + + if(SUCCESS != rc) { + ESP_LOGE(TAG, "aws_iot_mqtt_init returned error : %d ", rc); + return rc; //abort(); + } + + connectParams.keepAliveIntervalInSec = 10; + connectParams.isCleanSession = true; + connectParams.MQTTVersion = MQTT_3_1_1; + /* Client ID is set in the menuconfig of the example */ + connectParams.pClientID = clientID; + connectParams.clientIDLen = (uint16_t) strlen(clientID); + connectParams.isWillMsgPresent = false; + + ESP_LOGI(TAG, "Connecting to AWS..."); + + do { + rc = aws_iot_mqtt_connect(&client, &connectParams); + + if(SUCCESS != rc) { + ESP_LOGE(TAG, "Error(%d) connecting to %s:%d, \n\rTrying to reconnect", rc, mqttInitParams.pHostURL, mqttInitParams.port); + + } + + } while(SUCCESS != rc); + + + /* + * Enable Auto Reconnect functionality. Minimum and Maximum time of Exponential backoff are set in aws_iot_config.h + * #AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL + * #AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL + */ + /* rc = aws_iot_mqtt_autoreconnect_set_status(&client, true); + if(SUCCESS != rc) { + ESP_LOGE(TAG, "Unable to set Auto Reconnect to true - %d", rc); + abort(); + } */ + + if(rc == SUCCESS) + xTaskCreatePinnedToCore(&aws_iot_task, "aws_iot_task", stack_size, NULL, 5, NULL, 1); + + return rc; +} + + +int AWS_IOT::publish(char *pubtopic,char *pubPayLoad) +{ + IoT_Error_t rc; + + paramsQOS0.qos = QOS0; + paramsQOS0.payload = (void *) pubPayLoad; + paramsQOS0.isRetained = 0; + + paramsQOS0.payloadLen = strlen(pubPayLoad); + rc = aws_iot_mqtt_publish(&client, pubtopic, strlen(pubtopic), ¶msQOS0); + + return rc; +} + + + +int AWS_IOT::subscribe(char *subTopic, pSubCallBackHandler_t pSubCallBackHandler) +{ + IoT_Error_t rc; + + subApplCallBackHandler = pSubCallBackHandler; + + ESP_LOGI(TAG, "Subscribing..."); + rc = aws_iot_mqtt_subscribe(&client, subTopic, strlen(subTopic), QOS0, iot_subscribe_callback_handler, NULL); + if(SUCCESS != rc) { + ESP_LOGE(TAG, "Error subscribing : %d ", rc); + return rc; + } + ESP_LOGI(TAG, "Subscribing... Successful"); + + return rc; +} + + + + +void aws_iot_task(void *param) { + +IoT_Error_t rc = SUCCESS; + + while(1) + { + //Max time the yield function will wait for read messages + rc = aws_iot_mqtt_yield(&client, 200); + + if(NETWORK_ATTEMPTING_RECONNECT == rc) + { + // If the client is attempting to reconnect we will skip the rest of the loop. + continue; + } + + + vTaskDelay(1000 / portTICK_RATE_MS); + } +} diff --git a/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.h b/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.h new file mode 100644 index 00000000000..530c5bc0e06 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/AWS_IOT.h @@ -0,0 +1,47 @@ +/*************************************************************************************************** + ExploreEmbedded Copyright Notice +**************************************************************************************************** + * File: AWS_IOT.h + * Version: 1.0 + * Author: ExploreEmbedded + * Website: http://www.exploreembedded.com/wiki + * Description: ESP32 Arduino library for AWS IOT. + +This code has been developed and tested on ExploreEmbedded boards. +We strongly believe that the library works on any of development boards for respective controllers. +Check this link http://www.exploreembedded.com/wiki for awesome tutorials on 8051,PIC,AVR,ARM,Robotics,RTOS,IOT. +ExploreEmbedded invests substantial time and effort developing open source HW and SW tools, to support consider buying the ExploreEmbedded boards. + +The ExploreEmbedded libraries and examples are licensed under the terms of the new-bsd license(two-clause bsd license). +See also: http://www.opensource.org/licenses/bsd-license.php + +EXPLOREEMBEDDED DISCLAIMS ANY KIND OF HARDWARE FAILURE RESULTING OUT OF USAGE OF LIBRARIES, DIRECTLY OR +INDIRECTLY. FILES MAY BE SUBJECT TO CHANGE WITHOUT PRIOR NOTICE. THE REVISION HISTORY CONTAINS THE INFORMATION +RELATED TO UPDATES. + + +Permission to use, copy, modify, and distribute this software and its documentation for any purpose +and without fee is hereby granted, provided that this copyright notices appear in all copies +and that both those copyright notices and this permission notice appear in supporting documentation. +**************************************************************************************************/ + + +#ifndef _HORNBILL_AWS_IOT_LIB_H_ +#define _HORNBILL_AWS_IOT_LIB_H_ + + +typedef void (*pSubCallBackHandler_t)(char *topicName, int payloadLen, char *payLoad); + +class AWS_IOT{ + + private: + + public: + int connect(char *hostAddress, char *clientID); + int publish(char *pubtopic,char *pubPayLoad); + int subscribe(char *subTopic, pSubCallBackHandler_t pSubCallBackHandler); +}; + + +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_certficates.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_certficates.c new file mode 100644 index 00000000000..a8ab73f62b1 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_certficates.c @@ -0,0 +1,107 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_certifcates.c + * @brief File to store the AWS certificates in the form of arrays + */ + +#ifdef __cplusplus +extern "C" { +#endif + +const char aws_root_ca_pem[] = {"-----BEGIN CERTIFICATE-----\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +-----END CERTIFICATE-----\n"}; + +const char certificate_pem_crt[] = {"-----BEGIN CERTIFICATE-----\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +-----END CERTIFICATE-----\n"}; + +const char private_pem_key[] = {"-----BEGIN RSA PRIVATE KEY-----\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\ +-----END RSA PRIVATE KEY-----\n"}; + + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_config.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_config.h new file mode 100644 index 00000000000..4f7ddb5efc1 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_config.h @@ -0,0 +1,76 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_config.h + * @brief AWS IoT specific configuration file + */ + +#ifndef _AWS_IOT_CONFIG_H__ +#define _AWS_IOT_CONFIG_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_log.h" + + +extern char AWS_IOT_HOST_ADDRESS[]; + + +#define CONFIG_AWS_IOT_MQTT_HOST AWS_IOT_HOST_ADDRESS +#define CONFIG_AWS_IOT_MQTT_PORT 8883 + + +// This configuration macro needs to be available globally to enable threading +#define _ENABLE_THREAD_SUPPORT_ + +// These values are defined in the menuconfig of the AWS IoT component. +// However, you can override these constants from your own code. +#define AWS_IOT_MQTT_HOST CONFIG_AWS_IOT_MQTT_HOST ///< Customer specific MQTT HOST. The same will be used for Thing Shadow +#define AWS_IOT_MQTT_PORT CONFIG_AWS_IOT_MQTT_PORT ///< default port for MQTT/S + +// These values are defaults and are used for ShadowConnectParametersDefault. +// You should override them from your own code. +#define AWS_IOT_MQTT_CLIENT_ID "ESP32" ///< MQTT client ID should be unique for every device +#define AWS_IOT_MY_THING_NAME "ESP32" ///< Thing Name of the Shadow this device is associated with + +// MQTT PubSub +#define AWS_IOT_MQTT_TX_BUF_LEN 512 ///< Any time a message is sent out through the MQTT layer. The message is copied into this buffer anytime a publish is done. This will also be used in the case of Thing Shadow +#define AWS_IOT_MQTT_RX_BUF_LEN 512 ///< Any message that comes into the device should be less than this buffer size. If a received message is bigger than this buffer size the message will be dropped. +#define AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS 5 ///< Maximum number of topic filters the MQTT client can handle at any given time. This should be increased appropriately when using Thing Shadow + +// Thing Shadow specific configs +#define SHADOW_MAX_SIZE_OF_RX_BUFFER (AWS_IOT_MQTT_RX_BUF_LEN + 1) ///< Maximum size of the SHADOW buffer to store the received Shadow message +#define MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES 80 ///< Maximum size of the Unique Client Id. For More info on the Client Id refer \ref response "Acknowledgments" +#define MAX_SIZE_CLIENT_ID_WITH_SEQUENCE (MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES + 10) ///< This is size of the extra sequence number that will be appended to the Unique client Id +#define MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE (MAX_SIZE_CLIENT_ID_WITH_SEQUENCE + 20) ///< This is size of the the total clientToken key and value pair in the JSON +#define MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME 10 ///< At Any given time we will wait for this many responses. This will correlate to the rate at which the shadow actions are requested +#define MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME 10 ///< We could perform shadow action on any thing Name and this is maximum Thing Names we can act on at any given time +#define MAX_JSON_TOKEN_EXPECTED 120 ///< These are the max tokens that is expected to be in the Shadow JSON document. Include the metadata that gets published +#define MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME 60 ///< All shadow actions have to be published or subscribed to a topic which is of the formablogt $aws/things/{thingName}/shadow/update/accepted. This refers to the size of the topic without the Thing Name +#define MAX_SIZE_OF_THING_NAME 20 ///< The Thing Name should not be bigger than this value. Modify this if the Thing Name needs to be bigger +#define MAX_SHADOW_TOPIC_LENGTH_BYTES (MAX_SHADOW_TOPIC_LENGTH_WITHOUT_THINGNAME + MAX_SIZE_OF_THING_NAME) ///< This size includes the length of topic with Thing Name + +// Auto Reconnect specific config +#define AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL 1000 ///< Minimum time before the First reconnect attempt is made as part of the exponential back-off algorithm +#define AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL 128000 ///< Maximum time interval after which exponential back-off will stop attempting to reconnect. + +#ifdef __cplusplus +} +#endif + +#endif /* _AWS_IOT_CONFIG_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_error.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_error.h new file mode 100644 index 00000000000..03f297b21c9 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_error.h @@ -0,0 +1,160 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_error.h + * @brief Definition of error types for the SDK. + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_ERROR_H_ +#define AWS_IOT_SDK_SRC_IOT_ERROR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Used to avoid warnings in case of unused parameters in function pointers */ +#define IOT_UNUSED(x) (void)(x) + +/*! \public + * @brief IoT Error enum + * + * Enumeration of return values from the IoT_* functions within the SDK. + * Values less than -1 are specific error codes + * Value of -1 is a generic failure response + * Value of 0 is a generic success response + * Values greater than 0 are specific non-error return codes + */ +typedef enum { + /** Returned when the Network physical layer is connected */ + NETWORK_PHYSICAL_LAYER_CONNECTED = 6, + /** Returned when the Network is manually disconnected */ + NETWORK_MANUALLY_DISCONNECTED = 5, + /** Returned when the Network is disconnected and the reconnect attempt is in progress */ + NETWORK_ATTEMPTING_RECONNECT = 4, + /** Return value of yield function to indicate auto-reconnect was successful */ + NETWORK_RECONNECTED = 3, + /** Returned when a read attempt is made on the TLS buffer and it is empty */ + MQTT_NOTHING_TO_READ = 2, + /** Returned when a connection request is successful and packet response is connection accepted */ + MQTT_CONNACK_CONNECTION_ACCEPTED = 1, + /** Success return value - no error occurred */ + SUCCESS = 0, + /** A generic error. Not enough information for a specific error code */ + FAILURE = -1, + /** A required parameter was passed as null */ + NULL_VALUE_ERROR = -2, + /** The TCP socket could not be established */ + TCP_CONNECTION_ERROR = -3, + /** The TLS handshake failed */ + SSL_CONNECTION_ERROR = -4, + /** Error associated with setting up the parameters of a Socket */ + TCP_SETUP_ERROR = -5, + /** A timeout occurred while waiting for the TLS handshake to complete. */ + NETWORK_SSL_CONNECT_TIMEOUT_ERROR = -6, + /** A Generic write error based on the platform used */ + NETWORK_SSL_WRITE_ERROR = -7, + /** SSL initialization error at the TLS layer */ + NETWORK_SSL_INIT_ERROR = -8, + /** An error occurred when loading the certificates. The certificates could not be located or are incorrectly formatted. */ + NETWORK_SSL_CERT_ERROR = -9, + /** SSL Write times out */ + NETWORK_SSL_WRITE_TIMEOUT_ERROR = -10, + /** SSL Read times out */ + NETWORK_SSL_READ_TIMEOUT_ERROR = -11, + /** A Generic error based on the platform used */ + NETWORK_SSL_READ_ERROR = -12, + /** Returned when the Network is disconnected and reconnect is either disabled or physical layer is disconnected */ + NETWORK_DISCONNECTED_ERROR = -13, + /** Returned when the Network is disconnected and the reconnect attempt has timed out */ + NETWORK_RECONNECT_TIMED_OUT_ERROR = -14, + /** Returned when the Network is already connected and a connection attempt is made */ + NETWORK_ALREADY_CONNECTED_ERROR = -15, + /** Network layer Error Codes */ + /** Network layer Random number generator seeding failed */ + NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED = -16, + /** A generic error code for Network layer errors */ + NETWORK_SSL_UNKNOWN_ERROR = -17, + /** Returned when the physical layer is disconnected */ + NETWORK_PHYSICAL_LAYER_DISCONNECTED = -18, + /** Returned when the root certificate is invalid */ + NETWORK_X509_ROOT_CRT_PARSE_ERROR = -19, + /** Returned when the device certificate is invalid */ + NETWORK_X509_DEVICE_CRT_PARSE_ERROR = -20, + /** Returned when the private key failed to parse */ + NETWORK_PK_PRIVATE_KEY_PARSE_ERROR = -21, + /** Returned when the network layer failed to open a socket */ + NETWORK_ERR_NET_SOCKET_FAILED = -22, + /** Returned when the server is unknown */ + NETWORK_ERR_NET_UNKNOWN_HOST = -23, + /** Returned when connect request failed */ + NETWORK_ERR_NET_CONNECT_FAILED = -24, + /** Returned when there is nothing to read in the TLS read buffer */ + NETWORK_SSL_NOTHING_TO_READ = -25, + /** A connection could not be established. */ + MQTT_CONNECTION_ERROR = -26, + /** A timeout occurred while waiting for the TLS handshake to complete */ + MQTT_CONNECT_TIMEOUT_ERROR = -27, + /** A timeout occurred while waiting for the TLS request complete */ + MQTT_REQUEST_TIMEOUT_ERROR = -28, + /** The current client state does not match the expected value */ + MQTT_UNEXPECTED_CLIENT_STATE_ERROR = -29, + /** The client state is not idle when request is being made */ + MQTT_CLIENT_NOT_IDLE_ERROR = -30, + /** The MQTT RX buffer received corrupt or unexpected message */ + MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR = -31, + /** The MQTT RX buffer received a bigger message. The message will be dropped */ + MQTT_RX_BUFFER_TOO_SHORT_ERROR = -32, + /** The MQTT TX buffer is too short for the outgoing message. Request will fail */ + MQTT_TX_BUFFER_TOO_SHORT_ERROR = -33, + /** The client is subscribed to the maximum possible number of subscriptions */ + MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR = -34, + /** Failed to decode the remaining packet length on incoming packet */ + MQTT_DECODE_REMAINING_LENGTH_ERROR = -35, + /** Connect request failed with the server returning an unknown error */ + MQTT_CONNACK_UNKNOWN_ERROR = -36, + /** Connect request failed with the server returning an unacceptable protocol version error */ + MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = -37, + /** Connect request failed with the server returning an identifier rejected error */ + MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR = -38, + /** Connect request failed with the server returning an unavailable error */ + MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR = -39, + /** Connect request failed with the server returning a bad userdata error */ + MQTT_CONNACK_BAD_USERDATA_ERROR = -40, + /** Connect request failed with the server failing to authenticate the request */ + MQTT_CONNACK_NOT_AUTHORIZED_ERROR = -41, + /** An error occurred while parsing the JSON string. Usually malformed JSON. */ + JSON_PARSE_ERROR = -42, + /** Shadow: The response Ack table is currently full waiting for previously published updates */ + SHADOW_WAIT_FOR_PUBLISH = -43, + /** Any time an snprintf writes more than size value, this error will be returned */ + SHADOW_JSON_BUFFER_TRUNCATED = -44, + /** Any time an snprintf encounters an encoding error or not enough space in the given buffer */ + SHADOW_JSON_ERROR = -45, + /** Mutex initialization failed */ + MUTEX_INIT_ERROR = -46, + /** Mutex lock request failed */ + MUTEX_LOCK_ERROR = -47, + /** Mutex unlock request failed */ + MUTEX_UNLOCK_ERROR = -48, + /** Mutex destroy failed */ + MUTEX_DESTROY_ERROR = -49, +} IoT_Error_t; + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_IOT_ERROR_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.c new file mode 100644 index 00000000000..1614eb26f28 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.c @@ -0,0 +1,219 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_json_utils.c + * @brief Utilities for manipulating JSON + * + * json_utils provides JSON parsing utilities for use with the IoT SDK. + * Underlying JSON parsing relies on the Jasmine JSON parser. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_json_utils.h" + +#include +#include +#include + +#ifdef __cplusplus +#include +#else + +#include + +#endif + +#include "aws_iot_log.h" + +int8_t jsoneq(const char *json, jsmntok_t *tok, const char *s) { + if(tok->type == JSMN_STRING) { + if((int) strlen(s) == tok->end - tok->start) { + if(strncmp(json + tok->start, s, (size_t) (tok->end - tok->start)) == 0) { + return 0; + } + } + } + return -1; +} + +IoT_Error_t parseUnsignedInteger32Value(uint32_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%" SCNu32, i))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t parseUnsignedInteger16Value(uint16_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%" SCNu16, i))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t parseUnsignedInteger8Value(uint8_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + uint32_t i_word; + if(('-' == (char) (jsonString[token->start])) || (1 != sscanf(jsonString + token->start, "%" SCNu32, &i_word))) { + IOT_WARN("Token was not an unsigned integer."); + return JSON_PARSE_ERROR; + } + if (i_word > UINT8_MAX) { + IOT_WARN("Token value %u exceeds 8 bits", i_word); + return JSON_PARSE_ERROR; + } + *i = i_word; + + return SUCCESS; +} + +IoT_Error_t parseInteger32Value(int32_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%" SCNi32, i)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t parseInteger16Value(int16_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + int32_t i_word; + if(1 != sscanf(jsonString + token->start, "%" SCNi32, &i_word)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + if(i_word < INT16_MIN || i_word > INT16_MAX) { + IOT_WARN("Token value %d out of range for 16-bit int", i_word); + return JSON_PARSE_ERROR; + } + *i = i_word; + + return SUCCESS; +} + +IoT_Error_t parseInteger8Value(int8_t *i, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not an integer"); + return JSON_PARSE_ERROR; + } + + int32_t i_word; + if(1 != sscanf(jsonString + token->start, "%" SCNi32, &i_word)) { + IOT_WARN("Token was not an integer."); + return JSON_PARSE_ERROR; + } + if(i_word < INT8_MIN || i_word > INT8_MAX) { + IOT_WARN("Token value %d out of range for 8-bit int", i_word); + return JSON_PARSE_ERROR; + } + *i = i_word; + + return SUCCESS; +} + +IoT_Error_t parseFloatValue(float *f, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a float."); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%f", f)) { + IOT_WARN("Token was not a float."); + return JSON_PARSE_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t parseDoubleValue(double *d, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a double."); + return JSON_PARSE_ERROR; + } + + if(1 != sscanf(jsonString + token->start, "%lf", d)) { + IOT_WARN("Token was not a double."); + return JSON_PARSE_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t parseBooleanValue(bool *b, const char *jsonString, jsmntok_t *token) { + if(token->type != JSMN_PRIMITIVE) { + IOT_WARN("Token was not a primitive."); + return JSON_PARSE_ERROR; + } + if(jsonString[token->start] == 't' && jsonString[token->start + 1] == 'r' && jsonString[token->start + 2] == 'u' + && jsonString[token->start + 3] == 'e') { + *b = true; + } else if(jsonString[token->start] == 'f' && jsonString[token->start + 1] == 'a' + && jsonString[token->start + 2] == 'l' && jsonString[token->start + 3] == 's' + && jsonString[token->start + 4] == 'e') { + *b = false; + } else { + IOT_WARN("Token was not a bool."); + return JSON_PARSE_ERROR; + } + return SUCCESS; +} + +IoT_Error_t parseStringValue(char *buf, const char *jsonString, jsmntok_t *token) { + uint16_t size = 0; + if(token->type != JSMN_STRING) { + IOT_WARN("Token was not a string."); + return JSON_PARSE_ERROR; + } + size = (uint16_t) (token->end - token->start); + memcpy(buf, jsonString + token->start, size); + buf[size] = '\0'; + return SUCCESS; +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.h new file mode 100644 index 00000000000..ceb02b31d0c --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_json_utils.h @@ -0,0 +1,197 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_json_utils.h + * @brief Utilities for manipulating JSON + * + * json_utils provides JSON parsing utilities for use with the IoT SDK. + * Underlying JSON parsing relies on the Jasmine JSON parser. + * + */ + +#ifndef AWS_IOT_SDK_SRC_JSON_UTILS_H_ +#define AWS_IOT_SDK_SRC_JSON_UTILS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "aws_iot_error.h" +#include "jsmn.h" + +// utility functions +/** + * @brief JSON Equality Check + * + * Given a token pointing to a particular JSON node and an + * input string, check to see if the key is equal to the string. + * + * @param json json string + * @param tok json token - pointer to key to test for equality + * @param s input string for key to test equality + * + * @return 0 if equal, 1 otherwise + */ +int8_t jsoneq(const char *json, jsmntok_t *tok, const char *s); + +/** + * @brief Parse a signed 32-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int32_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger32Value(int32_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a signed 16-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int16_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger16Value(int16_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a signed 8-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of int8_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseInteger8Value(int8_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 32-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint32_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger32Value(uint32_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 16-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint16_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger16Value(uint16_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse an unsigned 8-bit integer value from a JSON node. + * + * Given a JSON node parse the integer value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param i address of uint8_t to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseUnsignedInteger8Value(uint8_t *i, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a float value from a JSON node. + * + * Given a JSON node parse the float value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param f address of float to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseFloatValue(float *f, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a double value from a JSON node. + * + * Given a JSON node parse the double value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param d address of double to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseDoubleValue(double *d, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a boolean value from a JSON node. + * + * Given a JSON node parse the boolean value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param b address of boolean to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseBooleanValue(bool *b, const char *jsonString, jsmntok_t *token); + +/** + * @brief Parse a string value from a JSON node. + * + * Given a JSON node parse the string value from the value. + * + * @param jsonString json string + * @param tok json token - pointer to JSON node + * @param s address of string to be updated + * + * @return SUCCESS - success + * @return JSON_PARSE_ERROR - error parsing value + */ +IoT_Error_t parseStringValue(char *buf, const char *jsonString, jsmntok_t *token); + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_JSON_UTILS_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_log.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_log.h new file mode 100644 index 00000000000..60c26086c98 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_log.h @@ -0,0 +1,131 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_log.h + * @brief Logging macros for the SDK. + * This file defines common logging macros with log levels to be used within the SDK. + * These macros can also be used in the IoT application code as a common way to output + * logs. The log levels can be tuned by modifying the makefile. Removing (commenting + * out) the IOT_* statement in the makefile disables that log level. + * + * It is expected that the macros below will be modified or replaced when porting to + * specific hardware platforms as printf may not be the desired behavior. + */ + +#ifndef _IOT_LOG_H +#define _IOT_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief Debug level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_DEBUG +#define IOT_DEBUG(...) \ + {\ + printf("DEBUG: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_DEBUG(...) +#endif + +/** + * @brief Debug level trace logging macro. + * + * Macro to print message function entry and exit + */ +#ifdef ENABLE_IOT_TRACE +#define FUNC_ENTRY \ + {\ + printf("FUNC_ENTRY: %s L#%d \n", __func__, __LINE__); \ + } +#define FUNC_EXIT \ + {\ + printf("FUNC_EXIT: %s L#%d \n", __func__, __LINE__); \ + } +#define FUNC_EXIT_RC(x) \ + {\ + printf("FUNC_EXIT: %s L#%d Return Code : %d \n", __func__, __LINE__, x); \ + return x; \ + } +#else +#define FUNC_ENTRY + +#define FUNC_EXIT +#define FUNC_EXIT_RC(x) { return x; } +#endif + +/** + * @brief Info level logging macro. + * + * Macro to expose desired log message. Info messages do not include automatic function names and line numbers. + */ +#ifdef ENABLE_IOT_INFO +#define IOT_INFO(...) \ + {\ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_INFO(...) +#endif + +/** + * @brief Warn level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_WARN +#define IOT_WARN(...) \ + { \ + printf("WARN: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_WARN(...) +#endif + +/** + * @brief Error level logging macro. + * + * Macro to expose function, line number as well as desired log message. + */ +#ifdef ENABLE_IOT_ERROR +#define IOT_ERROR(...) \ + { \ + printf("ERROR: %s L#%d ", __func__, __LINE__); \ + printf(__VA_ARGS__); \ + printf("\n"); \ + } +#else +#define IOT_ERROR(...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif // _IOT_LOG_H diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.c new file mode 100644 index 00000000000..29d2d768b56 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.c @@ -0,0 +1,311 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client.c + * @brief MQTT client API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_log.h" +#include "aws_iot_config.h" +#include "aws_iot_mqtt_client_interface.h" + +#ifdef _ENABLE_THREAD_SUPPORT_ +#include "threads_interface.h" +#endif + +const IoT_Client_Init_Params iotClientInitParamsDefault = IoT_Client_Init_Params_initializer; +const IoT_MQTT_Will_Options iotMqttWillOptionsDefault = IoT_MQTT_Will_Options_Initializer; +const IoT_Client_Connect_Params iotClientConnectParamsDefault = IoT_Client_Connect_Params_initializer; + +ClientState aws_iot_mqtt_get_client_state(AWS_IoT_Client *pClient) { + FUNC_ENTRY; + if(NULL == pClient) { + return CLIENT_STATE_INVALID; + } + + FUNC_EXIT_RC(pClient->clientStatus.clientState); +} + +#ifdef _ENABLE_THREAD_SUPPORT_ +IoT_Error_t aws_iot_mqtt_client_lock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex) { + FUNC_ENTRY; + IoT_Error_t threadRc = FAILURE; + + if(NULL == pClient || NULL == pMutex){ + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(false == pClient->clientData.isBlockOnThreadLockEnabled) { + threadRc = aws_iot_thread_mutex_trylock(pMutex); + } else { + threadRc = aws_iot_thread_mutex_lock(pMutex); + /* Should never return Error because the above request blocks until lock is obtained */ + } + + if(SUCCESS != threadRc) { + FUNC_EXIT_RC(threadRc); + } + + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_client_unlock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex) { + if(NULL == pClient || NULL == pMutex) { + return NULL_VALUE_ERROR; + } + IOT_UNUSED(pClient); + return aws_iot_thread_mutex_unlock(pMutex); +} +#endif + +IoT_Error_t aws_iot_mqtt_set_client_state(AWS_IoT_Client *pClient, ClientState expectedCurrentState, + ClientState newState) { + IoT_Error_t rc; +#ifdef _ENABLE_THREAD_SUPPORT_ + IoT_Error_t threadRc = FAILURE; +#endif + + FUNC_ENTRY; + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.state_change_mutex)); + if(SUCCESS != rc) { + return rc; + } +#endif + if(expectedCurrentState == aws_iot_mqtt_get_client_state(pClient)) { + pClient->clientStatus.clientState = newState; + rc = SUCCESS; + } else { + rc = MQTT_UNEXPECTED_CLIENT_STATE_ERROR; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.state_change_mutex)); + if(SUCCESS == rc && SUCCESS != threadRc) { + rc = threadRc; + } +#endif + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_mqtt_set_connect_params(AWS_IoT_Client *pClient, const IoT_Client_Connect_Params *pNewConnectParams) { + FUNC_ENTRY; + if(NULL == pClient || NULL == pNewConnectParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + pClient->clientData.options.isWillMsgPresent = pNewConnectParams->isWillMsgPresent; + pClient->clientData.options.MQTTVersion = pNewConnectParams->MQTTVersion; + pClient->clientData.options.pClientID = pNewConnectParams->pClientID; + pClient->clientData.options.clientIDLen = pNewConnectParams->clientIDLen; + pClient->clientData.options.pUsername = pNewConnectParams->pUsername; + pClient->clientData.options.usernameLen = pNewConnectParams->usernameLen; + pClient->clientData.options.pPassword = pNewConnectParams->pUsername; + pClient->clientData.options.passwordLen = pNewConnectParams->passwordLen; + pClient->clientData.options.will.pTopicName = pNewConnectParams->will.pTopicName; + pClient->clientData.options.will.topicNameLen = pNewConnectParams->will.topicNameLen; + pClient->clientData.options.will.pMessage = pNewConnectParams->will.pMessage; + pClient->clientData.options.will.msgLen = pNewConnectParams->will.msgLen; + pClient->clientData.options.will.qos = pNewConnectParams->will.qos; + pClient->clientData.options.will.isRetained = pNewConnectParams->will.isRetained; + pClient->clientData.options.keepAliveIntervalInSec = pNewConnectParams->keepAliveIntervalInSec; + pClient->clientData.options.isCleanSession = pNewConnectParams->isCleanSession; + + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_init(AWS_IoT_Client *pClient, const IoT_Client_Init_Params *pInitParams) { + uint32_t i; + IoT_Error_t rc; + IoT_Client_Connect_Params default_options = IoT_Client_Connect_Params_initializer; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pInitParams || NULL == pInitParams->pHostURL || 0 == pInitParams->port || + NULL == pInitParams->pRootCALocation || NULL == pInitParams->pDevicePrivateKeyLocation || + NULL == pInitParams->pDeviceCertLocation) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + pClient->clientData.messageHandlers[i].topicName = NULL; + pClient->clientData.messageHandlers[i].pApplicationHandler = NULL; + pClient->clientData.messageHandlers[i].pApplicationHandlerData = NULL; + pClient->clientData.messageHandlers[i].qos = QOS0; + } + + pClient->clientData.packetTimeoutMs = pInitParams->mqttPacketTimeout_ms; + pClient->clientData.commandTimeoutMs = pInitParams->mqttCommandTimeout_ms; + pClient->clientData.writeBufSize = AWS_IOT_MQTT_TX_BUF_LEN; + pClient->clientData.readBufSize = AWS_IOT_MQTT_RX_BUF_LEN; + pClient->clientData.counterNetworkDisconnected = 0; + pClient->clientData.disconnectHandler = pInitParams->disconnectHandler; + pClient->clientData.disconnectHandlerData = pInitParams->disconnectHandlerData; + pClient->clientData.nextPacketId = 1; + + /* Initialize default connection options */ + rc = aws_iot_mqtt_set_connect_params(pClient, &default_options); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + pClient->clientData.isBlockOnThreadLockEnabled = pInitParams->isBlockOnThreadLockEnabled; + rc = aws_iot_thread_mutex_init(&(pClient->clientData.state_change_mutex)); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_read_mutex)); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + rc = aws_iot_thread_mutex_init(&(pClient->clientData.tls_write_mutex)); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } +#endif + + pClient->clientStatus.isPingOutstanding = 0; + pClient->clientStatus.isAutoReconnectEnabled = pInitParams->enableAutoReconnect; + + rc = iot_tls_init(&(pClient->networkStack), pInitParams->pRootCALocation, pInitParams->pDeviceCertLocation, + pInitParams->pDevicePrivateKeyLocation, pInitParams->pHostURL, pInitParams->port, + pInitParams->tlsHandshakeTimeout_ms, pInitParams->isSSLHostnameVerify); + + if(SUCCESS != rc) { + pClient->clientStatus.clientState = CLIENT_STATE_INVALID; + FUNC_EXIT_RC(rc); + } + + init_timer(&(pClient->pingTimer)); + init_timer(&(pClient->reconnectDelayTimer)); + + pClient->clientStatus.clientState = CLIENT_STATE_INITIALIZED; + + FUNC_EXIT_RC(SUCCESS); +} + +uint16_t aws_iot_mqtt_get_next_packet_id(AWS_IoT_Client *pClient) { + return pClient->clientData.nextPacketId = (uint16_t) ((MAX_PACKET_ID == pClient->clientData.nextPacketId) ? 1 : ( + pClient->clientData.nextPacketId + 1)); +} + +bool aws_iot_mqtt_is_client_connected(AWS_IoT_Client *pClient) { + bool isConnected; + + FUNC_ENTRY; + + if(NULL == pClient) { + IOT_WARN(" Client is null! "); + FUNC_EXIT_RC(false); + } + + switch(pClient->clientStatus.clientState) { + case CLIENT_STATE_INVALID: + case CLIENT_STATE_INITIALIZED: + case CLIENT_STATE_CONNECTING: + isConnected = false; + break; + case CLIENT_STATE_CONNECTED_IDLE: + case CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN: + isConnected = true; + break; + case CLIENT_STATE_DISCONNECTING: + case CLIENT_STATE_DISCONNECTED_ERROR: + case CLIENT_STATE_DISCONNECTED_MANUALLY: + case CLIENT_STATE_PENDING_RECONNECT: + default: + isConnected = false; + break; + } + + FUNC_EXIT_RC(isConnected); +} + +bool aws_iot_is_autoreconnect_enabled(AWS_IoT_Client *pClient) { + FUNC_ENTRY; + if(NULL == pClient) { + IOT_WARN(" Client is null! "); + FUNC_EXIT_RC(false); + } + + FUNC_EXIT_RC(pClient->clientStatus.isAutoReconnectEnabled); +} + +IoT_Error_t aws_iot_mqtt_autoreconnect_set_status(AWS_IoT_Client *pClient, bool newStatus) { + FUNC_ENTRY; + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + pClient->clientStatus.isAutoReconnectEnabled = newStatus; + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_set_disconnect_handler(AWS_IoT_Client *pClient, iot_disconnect_handler pDisconnectHandler, + void *pDisconnectHandlerData) { + FUNC_ENTRY; + if(NULL == pClient || NULL == pDisconnectHandler) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + pClient->clientData.disconnectHandler = pDisconnectHandler; + pClient->clientData.disconnectHandlerData = pDisconnectHandlerData; + FUNC_EXIT_RC(SUCCESS); +} + +uint32_t aws_iot_mqtt_get_network_disconnected_count(AWS_IoT_Client *pClient) { + return pClient->clientData.counterNetworkDisconnected; +} + +void aws_iot_mqtt_reset_network_disconnected_count(AWS_IoT_Client *pClient) { + pClient->clientData.counterNetworkDisconnected = 0; +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.h new file mode 100644 index 00000000000..17c03d82585 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client.h @@ -0,0 +1,418 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client.h + * @brief Client definition for MQTT + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_MQTT_CLIENT_H_ +#define AWS_IOT_SDK_SRC_IOT_MQTT_CLIENT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Library Header files */ +#include "stdio.h" +#include "stdbool.h" +#include "stdint.h" +#include "stddef.h" + +/* AWS Specific header files */ +#include "aws_iot_error.h" +#include "aws_iot_config.h" + +/* Platform specific implementation header files */ +#include "network_interface.h" +#include "timer_interface.h" + +#ifdef _ENABLE_THREAD_SUPPORT_ +#include "threads_interface.h" +#endif + +#define MAX_PACKET_ID 65535 + +typedef struct _Client AWS_IoT_Client; + +/** + * @brief Quality of Service Type + * + * Defining a QoS type. + * @note QoS 2 is \b NOT supported by the AWS IoT Service at the time of this SDK release. + * + */ +typedef enum QoS { + QOS0 = 0, + QOS1 = 1 +} QoS; + +/** + * @brief Publish Message Parameters Type + * + * Defines a type for MQTT Publish messages. Used for both incoming and out going messages + * + */ +typedef struct { + QoS qos; ///< Message Quality of Service + uint8_t isRetained; ///< Retained messages are \b NOT supported by the AWS IoT Service at the time of this SDK release. + uint8_t isDup; ///< Is this message a duplicate QoS > 0 message? Handled automatically by the MQTT client. + uint16_t id; ///< Message sequence identifier. Handled automatically by the MQTT client. + void *payload; ///< Pointer to MQTT message payload (bytes). + size_t payloadLen; ///< Length of MQTT payload. +} IoT_Publish_Message_Params; + +/** + * @brief MQTT Version Type + * + * Defining an MQTT version type. Only 3.1.1 is supported at this time + * + */ +typedef enum { + MQTT_3_1_1 = 4 ///< MQTT 3.1.1 (protocol message byte = 4) +} MQTT_Ver_t; + +/** + * @brief Last Will and Testament Definition + * + * Defining a type for the MQTT "Last Will and Testament" (LWT) parameters. + * @note Retained messages are \b NOT supported by the AWS IoT Service at the time of this SDK release. + * + */ +typedef struct { + char struct_id[4]; ///< The eyecatcher for this structure. must be MQTW + char *pTopicName; ///< The LWT topic to which the LWT message will be published + uint16_t topicNameLen; ///< The length of the LWT topic, 16 bit unsinged integer + char *pMessage; ///< Message to be delivered as LWT + uint16_t msgLen; ///< The length of the Message, 16 bit unsinged integer + bool isRetained; ///< NOT supported. The retained flag for the LWT message (see MQTTAsync_message.retained) + QoS qos; ///< QoS of LWT message +} IoT_MQTT_Will_Options; +extern const IoT_MQTT_Will_Options iotMqttWillOptionsDefault; + +#define IoT_MQTT_Will_Options_Initializer { {'M', 'Q', 'T', 'W'}, NULL, 0, NULL, 0, false, QOS0 } + +/** + * @brief MQTT Connection Parameters + * + * Defining a type for MQTT connection parameters. Passed into client when establishing a connection. + * + */ +typedef struct { + char struct_id[4]; ///< The eyecatcher for this structure. must be MQTC + MQTT_Ver_t MQTTVersion; ///< Desired MQTT version used during connection + const char *pClientID; ///< Pointer to a string defining the MQTT client ID (this needs to be unique \b per \b device across your AWS account) + uint16_t clientIDLen; ///< Client Id Length. 16 bit unsigned integer + uint16_t keepAliveIntervalInSec; ///< MQTT keep alive interval in seconds. Defines inactivity time allowed before determining the connection has been lost. + bool isCleanSession; ///< MQTT clean session. True = this session is to be treated as clean. Previous server state is cleared and no stated is retained from this connection. + bool isWillMsgPresent; ///< Is there a LWT associated with this connection? + IoT_MQTT_Will_Options will; ///< MQTT LWT parameters. + char *pUsername; ///< Not used in the AWS IoT Service, will need to be cstring if used + uint16_t usernameLen; ///< Username Length. 16 bit unsigned integer + char *pPassword; ///< Not used in the AWS IoT Service, will need to be cstring if used + uint16_t passwordLen; ///< Password Length. 16 bit unsigned integer +} IoT_Client_Connect_Params; +extern const IoT_Client_Connect_Params iotClientConnectParamsDefault; + +#define IoT_Client_Connect_Params_initializer { {'M', 'Q', 'T', 'C'}, MQTT_3_1_1, NULL, 0, 60, true, false, \ + IoT_MQTT_Will_Options_Initializer, NULL, 0, NULL, 0 } + +/** + * @brief Disconnect Callback Handler Type + * + * Defining a TYPE for definition of disconnect callback function pointers. + * + */ +typedef void (*iot_disconnect_handler)(AWS_IoT_Client *, void *); + +/** + * @brief MQTT Initialization Parameters + * + * Defining a type for MQTT initialization parameters. + * Passed into client when to initialize the client + * + */ +typedef struct { + bool enableAutoReconnect; ///< Set to true to enable auto reconnect + char *pHostURL; ///< Pointer to a string defining the endpoint for the MQTT service + uint16_t port; ///< MQTT service listening port + const char *pRootCALocation; ///< Pointer to a string defining the Root CA file (full file, not path) + const char *pDeviceCertLocation; ///< Pointer to a string defining the device identity certificate file (full file, not path) + const char *pDevicePrivateKeyLocation; ///< Pointer to a string defining the device private key file (full file, not path) + uint32_t mqttPacketTimeout_ms; ///< Timeout for reading a complete MQTT packet. In milliseconds + uint32_t mqttCommandTimeout_ms; ///< Timeout for MQTT blocking calls. In milliseconds + uint32_t tlsHandshakeTimeout_ms; ///< TLS handshake timeout. In milliseconds + bool isSSLHostnameVerify; ///< Client should perform server certificate hostname validation + iot_disconnect_handler disconnectHandler; ///< Callback to be invoked upon connection loss + void *disconnectHandlerData; ///< Data to pass as argument when disconnect handler is called +#ifdef _ENABLE_THREAD_SUPPORT_ + bool isBlockOnThreadLockEnabled; ///< Timeout for Thread blocking calls. Set to 0 to block until lock is obtained. In milliseconds +#endif +} IoT_Client_Init_Params; +extern const IoT_Client_Init_Params iotClientInitParamsDefault; + +#ifdef _ENABLE_THREAD_SUPPORT_ +#define IoT_Client_Init_Params_initializer { true, NULL, 0, NULL, NULL, NULL, 2000, 20000, 5000, true, NULL, NULL, false } +#else +#define IoT_Client_Init_Params_initializer { true, NULL, 0, NULL, NULL, NULL, 2000, 20000, 5000, true, NULL, NULL } +#endif + +/** + * @brief MQTT Client State Type + * + * Defining a type for MQTT Client State + * + */ +typedef enum _ClientState { + CLIENT_STATE_INVALID = 0, + CLIENT_STATE_INITIALIZED = 1, + CLIENT_STATE_CONNECTING = 2, + CLIENT_STATE_CONNECTED_IDLE = 3, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS = 4, + CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS = 5, + CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS = 6, + CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS = 7, + CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS = 8, + CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN = 9, + CLIENT_STATE_DISCONNECTING = 10, + CLIENT_STATE_DISCONNECTED_ERROR = 11, + CLIENT_STATE_DISCONNECTED_MANUALLY = 12, + CLIENT_STATE_PENDING_RECONNECT = 13 +} ClientState; + +/** + * @brief Application Callback Handler Type + * + * Defining a TYPE for definition of application callback function pointers. + * Used to send incoming data to the application + * + */ +typedef void (*pApplicationHandler_t)(AWS_IoT_Client *pClient, char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams, void *pClientData); + +/** + * @brief MQTT Message Handler + * + * Defining a type for MQTT Message Handlers. + * Used to pass incoming data back to the application + * + */ +typedef struct _MessageHandlers { + const char *topicName; + uint16_t topicNameLen; + QoS qos; + pApplicationHandler_t pApplicationHandler; + void *pApplicationHandlerData; +} MessageHandlers; /* Message handlers are indexed by subscription topic */ + +/** + * @brief MQTT Client Status + * + * Defining a type for MQTT Client Status + * Contains information about the state of the MQTT Client + * + */ +typedef struct _ClientStatus { + ClientState clientState; + bool isPingOutstanding; + bool isAutoReconnectEnabled; +} ClientStatus; + +/** + * @brief MQTT Client Data + * + * Defining a type for MQTT Client Data + * Contains data used by the MQTT Client + * + */ +typedef struct _ClientData { + uint16_t nextPacketId; + + uint32_t packetTimeoutMs; + uint32_t commandTimeoutMs; + uint16_t keepAliveInterval; + uint32_t currentReconnectWaitInterval; + uint32_t counterNetworkDisconnected; + + /* The below values are initialized with the + * lengths of the TX/RX buffers and never modified + * afterwards */ + size_t writeBufSize; + size_t readBufSize; + + unsigned char writeBuf[AWS_IOT_MQTT_TX_BUF_LEN]; + unsigned char readBuf[AWS_IOT_MQTT_RX_BUF_LEN]; + +#ifdef _ENABLE_THREAD_SUPPORT_ + bool isBlockOnThreadLockEnabled; + IoT_Mutex_t state_change_mutex; + IoT_Mutex_t tls_read_mutex; + IoT_Mutex_t tls_write_mutex; +#endif + + IoT_Client_Connect_Params options; + + MessageHandlers messageHandlers[AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS]; + iot_disconnect_handler disconnectHandler; + + void *disconnectHandlerData; +} ClientData; + +/** + * @brief MQTT Client + * + * Defining a type for MQTT Client + * + */ +struct _Client { + Timer pingTimer; + Timer reconnectDelayTimer; + + ClientStatus clientStatus; + ClientData clientData; + Network networkStack; +}; + +/** + * @brief What is the next available packet Id + * + * Called to retrieve the next packet id to be used for outgoing packets. + * Automatically increments the last sent packet id variable + * + * @param pClient Reference to the IoT Client + * + * @return next packet id as a 16 bit unsigned integer + */ +uint16_t aws_iot_mqtt_get_next_packet_id(AWS_IoT_Client *pClient); + +/** + * @brief Set the connection parameters for the IoT Client + * + * Called to set the connection parameters for the IoT Client. + * Used to update the connection parameters provided before the last connect. + * Won't take effect until the next time connect is called + * + * @param pClient Reference to the IoT Client + * @param pNewConnectParams Reference to the new Connection Parameters structure + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_set_connect_params(AWS_IoT_Client *pClient, const IoT_Client_Connect_Params *pNewConnectParams); + +/** + * @brief Is the MQTT client currently connected? + * + * Called to determine if the MQTT client is currently connected. Used to support logic + * in the device application around reconnecting and managing offline state. + * + * @param pClient Reference to the IoT Client + * + * @return true = connected, false = not currently connected + */ +bool aws_iot_mqtt_is_client_connected(AWS_IoT_Client *pClient); + +/** + * @brief Get the current state of the client + * + * Called to get the current state of the client + * + * @param pClient Reference to the IoT Client + * + * @return ClientState value equal to the current state of the client + */ +ClientState aws_iot_mqtt_get_client_state(AWS_IoT_Client *pClient); + +/** + * @brief Is the MQTT client set to reconnect automatically? + * + * Called to determine if the MQTT client is set to reconnect automatically. + * Used to support logic in the device application around reconnecting + * + * @param pClient Reference to the IoT Client + * + * @return true = enabled, false = disabled + */ +bool aws_iot_is_autoreconnect_enabled(AWS_IoT_Client *pClient); + +/** + * @brief Set the IoT Client disconnect handler + * + * Called to set the IoT Client disconnect handler + * The disconnect handler is called whenever the client disconnects with error + * + * @param pClient Reference to the IoT Client + * @param pConnectHandler Reference to the new Disconnect Handler + * @param pDisconnectHandlerData Reference to the data to be passed as argument when disconnect handler is called + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_set_disconnect_handler(AWS_IoT_Client *pClient, iot_disconnect_handler pDisconnectHandler, + void *pDisconnectHandlerData); + +/** + * @brief Enable or Disable AutoReconnect on Network Disconnect + * + * Called to enable or disabled the auto reconnect features provided with the SDK + * + * @param pClient Reference to the IoT Client + * @param newStatus set to true for enabling and false for disabling + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_autoreconnect_set_status(AWS_IoT_Client *pClient, bool newStatus); + +/** + * @brief Get count of Network Disconnects + * + * Called to get the number of times a network disconnect occurred due to errors + * + * @param pClient Reference to the IoT Client + * + * @return uint32_t the disconnect count + */ +uint32_t aws_iot_mqtt_get_network_disconnected_count(AWS_IoT_Client *pClient); + +/** + * @brief Reset Network Disconnect conter + * + * Called to reset the Network Disconnect counter to zero + * + * @param pClient Reference to the IoT Client + */ +void aws_iot_mqtt_reset_network_disconnected_count(AWS_IoT_Client *pClient); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.c new file mode 100644 index 00000000000..5de914ad274 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.c @@ -0,0 +1,685 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Sergio R. Caprile - non-blocking packet read functions for stream transport + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_common_internal.c + * @brief MQTT client internal API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "aws_iot_mqtt_client_common_internal.h" + +/* Max length of packet header */ +#define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 + +/** + * Encodes the message length according to the MQTT algorithm + * @param buf the buffer into which the encoded data is written + * @param length the length to be encoded + * @return the number of bytes written to buffer + */ +size_t aws_iot_mqtt_internal_write_len_to_buffer(unsigned char *buf, uint32_t length) { + size_t outLen = 0; + unsigned char encodedByte; + + FUNC_ENTRY; + do { + encodedByte = (unsigned char) (length % 128); + length /= 128; + /* if there are more digits to encode, set the top bit of this digit */ + if(length > 0) { + encodedByte |= 0x80; + } + buf[outLen++] = encodedByte; + } while(length > 0); + + FUNC_EXIT_RC(outLen); +} + +/** + * Decodes the message length according to the MQTT algorithm + * @param the buffer containing the message + * @param value the decoded length returned + * @return the number of bytes read from the socket + */ +IoT_Error_t aws_iot_mqtt_internal_decode_remaining_length_from_buffer(unsigned char *buf, uint32_t *decodedLen, + uint32_t *readBytesLen) { + unsigned char encodedByte; + uint32_t multiplier, len; + FUNC_ENTRY; + + multiplier = 1; + len = 0; + *decodedLen = 0; + + do { + if(++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) { + /* bad data */ + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + encodedByte = *buf; + buf++; + *decodedLen += (encodedByte & 127) * multiplier; + multiplier *= 128; + } while((encodedByte & 128) != 0); + + *readBytesLen = len; + + FUNC_EXIT_RC(SUCCESS); +} + +uint32_t aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(uint32_t rem_len) { + rem_len += 1; /* header byte */ + /* now remaining_length field (MQTT 3.1.1 - 2.2.3)*/ + if(rem_len < 128) { + rem_len += 1; + } else if(rem_len < 16384) { + rem_len += 2; + } else if(rem_len < 2097152) { + rem_len += 3; + } else { + rem_len += 4; + } + return rem_len; +} + +/** + * Calculates uint16 packet id from two bytes read from the input buffer + * Checks Endianness at runtime + * + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the value calculated + */ +uint16_t aws_iot_mqtt_internal_read_uint16_t(unsigned char **pptr) { + unsigned char *ptr = *pptr; + uint16_t len = 0; + uint8_t firstByte = (uint8_t) (*ptr); + uint8_t secondByte = (uint8_t) (*(ptr + 1)); + len = (uint16_t) (secondByte + (256 * firstByte)); + + *pptr += 2; + return len; +} + +/** + * Writes an integer as 2 bytes to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param anInt the integer to write + */ +void aws_iot_mqtt_internal_write_uint_16(unsigned char **pptr, uint16_t anInt) { + **pptr = (unsigned char) (anInt / 256); + (*pptr)++; + **pptr = (unsigned char) (anInt % 256); + (*pptr)++; +} + +/** + * Reads one character from the input buffer. + * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned + * @return the character read + */ +unsigned char aws_iot_mqtt_internal_read_char(unsigned char **pptr) { + unsigned char c = **pptr; + (*pptr)++; + return c; +} + +/** + * Writes one character to an output buffer. + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param c the character to write + */ +void aws_iot_mqtt_internal_write_char(unsigned char **pptr, unsigned char c) { + **pptr = c; + (*pptr)++; +} + +void aws_iot_mqtt_internal_write_utf8_string(unsigned char **pptr, const char *string, uint16_t stringLen) { + /* Nothing that calls this function will have a stringLen with a size larger than 2 bytes (MQTT 3.1.1 - 1.5.3) */ + aws_iot_mqtt_internal_write_uint_16(pptr, stringLen); + if(stringLen > 0) { + memcpy(*pptr, string, stringLen); + *pptr += stringLen; + } +} + +/** + * Initialize the MQTTHeader structure. Used to ensure that Header bits are + * always initialized using the proper mappings. No Endianness issues here since + * the individual fields are all less than a byte. Also generates no warnings since + * all fields are initialized using hex constants + */ +IoT_Error_t aws_iot_mqtt_internal_init_header(MQTTHeader *pHeader, MessageTypes message_type, + QoS qos, uint8_t dup, uint8_t retained) { + FUNC_ENTRY; + + if(NULL == pHeader) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Set all bits to zero */ + pHeader->byte = 0; + switch(message_type) { + case UNKNOWN: + /* Should never happen */ + return FAILURE; + case CONNECT: + pHeader->bits.type = 0x01; + break; + case CONNACK: + pHeader->bits.type = 0x02; + break; + case PUBLISH: + pHeader->bits.type = 0x03; + break; + case PUBACK: + pHeader->bits.type = 0x04; + break; + case PUBREC: + pHeader->bits.type = 0x05; + break; + case PUBREL: + pHeader->bits.type = 0x06; + break; + case PUBCOMP: + pHeader->bits.type = 0x07; + break; + case SUBSCRIBE: + pHeader->bits.type = 0x08; + break; + case SUBACK: + pHeader->bits.type = 0x09; + break; + case UNSUBSCRIBE: + pHeader->bits.type = 0x0A; + break; + case UNSUBACK: + pHeader->bits.type = 0x0B; + break; + case PINGREQ: + pHeader->bits.type = 0x0C; + break; + case PINGRESP: + pHeader->bits.type = 0x0D; + break; + case DISCONNECT: + pHeader->bits.type = 0x0E; + break; + default: + /* Should never happen */ + FUNC_EXIT_RC(FAILURE); + } + + pHeader->bits.dup = (1 == dup) ? 0x01 : 0x00; + switch(qos) { + case QOS0: + pHeader->bits.qos = 0x00; + break; + case QOS1: + pHeader->bits.qos = 0x01; + break; + default: + /* Using QOS0 as default */ + pHeader->bits.qos = 0x00; + break; + } + + pHeader->bits.retain = (1 == retained) ? 0x01 : 0x00; + + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_internal_send_packet(AWS_IoT_Client *pClient, size_t length, Timer *pTimer) { + + size_t sentLen, sent; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTimer) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(length >= pClient->clientData.writeBufSize) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.tls_write_mutex)); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } +#endif + + sentLen = 0; + sent = 0; + + while(sent < length && !has_timer_expired(pTimer)) { + rc = pClient->networkStack.write(&(pClient->networkStack), &pClient->clientData.writeBuf[sent], length - sent, pTimer, + &sentLen); + if(SUCCESS != rc) { + /* there was an error writing the data */ + break; + } + sent += sentLen; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + rc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.tls_write_mutex)); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } +#endif + + if(sent == length) { + /* record the fact that we have successfully sent the packet */ + //countdown_sec(&c->pingTimer, c->clientData.keepAliveInterval); + FUNC_EXIT_RC(SUCCESS); + } + + FUNC_EXIT_RC(FAILURE); +} + +static IoT_Error_t _aws_iot_mqtt_internal_decode_packet_remaining_len(AWS_IoT_Client *pClient, + size_t *rem_len, Timer *pTimer) { + unsigned char encodedByte; + size_t multiplier, len; + IoT_Error_t rc; + + FUNC_ENTRY; + + multiplier = 1; + len = 0; + *rem_len = 0; + + do { + if(++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) { + /* bad data */ + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + + rc = pClient->networkStack.read(&(pClient->networkStack), &encodedByte, 1, pTimer, &len); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + *rem_len += ((encodedByte & 127) * multiplier); + multiplier *= 128; + } while((encodedByte & 128) != 0); + + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_internal_read_packet(AWS_IoT_Client *pClient, Timer *pTimer, uint8_t *pPacketType) { + size_t len, rem_len, total_bytes_read, bytes_to_be_read, read_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + Timer packetTimer; + init_timer(&packetTimer); + countdown_ms(&packetTimer, pClient->clientData.packetTimeoutMs); + + len = 0; + rem_len = 0; + total_bytes_read = 0; + bytes_to_be_read = 0; + read_len = 0; + + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf, 1, pTimer, &read_len); + /* 1. read the header byte. This has the packet type in it */ + if(NETWORK_SSL_NOTHING_TO_READ == rc) { + return MQTT_NOTHING_TO_READ; + } else if(SUCCESS != rc) { + return rc; + } + + len = 1; + + /* Use the constant packet receive timeout, instead of the variable (remaining) pTimer time, to + * determine packet receiving timeout. This is done so we don't prematurely time out packet receiving + * if the remaining time in pTimer is too short. + */ + pTimer = &packetTimer; + + /* 2. read the remaining length. This is variable in itself */ + rc = _aws_iot_mqtt_internal_decode_packet_remaining_len(pClient, &rem_len, pTimer); + if(SUCCESS != rc) { + return rc; + } + + /* if the buffer is too short then the message will be dropped silently */ + if(rem_len >= pClient->clientData.readBufSize) { + bytes_to_be_read = pClient->clientData.readBufSize; + do { + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf, bytes_to_be_read, + pTimer, &read_len); + if(SUCCESS == rc) { + total_bytes_read += read_len; + if((rem_len - total_bytes_read) >= pClient->clientData.readBufSize) { + bytes_to_be_read = pClient->clientData.readBufSize; + } else { + bytes_to_be_read = rem_len - total_bytes_read; + } + } + } while(total_bytes_read < rem_len && SUCCESS == rc); + return MQTT_RX_BUFFER_TOO_SHORT_ERROR; + } + + /* put the original remaining length into the read buffer */ + len += aws_iot_mqtt_internal_write_len_to_buffer(pClient->clientData.readBuf + 1, (uint32_t) rem_len); + + /* 3. read the rest of the buffer using a callback to supply the rest of the data */ + if(rem_len > 0) { + rc = pClient->networkStack.read(&(pClient->networkStack), pClient->clientData.readBuf + len, rem_len, pTimer, + &read_len); + if(SUCCESS != rc || read_len != rem_len) { + return FAILURE; + } + } + + header.byte = pClient->clientData.readBuf[0]; + *pPacketType = header.bits.type; + + FUNC_EXIT_RC(rc); +} + +// assume topic filter and name is in correct format +// # can only be at end +// + and # can only be next to separator +static char _aws_iot_mqtt_internal_is_topic_matched(char *pTopicFilter, char *pTopicName, uint16_t topicNameLen) { + + char *curf, *curn, *curn_end; + + if(NULL == pTopicFilter || NULL == pTopicName) { + return NULL_VALUE_ERROR; + } + + curf = pTopicFilter; + curn = pTopicName; + curn_end = curn + topicNameLen; + + while(*curf && (curn < curn_end)) { + if(*curn == '/' && *curf != '/') { + break; + } + if(*curf != '+' && *curf != '#' && *curf != *curn) { + break; + } + if(*curf == '+') { + /* skip until we meet the next separator, or end of string */ + char *nextpos = curn + 1; + while(nextpos < curn_end && *nextpos != '/') + nextpos = ++curn + 1; + } else if(*curf == '#') { + /* skip until end of string */ + curn = curn_end - 1; + } + + curf++; + curn++; + }; + + return (curn == curn_end) && (*curf == '\0'); +} + +static IoT_Error_t _aws_iot_mqtt_internal_deliver_message(AWS_IoT_Client *pClient, char *pTopicName, + uint16_t topicNameLen, + IoT_Publish_Message_Params *pMessageParams) { + uint32_t itr; + IoT_Error_t rc; + ClientState clientState; + + FUNC_ENTRY; + + if(NULL == pTopicName) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* This function can be called from all MQTT APIs + * But while callback return is in progress, Yield should not be called. + * The state for CB_RETURN accomplishes that, as yield cannot be called while in that state */ + clientState = aws_iot_mqtt_get_client_state(pClient); + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN); + + /* Find the right message handler - indexed by topic */ + for(itr = 0; itr < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++itr) { + if(NULL != pClient->clientData.messageHandlers[itr].topicName) { + if(((topicNameLen == pClient->clientData.messageHandlers[itr].topicNameLen) + && + (strncmp(pTopicName, (char *) pClient->clientData.messageHandlers[itr].topicName, topicNameLen) == 0)) + || _aws_iot_mqtt_internal_is_topic_matched((char *) pClient->clientData.messageHandlers[itr].topicName, + pTopicName, topicNameLen)) { + if(NULL != pClient->clientData.messageHandlers[itr].pApplicationHandler) { + pClient->clientData.messageHandlers[itr].pApplicationHandler(pClient, pTopicName, topicNameLen, + pMessageParams, + pClient->clientData.messageHandlers[itr].pApplicationHandlerData); + } + } + } + } + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN, clientState); + + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_internal_handle_publish(AWS_IoT_Client *pClient, Timer *pTimer) { + char *topicName; + uint16_t topicNameLen; + uint32_t len; + IoT_Error_t rc; + IoT_Publish_Message_Params msg; + + FUNC_ENTRY; + + topicName = NULL; + topicNameLen = 0; + len = 0; + + rc = aws_iot_mqtt_internal_deserialize_publish(&msg.isDup, &msg.qos, &msg.isRetained, + &msg.id, &topicName, &topicNameLen, + (unsigned char **) &msg.payload, &msg.payloadLen, + pClient->clientData.readBuf, + pClient->clientData.readBufSize); + + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_internal_deliver_message(pClient, topicName, topicNameLen, &msg); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + if(QOS0 == msg.qos) { + /* No further processing required for QoS0 */ + FUNC_EXIT_RC(SUCCESS); + } + + /* Message assumed to be QoS1 since we do not support QoS2 at this time */ + rc = aws_iot_mqtt_internal_serialize_ack(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + PUBACK, 0, msg.id, &len); + + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_send_packet(pClient, len, pTimer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_mqtt_internal_cycle_read(AWS_IoT_Client *pClient, Timer *pTimer, uint8_t *pPacketType) { + IoT_Error_t rc; + +#ifdef _ENABLE_THREAD_SUPPORT_ + IoT_Error_t threadRc; +#endif + + if(NULL == pClient || NULL == pTimer) { + return NULL_VALUE_ERROR; + } + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_lock_mutex(pClient, &(pClient->clientData.tls_read_mutex)); + if(SUCCESS != threadRc) { + FUNC_EXIT_RC(threadRc); + } +#endif + + /* read the socket, see what work is due */ + rc = _aws_iot_mqtt_internal_read_packet(pClient, pTimer, pPacketType); + +#ifdef _ENABLE_THREAD_SUPPORT_ + threadRc = aws_iot_mqtt_client_unlock_mutex(pClient, &(pClient->clientData.tls_read_mutex)); + if(SUCCESS != threadRc && (MQTT_NOTHING_TO_READ == rc || SUCCESS == rc)) { + return threadRc; + } +#endif + + if(MQTT_NOTHING_TO_READ == rc) { + /* Nothing to read, not a cycle failure */ + return SUCCESS; + } else if(SUCCESS != rc) { + return rc; + } + + switch(*pPacketType) { + case CONNACK: + case PUBACK: + case SUBACK: + case UNSUBACK: + /* SDK is blocking, these responses will be forwarded to calling function to process */ + break; + case PUBLISH: { + rc = _aws_iot_mqtt_internal_handle_publish(pClient, pTimer); + break; + } + case PUBREC: + case PUBCOMP: + /* QoS2 not supported at this time */ + break; + case PINGRESP: { + pClient->clientStatus.isPingOutstanding = 0; + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + break; + } + default: { + /* Either unknown packet type or Failure occurred + * Should not happen */ + rc = MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR; + break; + } + } + + return rc; +} + +/* only used in single-threaded mode where one command at a time is in process */ +IoT_Error_t aws_iot_mqtt_internal_wait_for_read(AWS_IoT_Client *pClient, uint8_t packetType, Timer *pTimer) { + IoT_Error_t rc; + uint8_t read_packet_type; + + FUNC_ENTRY; + if(NULL == pClient || NULL == pTimer) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + read_packet_type = 0; + do { + if(has_timer_expired(pTimer)) { + /* we timed out */ + rc = MQTT_REQUEST_TIMEOUT_ERROR; + break; + } + rc = aws_iot_mqtt_internal_cycle_read(pClient, pTimer, &read_packet_type); + } while(NETWORK_DISCONNECTED_ERROR != rc && read_packet_type != packetType); + + if(MQTT_REQUEST_TIMEOUT_ERROR != rc && NETWORK_DISCONNECTED_ERROR != rc && read_packet_type != packetType) { + FUNC_EXIT_RC(FAILURE); + } + + /* Something failed or we didn't receive the expected packet, return error code */ + FUNC_EXIT_RC(rc); +} + +/** + * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket + * @param buf the buffer into which the packet will be serialized + * @param buflen the length in bytes of the supplied buffer, to avoid overruns + * @param packettype the message type + * @param serialized length + * @return IoT_Error_t indicating function execution status + */ +IoT_Error_t aws_iot_mqtt_internal_serialize_zero(unsigned char *pTxBuf, size_t txBufLen, MessageTypes packetType, + size_t *pSerializedLength) { + unsigned char *ptr; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pSerializedLength) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Buffer should have at least 2 bytes for the header */ + if(4 > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + ptr = pTxBuf; + + rc = aws_iot_mqtt_internal_init_header(&header, packetType, QOS0, 0, 0); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* write header */ + aws_iot_mqtt_internal_write_char(&ptr, header.byte); + + /* write remaining length */ + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, 0); + *pSerializedLength = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.h new file mode 100644 index 00000000000..d962c8828a5 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_common_internal.h @@ -0,0 +1,141 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_common_internal.h + * @brief Internal MQTT functions not exposed to application + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H +#define AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "aws_iot_mqtt_client_interface.h" + +/* Enum order should match the packet ids array defined in MQTTFormat.c */ +typedef enum msgTypes { + UNKNOWN = -1, + CONNECT = 1, + CONNACK = 2, + PUBLISH = 3, + PUBACK = 4, + PUBREC = 5, + PUBREL = 6, + PUBCOMP = 7, + SUBSCRIBE = 8, + SUBACK = 9, + UNSUBSCRIBE = 10, + UNSUBACK = 11, + PINGREQ = 12, + PINGRESP = 13, + DISCONNECT = 14 +} MessageTypes; + +/** + * Bitfields for the MQTT header byte. + */ +typedef union { + unsigned char byte; /**< the whole byte */ +#if defined(REVERSED) + struct { + unsigned int type : 4; /**< message type nibble */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int retain : 1; /**< retained flag bit */ + } bits; +#else + struct { + unsigned int retain : 1; /**< retained flag bit */ + unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ + unsigned int dup : 1; /**< DUP flag bit */ + unsigned int type : 4; /**< message type nibble */ + } bits; +#endif +} MQTTHeader; + +IoT_Error_t aws_iot_mqtt_internal_init_header(MQTTHeader *pHeader, MessageTypes message_type, + QoS qos, uint8_t dup, uint8_t retained); + +IoT_Error_t aws_iot_mqtt_internal_serialize_ack(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes msgType, uint8_t dup, uint16_t packetId, + uint32_t *pSerializedLen); +IoT_Error_t aws_iot_mqtt_internal_deserialize_ack(unsigned char *, unsigned char *, + uint16_t *, unsigned char *, size_t); + +uint32_t aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(uint32_t rem_len); + +size_t aws_iot_mqtt_internal_write_len_to_buffer(unsigned char *buf, uint32_t length); +IoT_Error_t aws_iot_mqtt_internal_decode_remaining_length_from_buffer(unsigned char *buf, uint32_t *decodedLen, + uint32_t *readBytesLen); + +uint16_t aws_iot_mqtt_internal_read_uint16_t(unsigned char **pptr); +void aws_iot_mqtt_internal_write_uint_16(unsigned char **pptr, uint16_t anInt); + +unsigned char aws_iot_mqtt_internal_read_char(unsigned char **pptr); +void aws_iot_mqtt_internal_write_char(unsigned char **pptr, unsigned char c); +void aws_iot_mqtt_internal_write_utf8_string(unsigned char **pptr, const char *string, uint16_t stringLen); + +IoT_Error_t aws_iot_mqtt_internal_send_packet(AWS_IoT_Client *pClient, size_t length, Timer *pTimer); +IoT_Error_t aws_iot_mqtt_internal_cycle_read(AWS_IoT_Client *pClient, Timer *pTimer, uint8_t *pPacketType); +IoT_Error_t aws_iot_mqtt_internal_wait_for_read(AWS_IoT_Client *pClient, uint8_t packetType, Timer *pTimer); +IoT_Error_t aws_iot_mqtt_internal_serialize_zero(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes packetType, size_t *pSerializedLength); +IoT_Error_t aws_iot_mqtt_internal_deserialize_publish(uint8_t *dup, QoS *qos, + uint8_t *retained, uint16_t *pPacketId, + char **pTopicName, uint16_t *topicNameLen, + unsigned char **payload, size_t *payloadLen, + unsigned char *pRxBuf, size_t rxBufLen); + +IoT_Error_t aws_iot_mqtt_set_client_state(AWS_IoT_Client *pClient, ClientState expectedCurrentState, + ClientState newState); + +#ifdef _ENABLE_THREAD_SUPPORT_ + +IoT_Error_t aws_iot_mqtt_client_lock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex); + +IoT_Error_t aws_iot_mqtt_client_unlock_mutex(AWS_IoT_Client *pClient, IoT_Mutex_t *pMutex); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_SDK_SRC_IOT_COMMON_INTERNAL_H */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_connect.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_connect.c new file mode 100644 index 00000000000..6cd0ffee223 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_connect.c @@ -0,0 +1,621 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_connect.c + * @brief MQTT client connect API definition and related functions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_mqtt_client_common_internal.h" + +typedef union { + uint8_t all; /**< all connect flags */ +#if defined(REVERSED) + struct + { + unsigned int username : 1; /**< 3.1 user name */ + unsigned int password : 1; /**< 3.1 password */ + unsigned int willRetain : 1; /**< will retain setting */ + unsigned int willQoS : 2; /**< will QoS value */ + unsigned int will : 1; /**< will flag */ + unsigned int cleansession : 1; /**< clean session flag */ + unsigned int : 1; /**< unused */ + } bits; +#else + struct { + unsigned int : 1; + /**< unused */ + unsigned int cleansession : 1; + /**< cleansession flag */ + unsigned int will : 1; + /**< will flag */ + unsigned int willQoS : 2; + /**< will QoS value */ + unsigned int willRetain : 1; + /**< will retain setting */ + unsigned int password : 1; + /**< 3.1 password */ + unsigned int username : 1; /**< 3.1 user name */ + } bits; +#endif +} MQTT_Connect_Header_Flags; +/**< connect flags byte */ + +typedef union { + uint8_t all; /**< all connack flags */ +#if defined(REVERSED) + struct + { + unsigned int sessionpresent : 1; /**< session present flag */ + unsigned int : 7; /**< unused */ + } bits; +#else + struct { + unsigned int : 7; + /**< unused */ + unsigned int sessionpresent : 1; /**< session present flag */ + } bits; +#endif +} MQTT_Connack_Header_Flags; +/**< connack flags byte */ + +typedef enum { + CONNACK_CONNECTION_ACCEPTED = 0, + CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR = 1, + CONNACK_IDENTIFIER_REJECTED_ERROR = 2, + CONNACK_SERVER_UNAVAILABLE_ERROR = 3, + CONNACK_BAD_USERDATA_ERROR = 4, + CONNACK_NOT_AUTHORIZED_ERROR = 5 +} MQTT_Connack_Return_Codes; /**< Connect request response codes from server */ + + +/** + * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. + * @param options the options to be used to build the connect packet + * @param the length of buffer needed to contain the serialized version of the packet + * @return IoT_Error_t indicating function execution status + */ +static uint32_t _aws_iot_get_connect_packet_length(IoT_Client_Connect_Params *pConnectParams) { + uint32_t len; + /* Enable when adding further MQTT versions */ + /*size_t len = 0; + switch(pConnectParams->MQTTVersion) { + case MQTT_3_1_1: + len = 10; + break; + }*/ + FUNC_ENTRY; + + len = 10; // Len = 10 for MQTT_3_1_1 + len = len + pConnectParams->clientIDLen + 2; + + if(pConnectParams->isWillMsgPresent) { + len = len + pConnectParams->will.topicNameLen + 2 + pConnectParams->will.msgLen + 2; + } + + if(NULL != pConnectParams->pUsername) { + len = len + pConnectParams->usernameLen + 2; + } + + if(NULL != pConnectParams->pPassword) { + len = len + pConnectParams->passwordLen + 2; + } + + FUNC_EXIT_RC(len); +} + +/** + * Serializes the connect options into the buffer. + * @param buf the buffer into which the packet will be serialized + * @param len the length in bytes of the supplied buffer + * @param options the options to be used to build the connect packet + * @param serialized length + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_serialize_connect(unsigned char *pTxBuf, size_t txBufLen, + IoT_Client_Connect_Params *pConnectParams, + size_t *pSerializedLen) { + unsigned char *ptr; + uint32_t len; + IoT_Error_t rc; + MQTTHeader header = {0}; + MQTT_Connect_Header_Flags flags = {0}; + + FUNC_ENTRY; + + if(NULL == pTxBuf || NULL == pConnectParams || NULL == pSerializedLen || + (NULL == pConnectParams->pClientID && 0 != pConnectParams->clientIDLen) || + (NULL != pConnectParams->pClientID && 0 == pConnectParams->clientIDLen)) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* Check needed here before we start writing to the Tx buffer */ + switch(pConnectParams->MQTTVersion) { + case MQTT_3_1_1: + break; + default: + return MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR; + } + + ptr = pTxBuf; + len = _aws_iot_get_connect_packet_length(pConnectParams); + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, CONNECT, QOS0, 0, 0); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, len); /* write remaining length */ + + // Enable if adding support for more versions + //if(MQTT_3_1_1 == pConnectParams->MQTTVersion) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, "MQTT", 4); + aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pConnectParams->MQTTVersion); + //} + + flags.all = 0; + flags.bits.cleansession = (pConnectParams->isCleanSession) ? 1 : 0; + flags.bits.will = (pConnectParams->isWillMsgPresent) ? 1 : 0; + if(flags.bits.will) { + flags.bits.willQoS = pConnectParams->will.qos; + flags.bits.willRetain = (pConnectParams->will.isRetained) ? 1 : 0; + } + + if(pConnectParams->pUsername) { + flags.bits.username = 1; + } + + if(pConnectParams->pPassword) { + flags.bits.password = 1; + } + + aws_iot_mqtt_internal_write_char(&ptr, flags.all); + aws_iot_mqtt_internal_write_uint_16(&ptr, pConnectParams->keepAliveIntervalInSec); + + /* If the code have passed the check for incorrect values above, no client id was passed as argument */ + if(NULL == pConnectParams->pClientID) { + aws_iot_mqtt_internal_write_uint_16(&ptr, 0); + } else { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pClientID, pConnectParams->clientIDLen); + } + + if(pConnectParams->isWillMsgPresent) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pTopicName, + pConnectParams->will.topicNameLen); + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pMessage, pConnectParams->will.msgLen); + } + + if(flags.bits.username) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pUsername, pConnectParams->usernameLen); + } + + if(flags.bits.password) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pPassword, pConnectParams->passwordLen); + } + + *pSerializedLen = (size_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into connack data - return code + * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) + * @param connack_rc returned integer value of the connack return code + * @param buf the raw buffer data, of the correct length determined by the remaining length field + * @param buflen the length in bytes of the data in the supplied buffer + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_connack(unsigned char *pSessionPresent, IoT_Error_t *pConnackRc, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curdata, *enddata; + unsigned char connack_rc_char; + uint32_t decodedLen, readBytesLen; + IoT_Error_t rc; + MQTT_Connack_Header_Flags flags = {0}; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == pSessionPresent || NULL == pConnackRc || NULL == pRxBuf) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* CONNACK header size is fixed at two bytes for fixed and 2 bytes for variable, + * using that as minimum size + * MQTT v3.1.1 Specification 3.2.1 */ + if(4 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + curdata = pRxBuf; + enddata = NULL; + decodedLen = 0; + readBytesLen = 0; + + header.byte = aws_iot_mqtt_internal_read_char(&curdata); + if(CONNACK != header.bits.type) { + FUNC_EXIT_RC(FAILURE); + } + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curdata, &decodedLen, &readBytesLen); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* CONNACK remaining length should always be 2 as per MQTT 3.1.1 spec */ + curdata += (readBytesLen); + enddata = curdata + decodedLen; + if(2 != (enddata - curdata)) { + FUNC_EXIT_RC(MQTT_DECODE_REMAINING_LENGTH_ERROR); + } + + flags.all = aws_iot_mqtt_internal_read_char(&curdata); + *pSessionPresent = flags.bits.sessionpresent; + connack_rc_char = aws_iot_mqtt_internal_read_char(&curdata); + switch(connack_rc_char) { + case CONNACK_CONNECTION_ACCEPTED: + *pConnackRc = MQTT_CONNACK_CONNECTION_ACCEPTED; + break; + case CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR: + *pConnackRc = MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR; + break; + case CONNACK_IDENTIFIER_REJECTED_ERROR: + *pConnackRc = MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR; + break; + case CONNACK_SERVER_UNAVAILABLE_ERROR: + *pConnackRc = MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR; + break; + case CONNACK_BAD_USERDATA_ERROR: + *pConnackRc = MQTT_CONNACK_BAD_USERDATA_ERROR; + break; + case CONNACK_NOT_AUTHORIZED_ERROR: + *pConnackRc = MQTT_CONNACK_NOT_AUTHORIZED_ERROR; + break; + default: + *pConnackRc = MQTT_CONNACK_UNKNOWN_ERROR; + break; + } + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Check if client state is valid for a connect request + * + * Called to check if client state is valid for a connect request + * @param pClient Reference to the IoT Client + * + * @return bool true = state is valid, false = not valid + */ +static bool _aws_iot_mqtt_is_client_state_valid_for_connect(ClientState clientState) { + bool isValid = false; + + switch(clientState) { + case CLIENT_STATE_INVALID: + isValid = false; + break; + case CLIENT_STATE_INITIALIZED: + isValid = true; + break; + case CLIENT_STATE_CONNECTING: + case CLIENT_STATE_CONNECTED_IDLE: + case CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS: + case CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN: + case CLIENT_STATE_DISCONNECTING: + isValid = false; + break; + case CLIENT_STATE_DISCONNECTED_ERROR: + case CLIENT_STATE_DISCONNECTED_MANUALLY: + case CLIENT_STATE_PENDING_RECONNECT: + isValid = true; + break; + default: + break; + } + + return isValid; +} + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * This is the internal function which is called by the connect API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +static IoT_Error_t _aws_iot_mqtt_internal_connect(AWS_IoT_Client *pClient, const IoT_Client_Connect_Params *pConnectParams) { + Timer connect_timer; + IoT_Error_t connack_rc = FAILURE; + char sessionPresent = 0; + size_t len = 0; + IoT_Error_t rc = FAILURE; + + FUNC_ENTRY; + + if(NULL != pConnectParams) { + /* override default options if new options were supplied */ + rc = aws_iot_mqtt_set_connect_params(pClient, pConnectParams); + if(SUCCESS != rc) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + } + + rc = pClient->networkStack.connect(&(pClient->networkStack), NULL); + if(SUCCESS != rc) { + /* TLS Connect failed, return error */ + FUNC_EXIT_RC(rc); + } + + init_timer(&connect_timer); + countdown_ms(&connect_timer, pClient->clientData.commandTimeoutMs); + + pClient->clientData.keepAliveInterval = pClient->clientData.options.keepAliveIntervalInSec; + rc = _aws_iot_mqtt_serialize_connect(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + &(pClient->clientData.options), &len); + if(SUCCESS != rc || 0 >= len) { + FUNC_EXIT_RC(rc); + } + + /* send the connect packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &connect_timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* this will be a blocking call, wait for the CONNACK */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, CONNACK, &connect_timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Received CONNACK, check the return code */ + rc = _aws_iot_mqtt_deserialize_connack((unsigned char *) &sessionPresent, &connack_rc, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + if(MQTT_CONNACK_CONNECTION_ACCEPTED != connack_rc) { + FUNC_EXIT_RC(connack_rc); + } + + pClient->clientStatus.isPingOutstanding = false; + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * This is the outer function which does the validations and calls the internal connect above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_connect(AWS_IoT_Client *pClient, const IoT_Client_Connect_Params *pConnectParams) { + IoT_Error_t rc; + ClientState clientState; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + + if(false == _aws_iot_mqtt_is_client_state_valid_for_connect(clientState)) { + /* Don't send connect packet again if we are already connected + * or in the process of connecting/disconnecting */ + FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR); + } + + aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTING); + + rc = _aws_iot_mqtt_internal_connect(pClient, pConnectParams); + + if(SUCCESS != rc) { + pClient->networkStack.disconnect(&(pClient->networkStack)); + pClient->networkStack.destroy(&(pClient->networkStack)); + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_DISCONNECTED_ERROR); + } else { + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_CONNECTED_IDLE); + } + + FUNC_EXIT_RC(rc); +} + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * This is the internal function which is called by the disconnect API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t _aws_iot_mqtt_internal_disconnect(AWS_IoT_Client *pClient) { + /* We might wait for incomplete incoming publishes to complete */ + Timer timer; + size_t serialized_len = 0; + IoT_Error_t rc; + + FUNC_ENTRY; + + rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + DISCONNECT, + &serialized_len); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + /* send the disconnect packet */ + if(serialized_len > 0) { + rc = aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + /* Clean network stack */ + pClient->networkStack.disconnect(&(pClient->networkStack)); + rc = pClient->networkStack.destroy(&(pClient->networkStack)); + if(0 != rc) { + /* TLS Destroy failed, return error */ + FUNC_EXIT_RC(FAILURE); + } + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * This is the outer function which does the validations and calls the internal disconnect above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t aws_iot_mqtt_disconnect(AWS_IoT_Client *pClient) { + ClientState clientState; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(!aws_iot_mqtt_is_client_connected(pClient)) { + /* Network is already disconnected. Do nothing */ + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_DISCONNECTING); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_internal_disconnect(pClient); + + if(SUCCESS != rc) { + pClient->clientStatus.clientState = clientState; + } else { + /* If called from Keepalive, this gets set to CLIENT_STATE_DISCONNECTED_ERROR */ + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_MANUALLY; + } + + FUNC_EXIT_RC(rc); +} + +/** + * @brief MQTT Manual Re-Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * using parameters from the last time a connection was attempted + * Use after disconnect to start the reconnect process manually + * Makes only one reconnect attempt. Sets the client state to + * pending reconnect in case of failure + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_attempt_reconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR); + } + + /* Ignoring return code. failures expected if network is disconnected */ + rc = aws_iot_mqtt_connect(pClient, NULL); + + /* If still disconnected handle disconnect */ + if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) { + aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, CLIENT_STATE_PENDING_RECONNECT); + FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); + } + + rc = aws_iot_mqtt_resubscribe(pClient); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + FUNC_EXIT_RC(NETWORK_RECONNECTED); +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_interface.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_interface.h new file mode 100644 index 00000000000..fef12ee2a83 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_interface.h @@ -0,0 +1,199 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Xiang Rong - 442039 Add makefile to Embedded C client + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_interface.h + * @brief Interface definition for MQTT client. + */ + +#ifndef AWS_IOT_SDK_SRC_IOT_MQTT_INTERFACE_H +#define AWS_IOT_SDK_SRC_IOT_MQTT_INTERFACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Library Header files */ +#include "stdio.h" +#include "stdbool.h" +#include "stdint.h" +#include "stddef.h" + +/* AWS Specific header files */ +#include "aws_iot_error.h" +#include "aws_iot_config.h" +#include "aws_iot_mqtt_client.h" + +/* Platform specific implementation header files */ +#include "network_interface.h" +#include "timer_interface.h" + +/** + * @brief MQTT Client Initialization Function + * + * Called to initialize the MQTT Client + * + * @param pClient Reference to the IoT Client + * @param pInitParams Pointer to MQTT connection parameters + * + * @return IoT_Error_t Type defining successful/failed API call + */ +IoT_Error_t aws_iot_mqtt_init(AWS_IoT_Client *pClient, const IoT_Client_Init_Params *pInitParams); + +/** + * @brief MQTT Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * + * @param pClient Reference to the IoT Client + * @param pConnectParams Pointer to MQTT connection parameters + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_connect(AWS_IoT_Client *pClient, const IoT_Client_Connect_Params *pConnectParams); + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +IoT_Error_t aws_iot_mqtt_publish(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams); + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * @param pApplicationHandlerData Data to be passed as argument to the application handler callback + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData); + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to resubscribe to the topics that the client has active subscriptions on. + * Internally called when autoreconnect is enabled + * + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_resubscribe(AWS_IoT_Client *pClient); + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +IoT_Error_t aws_iot_mqtt_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, uint16_t topicFilterLen); + +/** + * @brief Disconnect an MQTT Connection + * + * Called to send a disconnect message to the broker. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed send of the disconnect control packet. + */ +IoT_Error_t aws_iot_mqtt_disconnect(AWS_IoT_Client *pClient); + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms); + +/** + * @brief MQTT Manual Re-Connection Function + * + * Called to establish an MQTT connection with the AWS IoT Service + * using parameters from the last time a connection was attempted + * Use after disconnect to start the reconnect process manually + * Makes only one reconnect attempt Sets the client state to + * pending reconnect in case of failure + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed connection + */ +IoT_Error_t aws_iot_mqtt_attempt_reconnect(AWS_IoT_Client *pClient); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_publish.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_publish.c new file mode 100644 index 00000000000..6895d5213f2 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_publish.c @@ -0,0 +1,429 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_publish.c + * @brief MQTT client publish API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * @param stringVar pointer to the String into which the data is to be read + * @param stringLen pointer to variable which has the length of the string + * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned + * @param enddata pointer to the end of the data: do not read beyond + * @return SUCCESS if successful, FAILURE if not + */ +static IoT_Error_t _aws_iot_mqtt_read_string_with_len(char **stringVar, uint16_t *stringLen, + unsigned char **pptr, unsigned char *enddata) { + IoT_Error_t rc = FAILURE; + + FUNC_ENTRY; + /* the first two bytes are the length of the string */ + /* enough length to read the integer? */ + if(enddata - (*pptr) > 1) { + *stringLen = aws_iot_mqtt_internal_read_uint16_t(pptr); /* increments pptr to point past length */ + if(&(*pptr)[*stringLen] <= enddata) { + *stringVar = (char *) *pptr; + *pptr += *stringLen; + rc = SUCCESS; + } + } + + FUNC_EXIT_RC(rc); +} + +/** + * Serializes the supplied publish data into the supplied buffer, ready for sending + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param dup uint8_t - the MQTT dup flag + * @param qos QoS - the MQTT QoS value + * @param retained uint8_t - the MQTT retained flag + * @param packetId uint16_t - the MQTT packet identifier + * @param pTopicName char * - the MQTT topic in the publish + * @param topicNameLen uint16_t - the length of the Topic Name + * @param pPayload byte buffer - the MQTT publish payload + * @param payloadLen size_t - the length of the MQTT payload + * @param pSerializedLen uint32_t - pointer to the variable that stores serialized len + * + * @return An IoT Error Type defining successful/failed call + */ +static IoT_Error_t _aws_iot_mqtt_internal_serialize_publish(unsigned char *pTxBuf, size_t txBufLen, uint8_t dup, + QoS qos, uint8_t retained, uint16_t packetId, + const char *pTopicName, uint16_t topicNameLen, + const unsigned char *pPayload, size_t payloadLen, + uint32_t *pSerializedLen) { + unsigned char *ptr; + uint32_t rem_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pPayload || NULL == pSerializedLen) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + rem_len = 0; + + rem_len += (uint32_t) (topicNameLen + payloadLen + 2); + if(qos > 0) { + rem_len += 2; /* packetId */ + } + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, PUBLISH, qos, dup, retained); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); /* write remaining length */; + + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicName, topicNameLen); + + if(qos > 0) { + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + } + + memcpy(ptr, pPayload, payloadLen); + ptr += payloadLen; + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * Serializes the ack packet into the supplied buffer. + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param msgType the MQTT packet type + * @param dup the MQTT dup flag + * @param packetId the MQTT packet identifier + * @param pSerializedLen uint32_t - pointer to the variable that stores serialized len + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_serialize_ack(unsigned char *pTxBuf, size_t txBufLen, + MessageTypes msgType, uint8_t dup, uint16_t packetId, + uint32_t *pSerializedLen) { + unsigned char *ptr; + QoS requestQoS; + IoT_Error_t rc; + MQTTHeader header = {0}; + FUNC_ENTRY; + if(NULL == pTxBuf || pSerializedLen == NULL) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + + /* Minimum byte length required by ACK headers is + * 2 for fixed and 2 for variable part */ + if(4 > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + requestQoS = (PUBREL == msgType) ? QOS1 : QOS0; + rc = aws_iot_mqtt_internal_init_header(&header, msgType, requestQoS, dup, 0); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, 2); /* write remaining length */ + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * This is the internal function which is called by the publish API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +static IoT_Error_t _aws_iot_mqtt_internal_publish(AWS_IoT_Client *pClient, const char *pTopicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *pParams) { + Timer timer; + uint32_t len = 0; + uint16_t packet_id; + unsigned char dup, type; + IoT_Error_t rc; + + FUNC_ENTRY; + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + if(QOS1 == pParams->qos) { + pParams->id = aws_iot_mqtt_get_next_packet_id(pClient); + } + + rc = _aws_iot_mqtt_internal_serialize_publish(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + pParams->qos, pParams->isRetained, pParams->id, pTopicName, + topicNameLen, (unsigned char *) pParams->payload, + pParams->payloadLen, &len); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the publish packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Wait for ack if QoS1 */ + if(QOS1 == pParams->qos) { + rc = aws_iot_mqtt_internal_wait_for_read(pClient, PUBACK, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_deserialize_ack(&type, &dup, &packet_id, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Publish an MQTT message on a topic + * + * Called to publish an MQTT message on a topic. + * @note Call is blocking. In the case of a QoS 0 message the function returns + * after the message was successfully passed to the TLS layer. In the case of QoS 1 + * the function returns after the receipt of the PUBACK control packet. + * This is the outer function which does the validations and calls the internal publish above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pParams Pointer to Publish Message parameters + * + * @return An IoT Error Type defining successful/failed publish + */ +IoT_Error_t aws_iot_mqtt_publish(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *pParams) { + IoT_Error_t rc, pubRc; + ClientState clientState; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTopicName || 0 == topicNameLen || NULL == pParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + pubRc = _aws_iot_mqtt_internal_publish(pClient, pTopicName, topicNameLen, pParams); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_PUBLISH_IN_PROGRESS, clientState); + if(SUCCESS == pubRc && SUCCESS != rc) { + pubRc = rc; + } + + FUNC_EXIT_RC(pubRc); +} + +/** + * Deserializes the supplied (wire) buffer into publish data + * @param dup returned uint8_t - the MQTT dup flag + * @param qos returned QoS type - the MQTT QoS value + * @param retained returned uint8_t - the MQTT retained flag + * @param pPacketId returned uint16_t - the MQTT packet identifier + * @param pTopicName returned String - the MQTT topic in the publish + * @param topicNameLen returned uint16_t - the length of the MQTT topic in the publish + * @param payload returned byte buffer - the MQTT publish payload + * @param payloadlen returned size_t - the length of the MQTT payload + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_deserialize_publish(uint8_t *dup, QoS *qos, + uint8_t *retained, uint16_t *pPacketId, + char **pTopicName, uint16_t *topicNameLen, + unsigned char **payload, size_t *payloadLen, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curData = pRxBuf; + unsigned char *endData = NULL; + IoT_Error_t rc = FAILURE; + uint32_t decodedLen = 0; + uint32_t readBytesLen = 0; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == dup || NULL == qos || NULL == retained || NULL == pPacketId) { + FUNC_EXIT_RC(FAILURE); + } + + /* Publish header size is at least four bytes. + * Fixed header is two bytes. + * Variable header size depends on QoS And Topic Name. + * QoS level 0 doesn't have a message identifier (0 - 2 bytes) + * Topic Name length fields decide size of topic name field (at least 2 bytes) + * MQTT v3.1.1 Specification 3.3.1 */ + if(4 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + header.byte = aws_iot_mqtt_internal_read_char(&curData); + if(PUBLISH != header.bits.type) { + FUNC_EXIT_RC(FAILURE); + } + + *dup = header.bits.dup; + *qos = (QoS) header.bits.qos; + *retained = header.bits.retain; + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curData, &decodedLen, &readBytesLen); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + return rc; + } + curData += (readBytesLen); + endData = curData + decodedLen; + + /* do we have enough data to read the protocol version byte? */ + if(SUCCESS != _aws_iot_mqtt_read_string_with_len(pTopicName, topicNameLen, &curData, endData) + || (0 > (endData - curData))) { + FUNC_EXIT_RC(FAILURE); + } + + if(QOS0 != *qos) { + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curData); + } + + *payloadLen = (size_t) (endData - curData); + *payload = curData; + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into an ack + * @param pPacketType returned integer - the MQTT packet type + * @param dup returned integer - the MQTT dup flag + * @param pPacketId returned integer - the MQTT packet identifier + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBuflen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed call + */ +IoT_Error_t aws_iot_mqtt_internal_deserialize_ack(unsigned char *pPacketType, unsigned char *dup, + uint16_t *pPacketId, unsigned char *pRxBuf, + size_t rxBuflen) { + IoT_Error_t rc = FAILURE; + unsigned char *curdata = pRxBuf; + unsigned char *enddata = NULL; + uint32_t decodedLen = 0; + uint32_t readBytesLen = 0; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + if(NULL == pPacketType || NULL == dup || NULL == pPacketId || NULL == pRxBuf) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + /* PUBACK fixed header size is two bytes, variable header is 2 bytes, MQTT v3.1.1 Specification 3.4.1 */ + if(4 > rxBuflen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + + header.byte = aws_iot_mqtt_internal_read_char(&curdata); + *dup = header.bits.dup; + *pPacketType = header.bits.type; + + /* read remaining length */ + rc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curdata, &decodedLen, &readBytesLen); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + curdata += (readBytesLen); + enddata = curdata + decodedLen; + + if(enddata - curdata < 2) { + FUNC_EXIT_RC(FAILURE); + } + + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curdata); + + FUNC_EXIT_RC(SUCCESS); +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_subscribe.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_subscribe.c new file mode 100644 index 00000000000..2311f73d14a --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_subscribe.c @@ -0,0 +1,437 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_subscribe.c + * @brief MQTT client subscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * Serializes the supplied subscribe data into the supplied buffer, ready for sending + * @param pTxBuf the buffer into which the packet will be serialized + * @param txBufLen the length in bytes of the supplied buffer + * @param dup unsigned char - the MQTT dup flag + * @param packetId uint16_t - the MQTT packet identifier + * @param topicCount - number of members in the topicFilters and reqQos arrays + * @param pTopicNameList - array of topic filter names + * @param pTopicNameLenList - array of length of topic filter names + * @param pRequestedQoSs - array of requested QoS + * @param pSerializedLen - the length of the serialized data + * + * @return An IoT Error Type defining successful/failed operation + */ +static IoT_Error_t _aws_iot_mqtt_serialize_subscribe(unsigned char *pTxBuf, size_t txBufLen, + unsigned char dup, uint16_t packetId, uint32_t topicCount, + const char **pTopicNameList, uint16_t *pTopicNameLenList, + QoS *pRequestedQoSs, uint32_t *pSerializedLen) { + unsigned char *ptr; + uint32_t itr, rem_len; + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pTxBuf || NULL == pSerializedLen) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + ptr = pTxBuf; + rem_len = 2; /* packetId */ + + for(itr = 0; itr < topicCount; ++itr) { + rem_len += (uint32_t) (pTopicNameLenList[itr] + 2 + 1); /* topic + length + req_qos */ + } + + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, SUBSCRIBE, QOS1, dup, 0); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + /* write header */ + aws_iot_mqtt_internal_write_char(&ptr, header.byte); + + /* write remaining length */ + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); + + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + + for(itr = 0; itr < topicCount; ++itr) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicNameList[itr], pTopicNameLenList[itr]); + aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pRequestedQoSs[itr]); + } + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * Deserializes the supplied (wire) buffer into suback data + * @param pPacketId returned integer - the MQTT packet identifier + * @param maxExpectedQoSCount - the maximum number of members allowed in the grantedQoSs array + * @param pGrantedQoSCount returned uint32_t - number of members in the grantedQoSs array + * @param pGrantedQoSs returned array of QoS type - the granted qualities of service + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * + * @return An IoT Error Type defining successful/failed operation + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_suback(uint16_t *pPacketId, uint32_t maxExpectedQoSCount, + uint32_t *pGrantedQoSCount, QoS *pGrantedQoSs, + unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char *curData, *endData; + uint32_t decodedLen, readBytesLen; + IoT_Error_t decodeRc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + if(NULL == pPacketId || NULL == pGrantedQoSCount || NULL == pGrantedQoSs) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + curData = pRxBuf; + endData = NULL; + decodeRc = FAILURE; + decodedLen = 0; + readBytesLen = 0; + + /* SUBACK header size is 4 bytes for header and at least one byte for QoS payload + * Need at least a 5 bytes buffer. MQTT3.1.1 specification 3.9 + */ + if(5 > rxBufLen) { + FUNC_EXIT_RC(MQTT_RX_BUFFER_TOO_SHORT_ERROR); + } + + header.byte = aws_iot_mqtt_internal_read_char(&curData); + if(SUBACK != header.bits.type) { + FUNC_EXIT_RC(FAILURE); + } + + /* read remaining length */ + decodeRc = aws_iot_mqtt_internal_decode_remaining_length_from_buffer(curData, &decodedLen, &readBytesLen); + if(SUCCESS != decodeRc) { + FUNC_EXIT_RC(decodeRc); + } + + curData += (readBytesLen); + endData = curData + decodedLen; + if(endData - curData < 2) { + FUNC_EXIT_RC(FAILURE); + } + + *pPacketId = aws_iot_mqtt_internal_read_uint16_t(&curData); + + *pGrantedQoSCount = 0; + while(curData < endData) { + if(*pGrantedQoSCount > maxExpectedQoSCount) { + FUNC_EXIT_RC(FAILURE); + } + pGrantedQoSs[(*pGrantedQoSCount)++] = (QoS) aws_iot_mqtt_internal_read_char(&curData); + } + + FUNC_EXIT_RC(SUCCESS); +} + +/* Returns MAX_MESSAGE_HANDLERS value if no free index is available */ +static uint32_t _aws_iot_mqtt_get_free_message_handler_index(AWS_IoT_Client *pClient) { + uint32_t itr; + + FUNC_ENTRY; + + for(itr = 0; itr < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; itr++) { + if(pClient->clientData.messageHandlers[itr].topicName == NULL) { + break; + } + } + + FUNC_EXIT_RC(itr); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. This is the internal function which is called by the + * subscribe API to perform the operation. Not meant to be called directly as + * it doesn't do validations or client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * + * @return An IoT Error Type defining successful/failed subscription + */ +static IoT_Error_t _aws_iot_mqtt_internal_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, + uint16_t topicNameLen, QoS qos, + pApplicationHandler_t pApplicationHandler, + void *pApplicationHandlerData) { + uint16_t txPacketId, rxPacketId; + uint32_t serializedLen, indexOfFreeMessageHandler, count; + IoT_Error_t rc; + Timer timer; + QoS grantedQoS[3] = {QOS0, QOS0, QOS0}; + + FUNC_ENTRY; + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + serializedLen = 0; + count = 0; + txPacketId = aws_iot_mqtt_get_next_packet_id(pClient); + rxPacketId = 0; + + rc = _aws_iot_mqtt_serialize_subscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + txPacketId, 1, &pTopicName, &topicNameLen, &qos, &serializedLen); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + indexOfFreeMessageHandler = _aws_iot_mqtt_get_free_message_handler_index(pClient); + if(AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS <= indexOfFreeMessageHandler) { + FUNC_EXIT_RC(MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR); + } + + /* send the subscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serializedLen, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* wait for suback */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, SUBACK, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Granted QoS can be 0, 1 or 2 */ + rc = _aws_iot_mqtt_deserialize_suback(&rxPacketId, 1, &count, grantedQoS, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* TODO : Figure out how to test this before activating this check */ + //if(txPacketId != rxPacketId) { + /* Different SUBACK received than expected. Return error + * This can cause issues if the request timeout value is too small */ + // return RX_MESSAGE_INVALID_ERROR; + //} + + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].topicName = + pTopicName; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].topicNameLen = + topicNameLen; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].pApplicationHandler = + pApplicationHandler; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].pApplicationHandlerData = + pApplicationHandlerData; + pClient->clientData.messageHandlers[indexOfFreeMessageHandler].qos = qos; + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. This is the outer function which does the validations and + * calls the internal subscribe above to perform the actual operation. + * It is also responsible for client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * @param pApplicationHandler_t Reference to the handler function for this subscription + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen, + QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData) { + ClientState clientState; + IoT_Error_t rc, subRc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pTopicName || NULL == pApplicationHandler) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + subRc = _aws_iot_mqtt_internal_subscribe(pClient, pTopicName, topicNameLen, qos, + pApplicationHandler, pApplicationHandlerData); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS, clientState); + if(SUCCESS == subRc && SUCCESS != rc) { + subRc = rc; + } + + FUNC_EXIT_RC(subRc); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * This is the internal function which is called by the resubscribe API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +static IoT_Error_t _aws_iot_mqtt_internal_resubscribe(AWS_IoT_Client *pClient) { + uint16_t packetId; + uint32_t len, count, existingSubCount, itr; + IoT_Error_t rc; + Timer timer; + QoS grantedQoS[3] = {QOS0, QOS0, QOS0}; + + FUNC_ENTRY; + + packetId = 0; + len = 0; + count = 0; + existingSubCount = _aws_iot_mqtt_get_free_message_handler_index(pClient); + + for(itr = 0; itr < existingSubCount; itr++) { + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + rc = _aws_iot_mqtt_serialize_subscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + aws_iot_mqtt_get_next_packet_id(pClient), 1, + &(pClient->clientData.messageHandlers[itr].topicName), + &(pClient->clientData.messageHandlers[itr].topicNameLen), + &(pClient->clientData.messageHandlers[itr].qos), &len); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the subscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, len, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* wait for suback */ + rc = aws_iot_mqtt_internal_wait_for_read(pClient, SUBACK, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Granted QoS can be 0, 1 or 2 */ + rc = _aws_iot_mqtt_deserialize_suback(&packetId, 1, &count, grantedQoS, pClient->clientData.readBuf, + pClient->clientData.readBufSize); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Subscribe to an MQTT topic. + * + * Called to send a subscribe message to the broker requesting a subscription + * to an MQTT topic. + * This is the outer function which does the validations and calls the internal resubscribe above + * to perform the actual operation. It is also responsible for client state changes + * @note Call is blocking. The call returns after the receipt of the SUBACK control packet. + * + * @param pClient Reference to the IoT Client + * + * @return An IoT Error Type defining successful/failed subscription + */ +IoT_Error_t aws_iot_mqtt_resubscribe(AWS_IoT_Client *pClient) { + IoT_Error_t rc, resubRc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(false == aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + resubRc = _aws_iot_mqtt_internal_resubscribe(pClient); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS, + CLIENT_STATE_CONNECTED_IDLE); + if(SUCCESS == resubRc && SUCCESS != rc) { + resubRc = rc; + } + + FUNC_EXIT_RC(resubRc); +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_unsubscribe.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_unsubscribe.c new file mode 100644 index 00000000000..fcbb97a98bb --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_unsubscribe.c @@ -0,0 +1,249 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_unsubscribe.c + * @brief MQTT client unsubscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending + * @param pTxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param txBufLen the length in bytes of the data in the supplied buffer + * @param dup integer - the MQTT dup flag + * @param packetId integer - the MQTT packet identifier + * @param count - number of members in the topicFilters array + * @param pTopicNameList - array of topic filter names + * @param pTopicNameLenList - array of length of topic filter names in pTopicNameList + * @param pSerializedLen - the length of the serialized data + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_serialize_unsubscribe(unsigned char *pTxBuf, size_t txBufLen, + uint8_t dup, uint16_t packetId, + uint32_t count, const char **pTopicNameList, + uint16_t *pTopicNameLenList, uint32_t *pSerializedLen) { + unsigned char *ptr = pTxBuf; + uint32_t i = 0; + uint32_t rem_len = 2; /* packetId */ + IoT_Error_t rc; + MQTTHeader header = {0}; + + FUNC_ENTRY; + + for(i = 0; i < count; ++i) { + rem_len += (uint32_t) (pTopicNameLenList[i] + 2); /* topic + length */ + } + + if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) { + FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR); + } + + rc = aws_iot_mqtt_internal_init_header(&header, UNSUBSCRIBE, QOS1, dup, 0); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */ + + ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len); /* write remaining length */ + + aws_iot_mqtt_internal_write_uint_16(&ptr, packetId); + + for(i = 0; i < count; ++i) { + aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicNameList[i], pTopicNameLenList[i]); + } + + *pSerializedLen = (uint32_t) (ptr - pTxBuf); + + FUNC_EXIT_RC(SUCCESS); +} + + +/** + * Deserializes the supplied (wire) buffer into unsuback data + * @param pPacketId returned integer - the MQTT packet identifier + * @param pRxBuf the raw buffer data, of the correct length determined by the remaining length field + * @param rxBufLen the length in bytes of the data in the supplied buffer + * @return IoT_Error_t indicating function execution status + */ +static IoT_Error_t _aws_iot_mqtt_deserialize_unsuback(uint16_t *pPacketId, unsigned char *pRxBuf, size_t rxBufLen) { + unsigned char type = 0; + unsigned char dup = 0; + IoT_Error_t rc; + + FUNC_ENTRY; + + rc = aws_iot_mqtt_internal_deserialize_ack(&type, &dup, pPacketId, pRxBuf, rxBufLen); + if(SUCCESS == rc && UNSUBACK != type) { + rc = FAILURE; + } + + FUNC_EXIT_RC(rc); +} + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * This is the internal function which is called by the unsubscribe API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +static IoT_Error_t _aws_iot_mqtt_internal_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, + uint16_t topicFilterLen) { + /* No NULL checks because this is a static internal function */ + + Timer timer; + + uint16_t packet_id; + uint32_t serializedLen = 0; + uint32_t i = 0; + IoT_Error_t rc; + bool subscriptionExists = false; + + FUNC_ENTRY; + + /* Remove from message handler array */ + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + if(pClient->clientData.messageHandlers[i].topicName != NULL && + (strcmp(pClient->clientData.messageHandlers[i].topicName, pTopicFilter) == 0)) { + subscriptionExists = true; + } + } + + if(false == subscriptionExists) { + FUNC_EXIT_RC(FAILURE); + } + + init_timer(&timer); + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + + rc = _aws_iot_mqtt_serialize_unsubscribe(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, 0, + aws_iot_mqtt_get_next_packet_id(pClient), 1, &pTopicFilter, + &topicFilterLen, &serializedLen); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the unsubscribe packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serializedLen, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = aws_iot_mqtt_internal_wait_for_read(pClient, UNSUBACK, &timer); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + rc = _aws_iot_mqtt_deserialize_unsuback(&packet_id, pClient->clientData.readBuf, pClient->clientData.readBufSize); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* Remove from message handler array */ + for(i = 0; i < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++i) { + if(pClient->clientData.messageHandlers[i].topicName != NULL && + (strcmp(pClient->clientData.messageHandlers[i].topicName, pTopicFilter) == 0)) { + pClient->clientData.messageHandlers[i].topicName = NULL; + /* We don't want to break here, in case the same topic is registered + * with 2 callbacks. Unlikely scenario */ + } + } + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Unsubscribe to an MQTT topic. + * + * Called to send an unsubscribe message to the broker requesting removal of a subscription + * to an MQTT topic. + * @note Call is blocking. The call returns after the receipt of the UNSUBACK control packet. + * This is the outer function which does the validations and calls the internal unsubscribe above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param pTopicName Topic Name to publish to + * @param topicNameLen Length of the topic name + * + * @return An IoT Error Type defining successful/failed unsubscribe call + */ +IoT_Error_t aws_iot_mqtt_unsubscribe(AWS_IoT_Client *pClient, const char *pTopicFilter, uint16_t topicFilterLen) { + IoT_Error_t rc, unsubRc; + ClientState clientState; + + if(NULL == pClient || NULL == pTopicFilter) { + return NULL_VALUE_ERROR; + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + return NETWORK_DISCONNECTED_ERROR; + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) { + return MQTT_CLIENT_NOT_IDLE_ERROR; + } + + rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS); + if(SUCCESS != rc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS, clientState); + return rc; + } + + unsubRc = _aws_iot_mqtt_internal_unsubscribe(pClient, pTopicFilter, topicFilterLen); + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_UNSUBSCRIBE_IN_PROGRESS, clientState); + if(SUCCESS == unsubRc && SUCCESS != rc) { + unsubRc = rc; + } + + return unsubRc; +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_yield.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_yield.c new file mode 100644 index 00000000000..c40bcbbb4bd --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_mqtt_client_yield.c @@ -0,0 +1,311 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +// Based on Eclipse Paho. +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file aws_iot_mqtt_client_yield.c + * @brief MQTT client yield API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_mqtt_client_common_internal.h" + +/** + * This is for the case when the aws_iot_mqtt_internal_send_packet Fails. + */ +static void _aws_iot_mqtt_force_client_disconnect(AWS_IoT_Client *pClient) { + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; + pClient->networkStack.disconnect(&(pClient->networkStack)); + pClient->networkStack.destroy(&(pClient->networkStack)); +} + +static IoT_Error_t _aws_iot_mqtt_handle_disconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; + + rc = aws_iot_mqtt_disconnect(pClient); + if(rc != SUCCESS) { + // If the aws_iot_mqtt_internal_send_packet prevents us from sending a disconnect packet then we have to clean the stack + _aws_iot_mqtt_force_client_disconnect(pClient); + } + + if(NULL != pClient->clientData.disconnectHandler) { + pClient->clientData.disconnectHandler(pClient, pClient->clientData.disconnectHandlerData); + } + + /* Reset to 0 since this was not a manual disconnect */ + pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_ERROR; + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); +} + + +static IoT_Error_t _aws_iot_mqtt_handle_reconnect(AWS_IoT_Client *pClient) { + IoT_Error_t rc; + + FUNC_ENTRY; + + if(!has_timer_expired(&(pClient->reconnectDelayTimer))) { + /* Timer has not expired. Not time to attempt reconnect yet. + * Return attempting reconnect */ + FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT); + } + + rc = NETWORK_PHYSICAL_LAYER_DISCONNECTED; + if(NULL != pClient->networkStack.isConnected) { + rc = pClient->networkStack.isConnected(&(pClient->networkStack)); + } + + if(NETWORK_PHYSICAL_LAYER_CONNECTED == rc) { + rc = aws_iot_mqtt_attempt_reconnect(pClient); + if(NETWORK_RECONNECTED == rc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + FUNC_EXIT_RC(NETWORK_RECONNECTED); + } + } + + pClient->clientData.currentReconnectWaitInterval *= 2; + + if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { + FUNC_EXIT_RC(NETWORK_RECONNECT_TIMED_OUT_ERROR); + } + countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); + FUNC_EXIT_RC(rc); +} + +static IoT_Error_t _aws_iot_mqtt_keep_alive(AWS_IoT_Client *pClient) { + IoT_Error_t rc = SUCCESS; + Timer timer; + size_t serialized_len; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(0 == pClient->clientData.keepAliveInterval) { + FUNC_EXIT_RC(SUCCESS); + } + + if(!has_timer_expired(&pClient->pingTimer)) { + FUNC_EXIT_RC(SUCCESS); + } + + if(pClient->clientStatus.isPingOutstanding) { + rc = _aws_iot_mqtt_handle_disconnect(pClient); + FUNC_EXIT_RC(rc); + } + + /* there is no ping outstanding - send one */ + init_timer(&timer); + + countdown_ms(&timer, pClient->clientData.commandTimeoutMs); + serialized_len = 0; + rc = aws_iot_mqtt_internal_serialize_zero(pClient->clientData.writeBuf, pClient->clientData.writeBufSize, + PINGREQ, &serialized_len); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + /* send the ping packet */ + rc = aws_iot_mqtt_internal_send_packet(pClient, serialized_len, &timer); + if(SUCCESS != rc) { + //If sending a PING fails we can no longer determine if we are connected. In this case we decide we are disconnected and begin reconnection attempts + rc = _aws_iot_mqtt_handle_disconnect(pClient); + FUNC_EXIT_RC(rc); + } + + pClient->clientStatus.isPingOutstanding = true; + /* start a timer to wait for PINGRESP from server */ + countdown_sec(&pClient->pingTimer, pClient->clientData.keepAliveInterval); + + FUNC_EXIT_RC(SUCCESS); +} + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * This is the internal function which is called by the yield API to perform the operation. + * Not meant to be called directly as it doesn't do validations or client state changes + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +static IoT_Error_t _aws_iot_mqtt_internal_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { + IoT_Error_t yieldRc = SUCCESS; + + uint8_t packet_type; + ClientState clientState; + Timer timer; + init_timer(&timer); + countdown_ms(&timer, timeout_ms); + + FUNC_ENTRY; + + // evaluate timeout at the end of the loop to make sure the actual yield runs at least once + do { + clientState = aws_iot_mqtt_get_client_state(pClient); + if(CLIENT_STATE_PENDING_RECONNECT == clientState) { + if(AWS_IOT_MQTT_MAX_RECONNECT_WAIT_INTERVAL < pClient->clientData.currentReconnectWaitInterval) { + yieldRc = NETWORK_RECONNECT_TIMED_OUT_ERROR; + break; + } + yieldRc = _aws_iot_mqtt_handle_reconnect(pClient); + /* Network reconnect attempted, check if yield timer expired before + * doing anything else */ + continue; + } + + yieldRc = aws_iot_mqtt_internal_cycle_read(pClient, &timer, &packet_type); + if(SUCCESS == yieldRc) { + yieldRc = _aws_iot_mqtt_keep_alive(pClient); + } else { + // SSL read and write errors are terminal, connection must be closed and retried + if(NETWORK_SSL_READ_ERROR == yieldRc || NETWORK_SSL_READ_TIMEOUT_ERROR == yieldRc + || NETWORK_SSL_WRITE_ERROR == yieldRc || NETWORK_SSL_WRITE_TIMEOUT_ERROR == yieldRc) { + yieldRc = _aws_iot_mqtt_handle_disconnect(pClient); + } + } + + if(NETWORK_DISCONNECTED_ERROR == yieldRc) { + pClient->clientData.counterNetworkDisconnected++; + if(1 == pClient->clientStatus.isAutoReconnectEnabled) { + yieldRc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, + CLIENT_STATE_PENDING_RECONNECT); + if(SUCCESS != yieldRc) { + FUNC_EXIT_RC(yieldRc); + } + + pClient->clientData.currentReconnectWaitInterval = AWS_IOT_MQTT_MIN_RECONNECT_WAIT_INTERVAL; + countdown_ms(&(pClient->reconnectDelayTimer), pClient->clientData.currentReconnectWaitInterval); + /* Depending on timer values, it is possible that yield timer has expired + * Set to rc to attempting reconnect to inform client that autoreconnect + * attempt has started */ + yieldRc = NETWORK_ATTEMPTING_RECONNECT; + } else { + break; + } + } else if(SUCCESS != yieldRc) { + break; + } + } while(!has_timer_expired(&timer)); + + FUNC_EXIT_RC(yieldRc); +} + +/** + * @brief Yield to the MQTT client + * + * Called to yield the current thread to the underlying MQTT client. This time is used by + * the MQTT client to manage PING requests to monitor the health of the TCP connection as + * well as periodically check the socket receive buffer for subscribe messages. Yield() + * must be called at a rate faster than the keepalive interval. It must also be called + * at a rate faster than the incoming message rate as this is the only way the client receives + * processing time to manage incoming messages. + * This is the outer function which does the validations and calls the internal yield above + * to perform the actual operation. It is also responsible for client state changes + * + * @param pClient Reference to the IoT Client + * @param timeout_ms Maximum number of milliseconds to pass thread execution to the client. + * + * @return An IoT Error Type defining successful/failed client processing. + * If this call results in an error it is likely the MQTT connection has dropped. + * iot_is_mqtt_connected can be called to confirm. + */ +IoT_Error_t aws_iot_mqtt_yield(AWS_IoT_Client *pClient, uint32_t timeout_ms) { + IoT_Error_t rc, yieldRc; + ClientState clientState; + + if(NULL == pClient || 0 == timeout_ms) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + clientState = aws_iot_mqtt_get_client_state(pClient); + /* Check if network was manually disconnected */ + if(CLIENT_STATE_DISCONNECTED_MANUALLY == clientState) { + FUNC_EXIT_RC(NETWORK_MANUALLY_DISCONNECTED); + } + + /* If we are in the pending reconnect state, skip other checks. + * Pending reconnect state is only set when auto-reconnect is enabled */ + if(CLIENT_STATE_PENDING_RECONNECT != clientState) { + /* Check if network is disconnected and auto-reconnect is not enabled */ + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR); + } + + /* Check if client is idle, if not another operation is in progress and we should return */ + if(CLIENT_STATE_CONNECTED_IDLE != clientState) { + FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR); + } + + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE, + CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + } + + yieldRc = _aws_iot_mqtt_internal_yield(pClient, timeout_ms); + + if(NETWORK_DISCONNECTED_ERROR != yieldRc && NETWORK_ATTEMPTING_RECONNECT != yieldRc) { + rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_YIELD_IN_PROGRESS, + CLIENT_STATE_CONNECTED_IDLE); + if(SUCCESS == yieldRc && SUCCESS != rc) { + yieldRc = rc; + } + } + + FUNC_EXIT_RC(yieldRc); +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow.c new file mode 100644 index 00000000000..e9b7eab6448 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow.c @@ -0,0 +1,229 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_shadow.c + * @brief Shadow client API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_interface.h" +#include "aws_iot_error.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_actions.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_shadow_key.h" +#include "aws_iot_shadow_records.h" + +const ShadowInitParameters_t ShadowInitParametersDefault = {(char *) AWS_IOT_MQTT_HOST, AWS_IOT_MQTT_PORT, NULL, NULL, + NULL, false, NULL}; + +const ShadowConnectParameters_t ShadowConnectParametersDefault = {(char *) AWS_IOT_MY_THING_NAME, + (char *) AWS_IOT_MQTT_CLIENT_ID, 0, NULL}; + +void aws_iot_shadow_reset_last_received_version(void) { + shadowJsonVersionNum = 0; +} + +uint32_t aws_iot_shadow_get_last_received_version(void) { + return shadowJsonVersionNum; +} + +void aws_iot_shadow_enable_discard_old_delta_msgs(void) { + shadowDiscardOldDeltaFlag = true; +} + +void aws_iot_shadow_disable_discard_old_delta_msgs(void) { + shadowDiscardOldDeltaFlag = false; +} + +IoT_Error_t aws_iot_shadow_init(AWS_IoT_Client *pClient, const ShadowInitParameters_t *pParams) { + IoT_Client_Init_Params mqttInitParams; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pParams) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + mqttInitParams.enableAutoReconnect = pParams->enableAutoReconnect; + mqttInitParams.pHostURL = pParams->pHost; + mqttInitParams.port = pParams->port; + mqttInitParams.pRootCALocation = pParams->pRootCA; + mqttInitParams.pDeviceCertLocation = pParams->pClientCRT; + mqttInitParams.pDevicePrivateKeyLocation = pParams->pClientKey; + mqttInitParams.mqttCommandTimeout_ms = 20000; + mqttInitParams.tlsHandshakeTimeout_ms = 5000; + mqttInitParams.isSSLHostnameVerify = true; + mqttInitParams.disconnectHandler = pParams->disconnectHandler; + + rc = aws_iot_mqtt_init(pClient, &mqttInitParams); + if(SUCCESS != rc) { + FUNC_EXIT_RC(rc); + } + + resetClientTokenSequenceNum(); + aws_iot_shadow_reset_last_received_version(); + initDeltaTokens(); + + FUNC_EXIT_RC(SUCCESS); +} + +IoT_Error_t aws_iot_shadow_connect(AWS_IoT_Client *pClient, const ShadowConnectParameters_t *pParams) { + IoT_Error_t rc = SUCCESS; + char deleteAcceptedTopic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + uint16_t deleteAcceptedTopicLen; + IoT_Client_Connect_Params ConnectParams = iotClientConnectParamsDefault; + + FUNC_ENTRY; + + if(NULL == pClient || NULL == pParams || NULL == pParams->pMqttClientId) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + snprintf(myThingName, MAX_SIZE_OF_THING_NAME, "%s", pParams->pMyThingName); + snprintf(mqttClientID, MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES, "%s", pParams->pMqttClientId); + + ConnectParams.keepAliveIntervalInSec = 10; + ConnectParams.MQTTVersion = MQTT_3_1_1; + ConnectParams.isCleanSession = true; + ConnectParams.isWillMsgPresent = false; + ConnectParams.pClientID = pParams->pMqttClientId; + ConnectParams.clientIDLen = pParams->mqttClientIdLen; + ConnectParams.pPassword = NULL; + ConnectParams.pUsername = NULL; + + rc = aws_iot_mqtt_connect(pClient, &ConnectParams); + + if(SUCCESS == rc) { + initializeRecords(pClient); + } + else + { + printf("\nMqtt connected failed : %d",rc); + } + + if(NULL != pParams->deleteActionHandler) { + snprintf(deleteAcceptedTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, + "$aws/things/%s/shadow/delete/accepted", myThingName); + deleteAcceptedTopicLen = (uint16_t) strlen(deleteAcceptedTopic); + rc = aws_iot_mqtt_subscribe(pClient, deleteAcceptedTopic, deleteAcceptedTopicLen, QOS1, + pParams->deleteActionHandler, (void *) myThingName); + } + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_register_delta(AWS_IoT_Client *pMqttClient, jsonStruct_t *pStruct) { + if(NULL == pMqttClient || NULL == pStruct) { + return NULL_VALUE_ERROR; + } + + if(!aws_iot_mqtt_is_client_connected(pMqttClient)) { + return MQTT_CONNECTION_ERROR; + } + + return registerJsonTokenOnDelta(pStruct); +} + +IoT_Error_t aws_iot_shadow_yield(AWS_IoT_Client *pClient, uint32_t timeout) { + if(NULL == pClient) { + return NULL_VALUE_ERROR; + } + + HandleExpiredResponseCallbacks(); + return aws_iot_mqtt_yield(pClient, timeout); +} + +IoT_Error_t aws_iot_shadow_disconnect(AWS_IoT_Client *pClient) { + return aws_iot_mqtt_disconnect(pClient); +} + +IoT_Error_t aws_iot_shadow_update(AWS_IoT_Client *pClient, const char *pThingName, char *pJsonString, + fpActionCallback_t callback, void *pContextData, uint8_t timeout_seconds, + bool isPersistentSubscribe) { + IoT_Error_t rc; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_UPDATE, pJsonString, callback, pContextData, + timeout_seconds, isPersistentSubscribe); + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_delete(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe) { + char deleteRequestJsonBuf[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + aws_iot_shadow_internal_delete_request_json(deleteRequestJsonBuf); + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_DELETE, deleteRequestJsonBuf, callback, pContextData, + timeout_seconds, isPersistentSubscribe); + + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_get(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe) { + char getRequestJsonBuf[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + IoT_Error_t rc; + + FUNC_ENTRY; + + if(NULL == pClient) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + if(!aws_iot_mqtt_is_client_connected(pClient)) { + FUNC_EXIT_RC(MQTT_CONNECTION_ERROR); + } + + aws_iot_shadow_internal_get_request_json(getRequestJsonBuf); + rc = aws_iot_shadow_internal_action(pThingName, SHADOW_GET, getRequestJsonBuf, callback, pContextData, + timeout_seconds, isPersistentSubscribe); + FUNC_EXIT_RC(rc); +} + +IoT_Error_t aws_iot_shadow_set_autoreconnect_status(AWS_IoT_Client *pClient, bool newStatus) { + return aws_iot_mqtt_autoreconnect_set_status(pClient, newStatus); +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.c new file mode 100644 index 00000000000..ed0f0cf84ec --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.c @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_shadow_actions.c + * @brief Shadow client Action API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_actions.h" + +#include "aws_iot_log.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_shadow_records.h" +#include "aws_iot_config.h" + +IoT_Error_t aws_iot_shadow_internal_action(const char *pThingName, ShadowActions_t action, + const char *pJsonDocumentToBeSent, fpActionCallback_t callback, + void *pCallbackContext, uint32_t timeout_seconds, bool isSticky) { + IoT_Error_t ret_val = SUCCESS; + bool isClientTokenPresent = false; + bool isAckWaitListFree = false; + uint8_t indexAckWaitList; + char extractedClientToken[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE]; + + FUNC_ENTRY; + + if(NULL == pThingName || NULL == pJsonDocumentToBeSent) { + FUNC_EXIT_RC(NULL_VALUE_ERROR); + } + + isClientTokenPresent = extractClientToken(pJsonDocumentToBeSent, extractedClientToken); + + if(isClientTokenPresent && (NULL != callback)) { + if(getNextFreeIndexOfAckWaitList(&indexAckWaitList)) { + isAckWaitListFree = true; + } + + if(isAckWaitListFree) { + if(!isSubscriptionPresent(pThingName, action)) { + ret_val = subscribeToShadowActionAcks(pThingName, action, isSticky); + } else { + incrementSubscriptionCnt(pThingName, action, isSticky); + } + } + else { + ret_val = FAILURE; + } + } + + if(SUCCESS == ret_val) { + ret_val = publishToShadowAction(pThingName, action, pJsonDocumentToBeSent); + } + + if(isClientTokenPresent && (NULL != callback) && (SUCCESS == ret_val) && isAckWaitListFree) { + addToAckWaitList(indexAckWaitList, pThingName, action, extractedClientToken, callback, pCallbackContext, + timeout_seconds); + } + + FUNC_EXIT_RC(ret_val); +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.h new file mode 100644 index 00000000000..a5f8755936e --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_actions.h @@ -0,0 +1,33 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_interface.h" + +IoT_Error_t aws_iot_shadow_internal_action(const char *pThingName, ShadowActions_t action, + const char *pJsonDocumentToBeSent, fpActionCallback_t callback, + void *pCallbackContext, uint32_t timeout_seconds, bool isSticky); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_ACTIONS_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_interface.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_interface.h new file mode 100644 index 00000000000..818b35f0e41 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_interface.h @@ -0,0 +1,296 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ +#ifndef AWS_IOT_SDK_SRC_IOT_SHADOW_H_ +#define AWS_IOT_SDK_SRC_IOT_SHADOW_H_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @file aws_iot_shadow_interface.h + * @brief Interface for thing shadow + * + * These are the functions and structs to manage/interact the Thing Shadow(in the cloud). + * This SDK will let you interact with your own thing shadow or any other shadow using its Thing Name. + * There are totally 3 actions a device can perform on the shadow - Get, Update and Delete. + * + * Currently the device should use MQTT/S underneath. In the future this will also support other protocols. As it supports MQTT, the shadow needs to connect and disconnect. + * It will also work on the pub/sub model. On performing any action, the acknowledgment will be received in either accepted or rejected. For Example: + * If we want to perform a GET on the thing shadow the following messages will be sent and received: + * 1. A MQTT Publish on the topic - $aws/things/{thingName}/shadow/get + * 2. Subscribe to MQTT topics - $aws/things/{thingName}/shadow/get/accepted and $aws/things/{thingName}/shadow/get/rejected. + * If the request was successful we will receive the things json document in the accepted topic. + * + * + */ +#include "aws_iot_mqtt_client_interface.h" +#include "aws_iot_shadow_json_data.h" + +/*! + * @brief Shadow Initialization parameters + * + * As the Shadow SDK uses MQTT underneath, it could be connected and disconnected on events to save some battery. + * @note Always use the \c ShadowIniTParametersDefault to initialize this struct + * + * + * + */ +typedef struct { + char *pHost; ///< This will be unique to a customer and can be retrieved from the console + uint16_t port; ///< By default the port is 8883 + const char *pRootCA; ///< Location with the Filename of the Root CA + const char *pClientCRT; ///< Location of Device certs signed by AWS IoT service + const char *pClientKey; ///< Location of Device private key + bool enableAutoReconnect; ///< Set to true to enable auto reconnect + iot_disconnect_handler disconnectHandler; ///< Callback to be invoked upon connection loss. +} ShadowInitParameters_t; + +/*! + * @brief Shadow Connect parameters + * + * As the Shadow SDK uses MQTT underneath, it could be connected and disconnected on events to save some battery. + * @note Always use the \c ShadowConnectParametersDefault to initialize this struct + * + *d + * + */ +typedef struct { + const char *pMyThingName; ///< Every device has a Thing Shadow and this is the placeholder for name + const char *pMqttClientId; ///< Currently the Shadow uses MQTT to connect and it is important to ensure we have unique client id + uint16_t mqttClientIdLen; ///< Currently the Shadow uses MQTT to connect and it is important to ensure we have unique client id + pApplicationHandler_t deleteActionHandler; ///< Callback to be invoked when Thing shadow for this device is deleted +} ShadowConnectParameters_t; + +/*! + * @brief This is set to defaults from the configuration file + * The certs are set to NULL because they need the path to the file. shadow_sample.c file demonstrates on how to get the relative path + * + * \relates ShadowInitParameters_t + */ +extern const ShadowInitParameters_t ShadowInitParametersDefault; + +/*! + * @brief This is set to defaults from the configuration file + * The length of the client id is initialized as 0. This is due to C language limitations of using constant literals + * only for creating const variables. The client id will be assigned using the value from aws_iot_config.h but the + * length needs to be assigned in code. shadow_sample.c file demonstrates this. + * + * \relates ShadowConnectParameters_t + */ +extern const ShadowConnectParameters_t ShadowConnectParametersDefault; + + +/** + * @brief Initialize the Thing Shadow before use + * + * This function takes care of initializing the internal book-keeping data structures and initializing the IoT client. + * + * @param pClient A new MQTT Client to be used as the protocol layer. Will be initialized with pParams. + * @return An IoT Error Type defining successful/failed Initialization + */ +IoT_Error_t aws_iot_shadow_init(AWS_IoT_Client *pClient, const ShadowInitParameters_t *pParams); + +/** + * @brief Connect to the AWS IoT Thing Shadow service over MQTT + * + * This function does the TLSv1.2 handshake and establishes the MQTT connection + * + * @param pClient MQTT Client used as the protocol layer + * @param pParams Shadow Conenction parameters like TLS cert location + * @return An IoT Error Type defining successful/failed Connection + */ +IoT_Error_t aws_iot_shadow_connect(AWS_IoT_Client *pClient, const ShadowConnectParameters_t *pParams); + +/** + * @brief Yield function to let the background tasks of MQTT and Shadow + * + * This function could be use in a separate thread waiting for the incoming messages, ensuring the connection is kept alive with the AWS Service. + * It also ensures the expired requests of Shadow actions are cleared and Timeout callback is executed. + * @note All callbacks ever used in the SDK will be executed in the context of this function. + * + * @param pClient MQTT Client used as the protocol layer + * @param timeout in milliseconds, This is the maximum time the yield function will wait for a message and/or read the messages from the TLS buffer + * @return An IoT Error Type defining successful/failed Yield + */ +IoT_Error_t aws_iot_shadow_yield(AWS_IoT_Client *pClient, uint32_t timeout); + +/** + * @brief Disconnect from the AWS IoT Thing Shadow service over MQTT + * + * This will close the underlying TCP connection, MQTT connection will also be closed + * + * @param pClient MQTT Client used as the protocol layer + * @return An IoT Error Type defining successful/failed disconnect status + */ +IoT_Error_t aws_iot_shadow_disconnect(AWS_IoT_Client *pClient); + +/** + * @brief Thing Shadow Acknowledgment enum + * + * This enum type is use in the callback for the action response + * + */ +typedef enum { + SHADOW_ACK_TIMEOUT, SHADOW_ACK_REJECTED, SHADOW_ACK_ACCEPTED +} Shadow_Ack_Status_t; + +/** + * @brief Thing Shadow Action type enum + * + * This enum type is use in the callback for the action response + * + */ +typedef enum { + SHADOW_GET, SHADOW_UPDATE, SHADOW_DELETE +} ShadowActions_t; + + +/** + * @brief Function Pointer typedef used as the callback for every action + * + * This function will be called from the context of \c aws_iot_shadow_yield() context + * + * @param pThingName Thing Name of the response received + * @param action The response of the action + * @param status Informs if the action was Accepted/Rejected or Timed out + * @param pReceivedJsonDocument Received JSON document + * @param pContextData the void* data passed in during the action call(update, get or delete) + * + */ +typedef void (*fpActionCallback_t)(const char *pThingName, ShadowActions_t action, Shadow_Ack_Status_t status, + const char *pReceivedJsonDocument, void *pContextData); + +/** + * @brief This function is the one used to perform an Update action to a Thing Name's Shadow. + * + * update is one of the most frequently used functionality by a device. In most cases the device may be just reporting few params to update the thing shadow in the cloud + * Update Action if no callback or if the JSON document does not have a client token then will just publish the update and not track it. + * + * @note The update has to subscribe to two topics update/accepted and update/rejected. This function waits 2 seconds to ensure the subscriptions are registered before publishing the update message. + * The following steps are performed on using this function: + * 1. Subscribe to Shadow topics - $aws/things/{thingName}/shadow/update/accepted and $aws/things/{thingName}/shadow/update/rejected + * 2. wait for 2 seconds for the subscription to take effect + * 3. Publish on the update topic - $aws/things/{thingName}/shadow/update + * 4. In the \c aws_iot_shadow_yield() function the response will be handled. In case of timeout or if the response is received, the subscription to shadow response topics are un-subscribed from. + * On the contrary if the persistent subscription is set to true then the un-subscribe will not be done. The topics will always be listened to. + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the shadow that needs to be Updated + * @param pJsonString The update action expects a JSON document to send. The JSON String should be a null terminated string. This JSON document should adhere to the AWS IoT Thing Shadow specification. To help in the process of creating this document- SDK provides apis in \c aws_iot_shadow_json_data.h + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device updates the same shadow then this should be set to true to avoid repeated subscription and unsubscription. If the Thing Name is one off update then this should be set to false + * @return An IoT Error Type defining successful/failed update action + */ +IoT_Error_t aws_iot_shadow_update(AWS_IoT_Client *pClient, const char *pThingName, char *pJsonString, + fpActionCallback_t callback, void *pContextData, uint8_t timeout_seconds, + bool isPersistentSubscribe); + +/** + * @brief This function is the one used to perform an Get action to a Thing Name's Shadow. + * + * One use of this function is usually to get the config of a device at boot up. + * It is similar to the Update function internally except it does not take a JSON document as the input. The entire JSON document will be sent over the accepted topic + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the JSON document that is needed + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device gets the same Sahdow (JSON document) then this should be set to true to avoid repeated subscription and un-subscription. If the Thing Name is one off get then this should be set to false + * @return An IoT Error Type defining successful/failed get action + */ +IoT_Error_t aws_iot_shadow_get(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscribe); + +/** + * @brief This function is the one used to perform an Delete action to a Thing Name's Shadow. + * + * This is not a very common use case for device. It is generally the responsibility of the accompanying app to do the delete. + * It is similar to the Update function internally except it does not take a JSON document as the input. The Thing Shadow referred by the ThingName will be deleted. + * + * @param pClient MQTT Client used as the protocol layer + * @param pThingName Thing Name of the Shadow that should be deleted + * @param callback This is the callback that will be used to inform the caller of the response from the AWS IoT Shadow service.Callback could be set to NULL if response is not important + * @param pContextData This is an extra parameter that could be passed along with the callback. It should be set to NULL if not used + * @param timeout_seconds It is the time the SDK will wait for the response on either accepted/rejected before declaring timeout on the action + * @param isPersistentSubscribe As mentioned above, every time if a device deletes the same Shadow (JSON document) then this should be set to true to avoid repeated subscription and un-subscription. If the Thing Name is one off delete then this should be set to false + * @return An IoT Error Type defining successful/failed delete action + */ +IoT_Error_t aws_iot_shadow_delete(AWS_IoT_Client *pClient, const char *pThingName, fpActionCallback_t callback, + void *pContextData, uint8_t timeout_seconds, bool isPersistentSubscriptions); + +/** + * @brief This function is used to listen on the delta topic of #AWS_IOT_MY_THING_NAME mentioned in the aws_iot_config.h file. + * + * Any time a delta is published the Json document will be delivered to the pStruct->cb. If you don't want the parsing done by the SDK then use the jsonStruct_t key set to "state". A good example of this is displayed in the sample_apps/shadow_console_echo.c + * + * @param pClient MQTT Client used as the protocol layer + * @param pStruct The struct used to parse JSON value + * @return An IoT Error Type defining successful/failed delta registering + */ +IoT_Error_t aws_iot_shadow_register_delta(AWS_IoT_Client *pClient, jsonStruct_t *pStruct); + +/** + * @brief Reset the last received version number to zero. + * This will be useful if the Thing Shadow is deleted and would like to to reset the local version + * @return no return values + * + */ +void aws_iot_shadow_reset_last_received_version(void); + +/** + * @brief Version of a document is received with every accepted/rejected and the SDK keeps track of the last received version of the JSON document of #AWS_IOT_MY_THING_NAME shadow + * + * One exception to this version tracking is that, the SDK will ignore the version from update/accepted topic. Rest of the responses will be scanned to update the version number. + * Accepting version change for update/accepted may cause version conflicts for delta message if the update message is received before the delta. + * + * @return version number of the last received response + * + */ +uint32_t aws_iot_shadow_get_last_received_version(void); + +/** + * @brief Enable the ignoring of delta messages with old version number + * + * As we use MQTT underneath, there could be more than 1 of the same message if we use QoS 0. To avoid getting called for the same message, this functionality should be enabled. All the old message will be ignored + */ +void aws_iot_shadow_enable_discard_old_delta_msgs(void); + +/** + * @brief Disable the ignoring of delta messages with old version number + */ +void aws_iot_shadow_disable_discard_old_delta_msgs(void); + +/** + * @brief This function is used to enable or disable autoreconnect + * + * Any time a disconnect happens the underlying MQTT client attempts to reconnect if this is set to true + * + * @param pClient MQTT Client used as the protocol layer + * @param newStatus The new status to set the autoreconnect option to + * + * @return An IoT Error Type defining successful/failed operation + */ +IoT_Error_t aws_iot_shadow_set_autoreconnect_status(AWS_IoT_Client *pClient, bool newStatus); + +#ifdef __cplusplus +} +#endif + +#endif //AWS_IOT_SDK_SRC_IOT_SHADOW_H_ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.c new file mode 100644 index 00000000000..9d1fdb0fa4c --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.c @@ -0,0 +1,472 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_shadow_json.c + * @brief Shadow client JSON parsing API definitions + */ + +#ifdef __cplusplus +extern "C" { +#include +#else + +#include + +#endif + +#include "aws_iot_shadow_json.h" + +#include +#include + +#include "aws_iot_json_utils.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_key.h" +#include "aws_iot_config.h" + +extern char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; + +static uint32_t clientTokenNum = 0; + +//helper functions +static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type, + void *pData); + +void resetClientTokenSequenceNum(void) { + clientTokenNum = 0; +} + +static void emptyJsonWithClientToken(char *pJsonDocument) { + sprintf(pJsonDocument, "{\"clientToken\":\""); + FillWithClientToken(pJsonDocument + strlen(pJsonDocument)); + sprintf(pJsonDocument + strlen(pJsonDocument), "\"}"); +} + +void aws_iot_shadow_internal_get_request_json(char *pJsonDocument) { + emptyJsonWithClientToken(pJsonDocument); +} + +void aws_iot_shadow_internal_delete_request_json(char *pJsonDocument) { + emptyJsonWithClientToken(pJsonDocument); +} + +static inline IoT_Error_t checkReturnValueOfSnPrintf(int32_t snPrintfReturn, size_t maxSizeOfJsonDocument) { + if(snPrintfReturn < 0) { + return SHADOW_JSON_ERROR; + } else if((size_t) snPrintfReturn >= maxSizeOfJsonDocument) { + return SHADOW_JSON_BUFFER_TRUNCATED; + } + return SUCCESS; +} + +IoT_Error_t aws_iot_shadow_init_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) { + + IoT_Error_t ret_val = SUCCESS; + int32_t snPrintfReturn = 0; + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + snPrintfReturn = snprintf(pJsonDocument, maxSizeOfJsonDocument, "{\"state\":{"); + + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizeOfJsonDocument); + + return ret_val; + +} + +IoT_Error_t aws_iot_shadow_add_desired(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) { + IoT_Error_t ret_val = SUCCESS; + size_t tempSize = 0; + int8_t i; + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + va_list pArgs; + jsonStruct_t *pTemporary = NULL; + va_start(pArgs, count); + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"desired\":{"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != SUCCESS) { + return ret_val; + } + + for(i = 0; i < count; i++) { + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + pTemporary = va_arg (pArgs, jsonStruct_t *); + if(pTemporary != NULL) { + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":", + pTemporary->pKey); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + if(ret_val != SUCCESS) { + return ret_val; + } + if(pTemporary->pKey != NULL && pTemporary->pData != NULL) { + ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, + pTemporary->type, pTemporary->pData); + } else { + return NULL_VALUE_ERROR; + } + if(ret_val != SUCCESS) { + return ret_val; + } + } else { + return NULL_VALUE_ERROR; + } + } + + va_end(pArgs); + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + return ret_val; +} + +IoT_Error_t aws_iot_shadow_add_reported(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...) { + IoT_Error_t ret_val = SUCCESS; + + int8_t i; + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + size_t tempSize = 0; + jsonStruct_t *pTemporary; + va_list pArgs; + va_start(pArgs, count); + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"reported\":{"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != SUCCESS) { + return ret_val; + } + + for(i = 0; i < count; i++) { + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + pTemporary = va_arg (pArgs, jsonStruct_t *); + if(pTemporary != NULL) { + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"%s\":", + pTemporary->pKey); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + if(ret_val != SUCCESS) { + return ret_val; + } + if(pTemporary->pKey != NULL && pTemporary->pData != NULL) { + ret_val = convertDataToString(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, + pTemporary->type, pTemporary->pData); + } else { + return NULL_VALUE_ERROR; + } + if(ret_val != SUCCESS) { + return ret_val; + } + } else { + return NULL_VALUE_ERROR; + } + } + + va_end(pArgs); + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "},"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + return ret_val; +} + + +int32_t FillWithClientTokenSize(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument) { + int32_t snPrintfReturn; + snPrintfReturn = snprintf(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument, "%s-%d", mqttClientID, + clientTokenNum++); + + return snPrintfReturn; +} + +IoT_Error_t aws_iot_fill_with_client_token(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument) { + + int32_t snPrintfRet = 0; + snPrintfRet = FillWithClientTokenSize(pBufferToBeUpdatedWithClientToken, maxSizeOfJsonDocument); + return checkReturnValueOfSnPrintf(snPrintfRet, maxSizeOfJsonDocument); + +} + +IoT_Error_t aws_iot_finalize_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument) { + size_t remSizeOfJsonBuffer = maxSizeOfJsonDocument; + int32_t snPrintfReturn = 0; + size_t tempSize = 0; + IoT_Error_t ret_val = SUCCESS; + + if(pJsonDocument == NULL) { + return NULL_VALUE_ERROR; + } + + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + // strlen(ShadowTxBuffer) - 1 is to ensure we remove the last ,(comma) that was added + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument) - 1, remSizeOfJsonBuffer, "}, \"%s\":\"", + SHADOW_CLIENT_TOKEN_STRING); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != SUCCESS) { + return ret_val; + } + // refactor this XXX repeated code + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + + snPrintfReturn = FillWithClientTokenSize(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + if(ret_val != SUCCESS) { + return ret_val; + } + tempSize = maxSizeOfJsonDocument - strlen(pJsonDocument); + if(tempSize <= 1) { + return SHADOW_JSON_ERROR; + } + remSizeOfJsonBuffer = tempSize; + + + snPrintfReturn = snprintf(pJsonDocument + strlen(pJsonDocument), remSizeOfJsonBuffer, "\"}"); + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, remSizeOfJsonBuffer); + + return ret_val; +} + +void FillWithClientToken(char *pBufferToBeUpdatedWithClientToken) { + sprintf(pBufferToBeUpdatedWithClientToken, "%s-%d", mqttClientID, clientTokenNum++); +} + +static IoT_Error_t convertDataToString(char *pStringBuffer, size_t maxSizoStringBuffer, JsonPrimitiveType type, + void *pData) { + int32_t snPrintfReturn = 0; + IoT_Error_t ret_val = SUCCESS; + + if(maxSizoStringBuffer == 0) { + return SHADOW_JSON_ERROR; + } + + if(type == SHADOW_JSON_INT32) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIi32",", *(int32_t *) (pData)); + } else if(type == SHADOW_JSON_INT16) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIi16",", *(int16_t *) (pData)); + } else if(type == SHADOW_JSON_INT8) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIi8",", *(int8_t *) (pData)); + } else if(type == SHADOW_JSON_UINT32) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIu32",", *(uint32_t *) (pData)); + } else if(type == SHADOW_JSON_UINT16) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIu16",", *(uint16_t *) (pData)); + } else if(type == SHADOW_JSON_UINT8) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%" PRIu8",", *(uint8_t *) (pData)); + } else if(type == SHADOW_JSON_DOUBLE) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(double *) (pData)); + } else if(type == SHADOW_JSON_FLOAT) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%f,", *(float *) (pData)); + } else if(type == SHADOW_JSON_BOOL) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "%s,", *(bool *) (pData) ? "true" : "false"); + } else if(type == SHADOW_JSON_STRING) { + snPrintfReturn = snprintf(pStringBuffer, maxSizoStringBuffer, "\"%s\",", (char *) (pData)); + } + + ret_val = checkReturnValueOfSnPrintf(snPrintfReturn, maxSizoStringBuffer); + + return ret_val; +} + +static jsmn_parser shadowJsonParser; +static jsmntok_t jsonTokenStruct[MAX_JSON_TOKEN_EXPECTED]; + +bool isJsonValidAndParse(const char *pJsonDocument, void *pJsonHandler, int32_t *pTokenCount) { + int32_t tokenCount; + + IOT_UNUSED(pJsonHandler); + + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %d\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + IOT_WARN("Top Level is not an object\n"); + return false; + } + + *pTokenCount = tokenCount; + + return true; +} + +static IoT_Error_t UpdateValueIfNoObject(const char *pJsonString, jsonStruct_t *pDataStruct, jsmntok_t token) { + IoT_Error_t ret_val = SUCCESS; + if(pDataStruct->type == SHADOW_JSON_BOOL) { + ret_val = parseBooleanValue((bool *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT32) { + ret_val = parseInteger32Value((int32_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT16) { + ret_val = parseInteger16Value((int16_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_INT8) { + ret_val = parseInteger8Value((int8_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT32) { + ret_val = parseUnsignedInteger32Value((uint32_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT16) { + ret_val = parseUnsignedInteger16Value((uint16_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_UINT8) { + ret_val = parseUnsignedInteger8Value((uint8_t *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_FLOAT) { + ret_val = parseFloatValue((float *) pDataStruct->pData, pJsonString, &token); + } else if(pDataStruct->type == SHADOW_JSON_DOUBLE) { + ret_val = parseDoubleValue((double *) pDataStruct->pData, pJsonString, &token); + } + + return ret_val; +} + +bool isJsonKeyMatchingAndUpdateValue(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, + jsonStruct_t *pDataStruct, uint32_t *pDataLength, int32_t *pDataPosition) { + int32_t i; + uint32_t dataLength; + jsmntok_t dataToken; + + IOT_UNUSED(pJsonHandler); + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &(jsonTokenStruct[i]), pDataStruct->pKey) == 0) { + dataToken = jsonTokenStruct[i + 1]; + dataLength = (uint32_t) (dataToken.end - dataToken.start); + UpdateValueIfNoObject(pJsonDocument, pDataStruct, dataToken); + *pDataPosition = dataToken.start; + *pDataLength = dataLength; + return true; + } + } + return false; +} + +bool isReceivedJsonValid(const char *pJsonDocument) { + int32_t tokenCount; + + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %d\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + return false; + } + + return true; +} + +bool extractClientToken(const char *pJsonDocument, char *pExtractedClientToken) { + int32_t tokenCount, i; + uint8_t length; + jsmntok_t ClientJsonToken; + jsmn_init(&shadowJsonParser); + + tokenCount = jsmn_parse(&shadowJsonParser, pJsonDocument, strlen(pJsonDocument), jsonTokenStruct, + sizeof(jsonTokenStruct) / sizeof(jsonTokenStruct[0])); + + if(tokenCount < 0) { + IOT_WARN("Failed to parse JSON: %d\n", tokenCount); + return false; + } + + /* Assume the top-level element is an object */ + if(tokenCount < 1 || jsonTokenStruct[0].type != JSMN_OBJECT) { + return false; + } + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &jsonTokenStruct[i], SHADOW_CLIENT_TOKEN_STRING) == 0) { + ClientJsonToken = jsonTokenStruct[i + 1]; + length = (uint8_t) (ClientJsonToken.end - ClientJsonToken.start); + strncpy(pExtractedClientToken, pJsonDocument + ClientJsonToken.start, length); + pExtractedClientToken[length] = '\0'; + return true; + } + } + + return false; +} + +bool extractVersionNumber(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, uint32_t *pVersionNumber) { + int32_t i; + IoT_Error_t ret_val = SUCCESS; + + IOT_UNUSED(pJsonHandler); + + for(i = 1; i < tokenCount; i++) { + if(jsoneq(pJsonDocument, &(jsonTokenStruct[i]), SHADOW_VERSION_STRING) == 0) { + ret_val = parseUnsignedInteger32Value(pVersionNumber, pJsonDocument, &jsonTokenStruct[i + 1]); + if(ret_val == SUCCESS) { + return true; + } + } + } + return false; +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.h new file mode 100644 index 00000000000..a84713ecea6 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json.h @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ +#ifndef AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_ +#define AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "aws_iot_error.h" +#include "aws_iot_shadow_json_data.h" + +bool isJsonValidAndParse(const char *pJsonDocument, void *pJsonHandler, int32_t *pTokenCount); + +bool isJsonKeyMatchingAndUpdateValue(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, + jsonStruct_t *pDataStruct, uint32_t *pDataLength, int32_t *pDataPosition); + +void aws_iot_shadow_internal_get_request_json(char *pJsonDocument); + +void aws_iot_shadow_internal_delete_request_json(char *pJsonDocument); + +void resetClientTokenSequenceNum(void); + + +bool isReceivedJsonValid(const char *pJsonDocument); + +void FillWithClientToken(char *pStringToUpdateClientToken); + +bool extractClientToken(const char *pJsonDocumentToBeSent, char *pExtractedClientToken); + +bool extractVersionNumber(const char *pJsonDocument, void *pJsonHandler, int32_t tokenCount, uint32_t *pVersionNumber); + +#ifdef __cplusplus +} +#endif + +#endif // AWS_IOT_SDK_SRC_IOT_SHADOW_JSON_H_ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json_data.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json_data.h new file mode 100644 index 00000000000..deaf9a177ff --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_json_data.h @@ -0,0 +1,149 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @file aws_iot_shadow_json_data.h + * @brief This file is the interface for all the Shadow related JSON functions. + */ + +#include + +/** + * @brief This is a static JSON object that could be used in code + * + */ +typedef struct jsonStruct jsonStruct_t; + +/** + * @brief Every JSON name value can have a callback. The callback should follow this signature + */ +typedef void (*jsonStructCallback_t)(const char *pJsonValueBuffer, uint32_t valueLength, jsonStruct_t *pJsonStruct_t); + +/** + * @brief All the JSON object types enum + * + * JSON number types need to be split into proper integer / floating point data types and sizes on embedded platforms. + */ +typedef enum { + SHADOW_JSON_INT32, + SHADOW_JSON_INT16, + SHADOW_JSON_INT8, + SHADOW_JSON_UINT32, + SHADOW_JSON_UINT16, + SHADOW_JSON_UINT8, + SHADOW_JSON_FLOAT, + SHADOW_JSON_DOUBLE, + SHADOW_JSON_BOOL, + SHADOW_JSON_STRING, + SHADOW_JSON_OBJECT +} JsonPrimitiveType; + +/** + * @brief This is the struct form of a JSON Key value pair + */ +struct jsonStruct { + const char *pKey; ///< JSON key + void *pData; ///< pointer to the data (JSON value) + JsonPrimitiveType type; ///< type of JSON + jsonStructCallback_t cb; ///< callback to be executed on receiving the Key value pair +}; + +/** + * @brief Initialize the JSON document with Shadow expected name/value + * + * This Function will fill the JSON Buffer with a null terminated string. Internally it uses snprintf + * This function should always be used First, followed by iot_shadow_add_reported and/or iot_shadow_add_desired. + * Always finish the call sequence with iot_finalize_json_document + * + * @note Ensure the size of the Buffer is enough to hold the entire JSON Document. + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_init_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument); + +/** + * @brief Add the reported section of the JSON document of jsonStruct_t + * + * This is a variadic function and please be careful with the usage. count is the number of jsonStruct_t types that you would like to add in the reported section + * This function will add "reported":{} + * + * @note Ensure the size of the Buffer is enough to hold the reported section + the init section. Always use the same JSON document buffer used in the iot_shadow_init_json_document function. This function will accommodate the size of previous null terminated string, so pass teh max size of the buffer + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @param count total number of arguments(jsonStruct_t object) passed in the arguments + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_add_reported(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...); + +/** + * @brief Add the desired section of the JSON document of jsonStruct_t + * + * This is a variadic function and please be careful with the usage. count is the number of jsonStruct_t types that you would like to add in the reported section + * This function will add "desired":{} + * + * @note Ensure the size of the Buffer is enough to hold the reported section + the init section. Always use the same JSON document buffer used in the iot_shadow_init_json_document function. This function will accommodate the size of previous null terminated string, so pass the max size of the buffer + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @param count total number of arguments(jsonStruct_t object) passed in the arguments + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_shadow_add_desired(char *pJsonDocument, size_t maxSizeOfJsonDocument, uint8_t count, ...); + +/** + * @brief Finalize the JSON document with Shadow expected client Token. + * + * This function will automatically increment the client token every time this function is called. + * + * @note Ensure the size of the Buffer is enough to hold the entire JSON Document. If the finalized section is not invoked then the JSON doucment will not be valid + * + * + * @param pJsonDocument The JSON Document filled in this char buffer + * @param maxSizeOfJsonDocument maximum size of the pJsonDocument that can be used to fill the JSON document + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ +IoT_Error_t aws_iot_finalize_json_document(char *pJsonDocument, size_t maxSizeOfJsonDocument); + +/** + * @brief Fill the given buffer with client token for tracking the Repsonse. + * + * This function will add the AWS_IOT_MQTT_CLIENT_ID with a sequence number. Every time this function is used the sequence number gets incremented + * + * + * @param pBufferToBeUpdatedWithClientToken buffer to be updated with the client token string + * @param maxSizeOfJsonDocument maximum size of the pBufferToBeUpdatedWithClientToken that can be used + * @return An IoT Error Type defining if the buffer was null or the entire string was not filled up + */ + +IoT_Error_t aws_iot_fill_with_client_token(char *pBufferToBeUpdatedWithClientToken, size_t maxSizeOfJsonDocument); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_JSON_DATA_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_key.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_key.h new file mode 100644 index 00000000000..075a726daa8 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_key.h @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ + +#define SHADOW_CLIENT_TOKEN_STRING "clientToken" +#define SHADOW_VERSION_STRING "version" + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_KEY_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.c b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.c new file mode 100644 index 00000000000..bd4506f1e0b --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.c @@ -0,0 +1,526 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_mqtt_client_subscribe.c + * @brief MQTT client subscribe API definitions + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "aws_iot_shadow_records.h" + +#include +#include + +#include "timer_interface.h" +#include "aws_iot_json_utils.h" +#include "aws_iot_log.h" +#include "aws_iot_shadow_json.h" +#include "aws_iot_config.h" + +typedef struct { + char clientTokenID[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE]; + char thingName[MAX_SIZE_OF_THING_NAME]; + ShadowActions_t action; + fpActionCallback_t callback; + void *pCallbackContext; + bool isFree; + Timer timer; +} ToBeReceivedAckRecord_t; + +typedef struct { + const char *pKey; + void *pStruct; + jsonStructCallback_t callback; + bool isFree; +} JsonTokenTable_t; + +typedef struct { + char Topic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + uint8_t count; + bool isFree; + bool isSticky; +} SubscriptionRecord_t; + +typedef enum { + SHADOW_ACCEPTED, SHADOW_REJECTED, SHADOW_ACTION +} ShadowAckTopicTypes_t; + +ToBeReceivedAckRecord_t AckWaitList[MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME]; + +AWS_IoT_Client *pMqttClient; + +char myThingName[MAX_SIZE_OF_THING_NAME]; +char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; + +char shadowDeltaTopic[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + +#define MAX_TOPICS_AT_ANY_GIVEN_TIME 2*MAX_THINGNAME_HANDLED_AT_ANY_GIVEN_TIME +SubscriptionRecord_t SubscriptionList[MAX_TOPICS_AT_ANY_GIVEN_TIME]; + +#define SUBSCRIBE_SETTLING_TIME 2 +char shadowRxBuf[SHADOW_MAX_SIZE_OF_RX_BUFFER]; + +static JsonTokenTable_t tokenTable[MAX_JSON_TOKEN_EXPECTED]; +static uint32_t tokenTableIndex = 0; +static bool deltaTopicSubscribedFlag = false; +uint32_t shadowJsonVersionNum = 0; +bool shadowDiscardOldDeltaFlag = true; + +// local helper functions +static void AckStatusCallback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData); + +static void shadow_delta_callback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData); + +static void topicNameFromThingAndAction(char *pTopic, const char *pThingName, ShadowActions_t action, + ShadowAckTopicTypes_t ackType); + +static int16_t getNextFreeIndexOfSubscriptionList(void); + +static void unsubscribeFromAcceptedAndRejected(uint8_t index); + +void initDeltaTokens(void) { + uint32_t i; + for(i = 0; i < MAX_JSON_TOKEN_EXPECTED; i++) { + tokenTable[i].isFree = true; + } + tokenTableIndex = 0; + deltaTopicSubscribedFlag = false; +} + +IoT_Error_t registerJsonTokenOnDelta(jsonStruct_t *pStruct) { + + IoT_Error_t rc = SUCCESS; + + if(!deltaTopicSubscribedFlag) { + snprintf(shadowDeltaTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/update/delta", myThingName); + rc = aws_iot_mqtt_subscribe(pMqttClient, shadowDeltaTopic, (uint16_t) strlen(shadowDeltaTopic), QOS0, + shadow_delta_callback, NULL); + deltaTopicSubscribedFlag = true; + } + + if(tokenTableIndex >= MAX_JSON_TOKEN_EXPECTED) { + return FAILURE; + } + + tokenTable[tokenTableIndex].pKey = pStruct->pKey; + tokenTable[tokenTableIndex].callback = pStruct->cb; + tokenTable[tokenTableIndex].pStruct = pStruct; + tokenTable[tokenTableIndex].isFree = false; + tokenTableIndex++; + + return rc; +} + +static int16_t getNextFreeIndexOfSubscriptionList(void) { + uint8_t i; + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(SubscriptionList[i].isFree) { + SubscriptionList[i].isFree = false; + return i; + } + } + return -1; +} + +static void topicNameFromThingAndAction(char *pTopic, const char *pThingName, ShadowActions_t action, + ShadowAckTopicTypes_t ackType) { + + char actionBuf[10]; + char ackTypeBuf[10]; + + if(SHADOW_GET == action) { + strncpy(actionBuf, "get", 10); + } else if(SHADOW_UPDATE == action) { + strncpy(actionBuf, "update", 10); + } else if(SHADOW_DELETE == action) { + strncpy(actionBuf, "delete", 10); + } + + if(SHADOW_ACCEPTED == ackType) { + strncpy(ackTypeBuf, "accepted", 10); + } else if(SHADOW_REJECTED == ackType) { + strncpy(ackTypeBuf, "rejected", 10); + } + + if(SHADOW_ACTION == ackType) { + snprintf(pTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/%s", pThingName, actionBuf); + } else { + snprintf(pTopic, MAX_SHADOW_TOPIC_LENGTH_BYTES, "$aws/things/%s/shadow/%s/%s", pThingName, actionBuf, + ackTypeBuf); + } +} + +static bool isAckForMyThingName(const char *pTopicName) { + if(strstr(pTopicName, myThingName) != NULL && + ((strstr(pTopicName, "get/accepted") != NULL) || (strstr(pTopicName, "update/accepted") != NULL) || + (strstr(pTopicName, "delta") != NULL))) { + return true; + } + return false; +} + +static void AckStatusCallback(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen, + IoT_Publish_Message_Params *params, void *pData) { + int32_t tokenCount; + uint8_t i; + void *pJsonHandler = NULL; + char temporaryClientToken[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE]; + + IOT_UNUSED(pClient); + IOT_UNUSED(topicNameLen); + IOT_UNUSED(pData); + + if(params->payloadLen > SHADOW_MAX_SIZE_OF_RX_BUFFER) { + IOT_WARN("Payload larger than RX Buffer"); + return; + } + + memcpy(shadowRxBuf, params->payload, params->payloadLen); + shadowRxBuf[params->payloadLen] = '\0'; // jsmn_parse relies on a string + + if(!isJsonValidAndParse(shadowRxBuf, pJsonHandler, &tokenCount)) { + IOT_WARN("Received JSON is not valid"); + return; + } + + if(isAckForMyThingName(topicName)) { + uint32_t tempVersionNumber = 0; + if(extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) { + if(tempVersionNumber > shadowJsonVersionNum) { + shadowJsonVersionNum = tempVersionNumber; + } + } + } + + if(extractClientToken(shadowRxBuf, temporaryClientToken)) { + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(!AckWaitList[i].isFree) { + if(strcmp(AckWaitList[i].clientTokenID, temporaryClientToken) == 0) { + Shadow_Ack_Status_t status; + if(strstr(topicName, "accepted") != NULL) { + status = SHADOW_ACK_ACCEPTED; + } else if(strstr(topicName, "rejected") != NULL) { + status = SHADOW_ACK_REJECTED; + } else { + continue; + } + /* status == SHADOW_ACK_ACCEPTED || status == SHADOW_ACK_REJECTED */ + if(AckWaitList[i].callback != NULL) { + AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, status, + shadowRxBuf, AckWaitList[i].pCallbackContext); + } + unsubscribeFromAcceptedAndRejected(i); + AckWaitList[i].isFree = true; + return; + } + } + } + } +} + +static int16_t findIndexOfSubscriptionList(const char *pTopic) { + uint8_t i; + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(pTopic, SubscriptionList[i].Topic) == 0)) { + return i; + } + } + } + return -1; +} + +static void unsubscribeFromAcceptedAndRejected(uint8_t index) { + + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + IoT_Error_t ret_val = SUCCESS; + + int16_t indexSubList; + + topicNameFromThingAndAction(TemporaryTopicNameAccepted, AckWaitList[index].thingName, AckWaitList[index].action, + SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, AckWaitList[index].thingName, AckWaitList[index].action, + SHADOW_REJECTED); + + indexSubList = findIndexOfSubscriptionList(TemporaryTopicNameAccepted); + if((indexSubList >= 0)) { + if(!SubscriptionList[indexSubList].isSticky && (SubscriptionList[indexSubList].count == 1)) { + ret_val = aws_iot_mqtt_unsubscribe(pMqttClient, TemporaryTopicNameAccepted, + (uint16_t) strlen(TemporaryTopicNameAccepted)); + if(ret_val == SUCCESS) { + SubscriptionList[indexSubList].isFree = true; + } + } else if(SubscriptionList[indexSubList].count > 1) { + SubscriptionList[indexSubList].count--; + } + } + + indexSubList = findIndexOfSubscriptionList(TemporaryTopicNameRejected); + if((indexSubList >= 0)) { + if(!SubscriptionList[indexSubList].isSticky && (SubscriptionList[indexSubList].count == 1)) { + ret_val = aws_iot_mqtt_unsubscribe(pMqttClient, TemporaryTopicNameRejected, + (uint16_t) strlen(TemporaryTopicNameRejected)); + if(ret_val == SUCCESS) { + SubscriptionList[indexSubList].isFree = true; + } + } else if(SubscriptionList[indexSubList].count > 1) { + SubscriptionList[indexSubList].count--; + } + } +} + +void initializeRecords(AWS_IoT_Client *pClient) { + uint8_t i; + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + AckWaitList[i].isFree = true; + } + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + SubscriptionList[i].isFree = true; + SubscriptionList[i].count = 0; + SubscriptionList[i].isSticky = false; + } + + pMqttClient = pClient; +} + +bool isSubscriptionPresent(const char *pThingName, ShadowActions_t action) { + + uint8_t i = 0; + bool isAcceptedPresent = false; + bool isRejectedPresent = false; + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + + topicNameFromThingAndAction(TemporaryTopicNameAccepted, pThingName, action, SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, pThingName, action, SHADOW_REJECTED); + + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(TemporaryTopicNameAccepted, SubscriptionList[i].Topic) == 0)) { + isAcceptedPresent = true; + } else if((strcmp(TemporaryTopicNameRejected, SubscriptionList[i].Topic) == 0)) { + isRejectedPresent = true; + } + } + } + + if(isRejectedPresent && isAcceptedPresent) { + return true; + } + + return false; +} + +IoT_Error_t subscribeToShadowActionAcks(const char *pThingName, ShadowActions_t action, bool isSticky) { + IoT_Error_t ret_val = SUCCESS; + + bool clearBothEntriesFromList = true; + int16_t indexAcceptedSubList = 0; + int16_t indexRejectedSubList = 0; + Timer subSettlingtimer; + indexAcceptedSubList = getNextFreeIndexOfSubscriptionList(); + indexRejectedSubList = getNextFreeIndexOfSubscriptionList(); + + if(indexAcceptedSubList >= 0 && indexRejectedSubList >= 0) { + topicNameFromThingAndAction(SubscriptionList[indexAcceptedSubList].Topic, pThingName, action, SHADOW_ACCEPTED); + ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic), QOS0, + AckStatusCallback, NULL); + if(ret_val == SUCCESS) { + SubscriptionList[indexAcceptedSubList].count = 1; + SubscriptionList[indexAcceptedSubList].isSticky = isSticky; + topicNameFromThingAndAction(SubscriptionList[indexRejectedSubList].Topic, pThingName, action, + SHADOW_REJECTED); + ret_val = aws_iot_mqtt_subscribe(pMqttClient, SubscriptionList[indexRejectedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexRejectedSubList].Topic), QOS0, + AckStatusCallback, NULL); + if(ret_val == SUCCESS) { + SubscriptionList[indexRejectedSubList].count = 1; + SubscriptionList[indexRejectedSubList].isSticky = isSticky; + clearBothEntriesFromList = false; + + // wait for SUBSCRIBE_SETTLING_TIME seconds to let the subscription take effect + init_timer(&subSettlingtimer); + countdown_sec(&subSettlingtimer, SUBSCRIBE_SETTLING_TIME); + while(!has_timer_expired(&subSettlingtimer)); + + } + } + } + + if(clearBothEntriesFromList) { + if(indexAcceptedSubList >= 0) { + SubscriptionList[indexAcceptedSubList].isFree = true; + } else if(indexRejectedSubList >= 0) { + SubscriptionList[indexRejectedSubList].isFree = true; + } + if(SubscriptionList[indexAcceptedSubList].count == 1) { + aws_iot_mqtt_unsubscribe(pMqttClient, SubscriptionList[indexAcceptedSubList].Topic, + (uint16_t) strlen(SubscriptionList[indexAcceptedSubList].Topic)); + } + } + + return ret_val; +} + +void incrementSubscriptionCnt(const char *pThingName, ShadowActions_t action, bool isSticky) { + char TemporaryTopicNameAccepted[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + char TemporaryTopicNameRejected[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + uint8_t i; + topicNameFromThingAndAction(TemporaryTopicNameAccepted, pThingName, action, SHADOW_ACCEPTED); + topicNameFromThingAndAction(TemporaryTopicNameRejected, pThingName, action, SHADOW_REJECTED); + + for(i = 0; i < MAX_TOPICS_AT_ANY_GIVEN_TIME; i++) { + if(!SubscriptionList[i].isFree) { + if((strcmp(TemporaryTopicNameAccepted, SubscriptionList[i].Topic) == 0) + || (strcmp(TemporaryTopicNameRejected, SubscriptionList[i].Topic) == 0)) { + SubscriptionList[i].count++; + SubscriptionList[i].isSticky = isSticky; + } + } + } +} + +IoT_Error_t publishToShadowAction(const char *pThingName, ShadowActions_t action, const char *pJsonDocumentToBeSent) { + IoT_Error_t ret_val = SUCCESS; + char TemporaryTopicName[MAX_SHADOW_TOPIC_LENGTH_BYTES]; + IoT_Publish_Message_Params msgParams; + + if(NULL == pThingName || NULL == pJsonDocumentToBeSent) { + return NULL_VALUE_ERROR; + } + + topicNameFromThingAndAction(TemporaryTopicName, pThingName, action, SHADOW_ACTION); + + msgParams.qos = QOS0; + msgParams.payloadLen = strlen(pJsonDocumentToBeSent); + msgParams.payload = (char *) pJsonDocumentToBeSent; + ret_val = aws_iot_mqtt_publish(pMqttClient, TemporaryTopicName, (uint16_t) strlen(TemporaryTopicName), &msgParams); + + return ret_val; +} + +bool getNextFreeIndexOfAckWaitList(uint8_t *pIndex) { + uint8_t i; + bool rc = false; + + if(NULL == pIndex) { + return false; + } + + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(AckWaitList[i].isFree) { + *pIndex = i; + rc = true; + break; + } + } + + return rc; +} + +void addToAckWaitList(uint8_t indexAckWaitList, const char *pThingName, ShadowActions_t action, + const char *pExtractedClientToken, fpActionCallback_t callback, void *pCallbackContext, + uint32_t timeout_seconds) { + AckWaitList[indexAckWaitList].callback = callback; + strncpy(AckWaitList[indexAckWaitList].clientTokenID, pExtractedClientToken, MAX_SIZE_CLIENT_ID_WITH_SEQUENCE); + strncpy(AckWaitList[indexAckWaitList].thingName, pThingName, MAX_SIZE_OF_THING_NAME); + AckWaitList[indexAckWaitList].pCallbackContext = pCallbackContext; + AckWaitList[indexAckWaitList].action = action; + init_timer(&(AckWaitList[indexAckWaitList].timer)); + countdown_sec(&(AckWaitList[indexAckWaitList].timer), timeout_seconds); + AckWaitList[indexAckWaitList].isFree = false; +} + +void HandleExpiredResponseCallbacks(void) { + uint8_t i; + for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) { + if(!AckWaitList[i].isFree) { + if(has_timer_expired(&(AckWaitList[i].timer))) { + if(AckWaitList[i].callback != NULL) { + AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, SHADOW_ACK_TIMEOUT, + shadowRxBuf, AckWaitList[i].pCallbackContext); + } + AckWaitList[i].isFree = true; + unsubscribeFromAcceptedAndRejected(i); + } + } + } +} + +static void shadow_delta_callback(AWS_IoT_Client *pClient, char *topicName, + uint16_t topicNameLen, IoT_Publish_Message_Params *params, void *pData) { + int32_t tokenCount; + uint32_t i = 0; + void *pJsonHandler = NULL; + int32_t DataPosition; + uint32_t dataLength; + uint32_t tempVersionNumber = 0; + + FUNC_ENTRY; + + IOT_UNUSED(pClient); + IOT_UNUSED(topicName); + IOT_UNUSED(topicNameLen); + IOT_UNUSED(pData); + + if(params->payloadLen > SHADOW_MAX_SIZE_OF_RX_BUFFER) { + IOT_WARN("Payload larger than RX Buffer"); + return; + } + + memcpy(shadowRxBuf, params->payload, params->payloadLen); + shadowRxBuf[params->payloadLen] = '\0'; // jsmn_parse relies on a string + + if(!isJsonValidAndParse(shadowRxBuf, pJsonHandler, &tokenCount)) { + IOT_WARN("Received JSON is not valid"); + return; + } + + if(shadowDiscardOldDeltaFlag) { + if(extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) { + if(tempVersionNumber > shadowJsonVersionNum) { + shadowJsonVersionNum = tempVersionNumber; + } else { + IOT_WARN("Old Delta Message received - Ignoring rx: %d local: %d", tempVersionNumber, + shadowJsonVersionNum); + return; + } + } + } + + for(i = 0; i < tokenTableIndex; i++) { + if(!tokenTable[i].isFree) { + if(isJsonKeyMatchingAndUpdateValue(shadowRxBuf, pJsonHandler, tokenCount, + (jsonStruct_t *) tokenTable[i].pStruct, &dataLength, &DataPosition)) { + if(tokenTable[i].callback != NULL) { + tokenTable[i].callback(shadowRxBuf + DataPosition, dataLength, + (jsonStruct_t *) tokenTable[i].pStruct); + } + } + } + } +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.h new file mode 100644 index 00000000000..fc2de03223e --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_shadow_records.h @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ +#define SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "aws_iot_shadow_interface.h" +#include "aws_iot_config.h" + + +extern uint32_t shadowJsonVersionNum; +extern bool shadowDiscardOldDeltaFlag; + +extern char myThingName[MAX_SIZE_OF_THING_NAME]; +extern uint16_t myThingNameLen; +extern char mqttClientID[MAX_SIZE_OF_UNIQUE_CLIENT_ID_BYTES]; +extern uint16_t mqttClientIDLen; + +void initializeRecords(AWS_IoT_Client *pClient); +bool isSubscriptionPresent(const char *pThingName, ShadowActions_t action); +IoT_Error_t subscribeToShadowActionAcks(const char *pThingName, ShadowActions_t action, bool isSticky); +void incrementSubscriptionCnt(const char *pThingName, ShadowActions_t action, bool isSticky); + +IoT_Error_t publishToShadowAction(const char *pThingName, ShadowActions_t action, const char *pJsonDocumentToBeSent); +void addToAckWaitList(uint8_t indexAckWaitList, const char *pThingName, ShadowActions_t action, + const char *pExtractedClientToken, fpActionCallback_t callback, void *pCallbackContext, + uint32_t timeout_seconds); +bool getNextFreeIndexOfAckWaitList(uint8_t *pIndex); +void HandleExpiredResponseCallbacks(void); +void initDeltaTokens(void); +IoT_Error_t registerJsonTokenOnDelta(jsonStruct_t *pStruct); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_SHADOW_AWS_IOT_SHADOW_RECORDS_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/aws_iot_version.h b/libraries/AWS_IOT_TRUSTX/src/aws_iot_version.h new file mode 100644 index 00000000000..832fcf48662 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/aws_iot_version.h @@ -0,0 +1,48 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file aws_iot_version.h + * @brief Constants defining the release version of the SDK. + * + * This file contains constants defining the release version of the SDK. + * This file is modified by AWS upon release of the SDK and should not be + * modified by the consumer of the SDK. The provided samples show example + * usage of these constants. + * + * Versioning of the SDK follows the MAJOR.MINOR.PATCH Semantic Versioning guidelines. + * @see http://semver.org/ + */ +#ifndef SRC_UTILS_AWS_IOT_VERSION_H_ +#define SRC_UTILS_AWS_IOT_VERSION_H_ + +/** + * @brief MAJOR version, incremented when incompatible API changes are made. + */ +#define VERSION_MAJOR 2 +/** + * @brief MINOR version when functionality is added in a backwards-compatible manner. + */ +#define VERSION_MINOR 1 +/** + * @brief PATCH version when backwards-compatible bug fixes are made. + */ +#define VERSION_PATCH 1 +/** + * @brief TAG is an (optional) tag appended to the version if a more descriptive verion is needed. + */ +#define VERSION_TAG "" + +#endif /* SRC_UTILS_AWS_IOT_VERSION_H_ */ diff --git a/libraries/AWS_IOT_TRUSTX/src/jsonParser.cpp b/libraries/AWS_IOT_TRUSTX/src/jsonParser.cpp new file mode 100644 index 00000000000..e9fc42df61a --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/jsonParser.cpp @@ -0,0 +1,331 @@ +#include +#include +#include +#include +#include "jsonParser.h" + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmnClass::Init(jsmn_parser_t *tokenParser) +{ + tokenParser->pos = 0; + tokenParser->toknext = 0; + tokenParser->toksuper = -1; +} + + +int jsmnClass::equate(const char *json, jsmntok_t *tok, const char *s) +{ + if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && + strncmp(json + tok->start, s, tok->end - tok->start) == 0) + { + return 0; + } + return -1; +} + + +jsmntok_t *jsmnClass::allocToken(jsmn_parser_t *tokenParser, jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *tok; + if (tokenParser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[tokenParser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +void jsmnClass::fillToken(jsmntok_t *token, jsmntype_t type, int start, int end) +{ + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +int jsmnClass::parsePrimitive(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *token; + int start; + + start = tokenParser->pos; + + for (; tokenParser->pos < len && js[tokenParser->pos] != '\0'; tokenParser->pos++) { + switch (js[tokenParser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[tokenParser->pos] < 32 || js[tokenParser->pos] >= 127) { + tokenParser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + tokenParser->pos = start; + return JSMN_ERROR_PART; +#endif + + found: + if (tokens == NULL) { + tokenParser->pos--; + return 0; + } + token = allocToken(tokenParser, tokens, num_tokens); + if (token == NULL) { + tokenParser->pos = start; + return JSMN_ERROR_NOMEM; + } + fillToken(token, JSMN_PRIMITIVE, start, tokenParser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = tokenParser->toksuper; +#endif + tokenParser->pos--; + return 0; +} + +/** + * Fills next token with JSON string. + */ +int jsmnClass::parseString(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens) +{ + jsmntok_t *token; + + int start = tokenParser->pos; + + tokenParser->pos++; + + /* Skip starting quote */ + for (; tokenParser->pos < len && js[tokenParser->pos] != '\0'; tokenParser->pos++) { + char c = js[tokenParser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = allocToken(tokenParser, tokens, num_tokens); + if (token == NULL) { + tokenParser->pos = start; + return JSMN_ERROR_NOMEM; + } + fillToken(token, JSMN_STRING, start+1, tokenParser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = tokenParser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && tokenParser->pos + 1 < len) { + int i; + tokenParser->pos++; + switch (js[tokenParser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + tokenParser->pos++; + for(i = 0; i < 4 && tokenParser->pos < len && js[tokenParser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[tokenParser->pos] >= 48 && js[tokenParser->pos] <= 57) || /* 0-9 */ + (js[tokenParser->pos] >= 65 && js[tokenParser->pos] <= 70) || /* A-F */ + (js[tokenParser->pos] >= 97 && js[tokenParser->pos] <= 102))) { /* a-f */ + tokenParser->pos = start; + return JSMN_ERROR_INVAL; + } + tokenParser->pos++; + } + tokenParser->pos--; + break; + /* Unexpected symbol */ + default: + tokenParser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + tokenParser->pos = start; + return JSMN_ERROR_PART; +} + + +/** + * Parse JSON string and fill tokens. + */ +int jsmnClass::parse(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens) +{ + int r; + int i; + jsmntok_t *token; + int count = tokenParser->toknext; + + for (; tokenParser->pos < len && js[tokenParser->pos] != '\0'; tokenParser->pos++) { + char c; + jsmntype_t type; + + c = js[tokenParser->pos]; + + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = allocToken(tokenParser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (tokenParser->toksuper != -1) { + tokens[tokenParser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = tokenParser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = tokenParser->pos; + tokenParser->toksuper = tokenParser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (tokenParser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[tokenParser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = tokenParser->pos + 1; + tokenParser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + if(token->type != type || tokenParser->toksuper == -1) { + return JSMN_ERROR_INVAL; + } + break; + } + token = &tokens[token->parent]; + } +#else + for (i = tokenParser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + tokenParser->toksuper = -1; + token->end = tokenParser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + tokenParser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = parseString(tokenParser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (tokenParser->toksuper != -1 && tokens != NULL) + tokens[tokenParser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + tokenParser->toksuper = tokenParser->toknext - 1; + break; + case ',': + if (tokens != NULL && tokenParser->toksuper != -1 && + tokens[tokenParser->toksuper].type != JSMN_ARRAY && + tokens[tokenParser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + tokenParser->toksuper = tokens[tokenParser->toksuper].parent; +#else + for (i = tokenParser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + tokenParser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && tokenParser->toksuper != -1) { + jsmntok_t *t = &tokens[tokenParser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = parsePrimitive(tokenParser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (tokenParser->toksuper != -1 && tokens != NULL) + tokens[tokenParser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = tokenParser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +jsmnClass jsmn; diff --git a/libraries/AWS_IOT_TRUSTX/src/jsonParser.h b/libraries/AWS_IOT_TRUSTX/src/jsonParser.h new file mode 100644 index 00000000000..85a02f0159b --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/jsonParser.h @@ -0,0 +1,82 @@ +#ifndef _JSON_PARSER_H_ +#define _JSON_PARSER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + + +/** + * JSON token description. + * type type (object, array, string etc.) + * start start position in JSON data string + * end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser_t; + + +class jsmnClass{ +private : + + jsmntok_t* allocToken(jsmn_parser_t *tokenParser, jsmntok_t *tokens, size_t num_tokens); + void fillToken(jsmntok_t *token, jsmntype_t type, int start, int end); + int parsePrimitive(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); + + +public: + void Init(jsmn_parser_t *tokenParser); + int equate(const char *json, jsmntok_t *tok, const char *s); + int parse(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens); + int parseString(jsmn_parser_t *tokenParser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens); + +}; + +extern jsmnClass jsmn; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/network_interface.h b/libraries/AWS_IOT_TRUSTX/src/network_interface.h new file mode 100644 index 00000000000..d3d73c6d600 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/network_interface.h @@ -0,0 +1,167 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file network_interface.h + * @brief Network interface definition for MQTT client. + * + * Defines an interface to the TLS layer to be used by the MQTT client. + * Starting point for porting the SDK to the networking layer of a new platform. + */ + +#ifndef __NETWORK_INTERFACE_H_ +#define __NETWORK_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include "timer_interface.h" +#include "network_platform.h" + +/** + * @brief Network Type + * + * Defines a type for the network struct. See structure definition below. + */ +typedef struct Network Network; + +/** + * @brief TLS Connection Parameters + * + * Defines a type containing TLS specific parameters to be passed down to the + * TLS networking layer to create a TLS secured socket. + */ +typedef struct { + const char *pRootCALocation; ///< Pointer to string containing the filename (including path) of the root CA file. + const char *pDeviceCertLocation; ///< Pointer to string containing the filename (including path) of the device certificate. + const char *pDevicePrivateKeyLocation; ///< Pointer to string containing the filename (including path) of the device private key file. + const char *pDestinationURL; ///< Pointer to string containing the endpoint of the MQTT service. + uint16_t DestinationPort; ///< Integer defining the connection port of the MQTT service. + uint32_t timeout_ms; ///< Unsigned integer defining the TLS handshake timeout value in milliseconds. + bool ServerVerificationFlag; ///< Boolean. True = perform server certificate hostname validation. False = skip validation \b NOT recommended. +} TLSConnectParams; + +/** + * @brief Network Structure + * + * Structure for defining a network connection. + */ +struct Network { + IoT_Error_t (*connect)(Network *, TLSConnectParams *); + + IoT_Error_t (*read)(Network *, unsigned char *, size_t, Timer *, size_t *); ///< Function pointer pointing to the network function to read from the network + IoT_Error_t (*write)(Network *, unsigned char *, size_t, Timer *, size_t *); ///< Function pointer pointing to the network function to write to the network + IoT_Error_t (*disconnect)(Network *); ///< Function pointer pointing to the network function to disconnect from the network + IoT_Error_t (*isConnected)(Network *); ///< Function pointer pointing to the network function to check if TLS is connected + IoT_Error_t (*destroy)(Network *); ///< Function pointer pointing to the network function to destroy the network object + + TLSConnectParams tlsConnectParams; ///< TLSConnect params structure containing the common connection parameters + TLSDataParams tlsDataParams; ///< TLSData params structure containing the connection data parameters that are specific to the library being used +}; + +/** + * @brief Initialize the TLS implementation + * + * Perform any initialization required by the TLS layer. + * Connects the interface to implementation by setting up + * the network layer function pointers to platform implementations. + * + * @param pNetwork - Pointer to a Network struct defining the network interface. + * @param pRootCALocation - Path of the location of the Root CA + * @param pDeviceCertLocation - Path to the location of the Device Cert + * @param pDevicyPrivateKeyLocation - Path to the location of the device private key file + * @param pDestinationURL - The target endpoint to connect to + * @param DestinationPort - The port on the target to connect to + * @param timeout_ms - The value to use for timeout of operation + * @param ServerVerificationFlag - used to decide whether server verification is needed or not + * + * @return IoT_Error_t - successful initialization or TLS error + */ +IoT_Error_t iot_tls_init(Network *pNetwork, const char *pRootCALocation, const char *pDeviceCertLocation, + const char *pDevicePrivateKeyLocation, const char *pDestinationURL, + uint16_t DestinationPort, uint32_t timeout_ms, bool ServerVerificationFlag); + +/** + * @brief Create a TLS socket and open the connection + * + * Creates an open socket connection including TLS handshake. + * + * @param pNetwork - Pointer to a Network struct defining the network interface. + * @param TLSParams - TLSConnectParams defines the properties of the TLS connection. + * @return IoT_Error_t - successful connection or TLS error + */ +IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *TLSParams); + +/** + * @brief Write bytes to the network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @param unsigned char pointer - buffer to write to socket + * @param integer - number of bytes to write + * @param Timer * - operation timer + * @return integer - number of bytes written or TLS error + * @return IoT_Error_t - successful write or TLS error code + */ +IoT_Error_t iot_tls_write(Network *, unsigned char *, size_t, Timer *, size_t *); + +/** + * @brief Read bytes from the network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @param unsigned char pointer - pointer to buffer where read bytes should be copied + * @param size_t - number of bytes to read + * @param Timer * - operation timer + * @param size_t - pointer to store number of bytes read + * @return IoT_Error_t - successful read or TLS error code + */ +IoT_Error_t iot_tls_read(Network *, unsigned char *, size_t, Timer *, size_t *); + +/** + * @brief Disconnect from network socket + * + * @param Network - Pointer to a Network struct defining the network interface. + * @return IoT_Error_t - successful read or TLS error code + */ +IoT_Error_t iot_tls_disconnect(Network *pNetwork); + +/** + * @brief Perform any tear-down or cleanup of TLS layer + * + * Called to cleanup any resources required for the TLS layer. + * + * @param Network - Pointer to a Network struct defining the network interface + * @return IoT_Error_t - successful cleanup or TLS error code + */ +IoT_Error_t iot_tls_destroy(Network *pNetwork); + +/** + * @brief Check if TLS layer is still connected + * + * Called to check if the TLS layer is still connected or not. + * + * @param Network - Pointer to a Network struct defining the network interface + * @return IoT_Error_t - TLS error code indicating status of network physical layer connection + */ +IoT_Error_t iot_tls_is_connected(Network *pNetwork); + +#ifdef __cplusplus +} +#endif + +#endif //__NETWORK_INTERFACE_H_ diff --git a/libraries/AWS_IOT_TRUSTX/src/network_mbedtls_wrapper.c b/libraries/AWS_IOT_TRUSTX/src/network_mbedtls_wrapper.c new file mode 100644 index 00000000000..07e2ed4138e --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/network_mbedtls_wrapper.c @@ -0,0 +1,408 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ +#include +#include +#include +#include +#include + +#include "aws_iot_config.h" +#include "aws_iot_error.h" +#include "network_interface.h" +#include "network_platform.h" + +#include "mbedtls/esp_debug.h" + +#include "esp_log.h" +#include "esp_vfs.h" + +static const char *TAG = "aws_iot"; + +/* This is the value used for ssl read timeout */ +#define IOT_SSL_READ_TIMEOUT 10 + +/* + * This is a function to do further verification if needed on the cert received. + * + * Currently used to print debug-level information about each cert. + */ +static int _iot_tls_verify_cert(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags) { + char buf[256]; + ((void) data); + + if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { + ESP_LOGD(TAG, "Verify requested for (Depth %d):", depth); + mbedtls_x509_crt_info(buf, sizeof(buf) - 1, "", crt); + ESP_LOGD(TAG, "%s", buf); + + if((*flags) == 0) { + ESP_LOGD(TAG, " This certificate has no flags"); + } else { + ESP_LOGD(TAG, "Verify result:%s", buf); + } + } + + return 0; +} + +static void _iot_tls_set_connect_params(Network *pNetwork, const char *pRootCALocation, const char *pDeviceCertLocation, + const char *pDevicePrivateKeyLocation, const char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) { + pNetwork->tlsConnectParams.DestinationPort = destinationPort; + pNetwork->tlsConnectParams.pDestinationURL = pDestinationURL; + pNetwork->tlsConnectParams.pDeviceCertLocation = pDeviceCertLocation; + pNetwork->tlsConnectParams.pDevicePrivateKeyLocation = pDevicePrivateKeyLocation; + pNetwork->tlsConnectParams.pRootCALocation = pRootCALocation; + pNetwork->tlsConnectParams.timeout_ms = timeout_ms; + pNetwork->tlsConnectParams.ServerVerificationFlag = ServerVerificationFlag; +} + +IoT_Error_t iot_tls_init(Network *pNetwork, const char *pRootCALocation, const char *pDeviceCertLocation, + const char *pDevicePrivateKeyLocation, const char *pDestinationURL, + uint16_t destinationPort, uint32_t timeout_ms, bool ServerVerificationFlag) { + _iot_tls_set_connect_params(pNetwork, pRootCALocation, pDeviceCertLocation, pDevicePrivateKeyLocation, + pDestinationURL, destinationPort, timeout_ms, ServerVerificationFlag); + + pNetwork->connect = iot_tls_connect; + pNetwork->read = iot_tls_read; + pNetwork->write = iot_tls_write; + pNetwork->disconnect = iot_tls_disconnect; + pNetwork->isConnected = iot_tls_is_connected; + pNetwork->destroy = iot_tls_destroy; + + pNetwork->tlsDataParams.flags = 0; + + return SUCCESS; +} + +IoT_Error_t iot_tls_is_connected(Network *pNetwork) { + /* Use this to add implementation which can check for physical layer disconnect */ + return NETWORK_PHYSICAL_LAYER_CONNECTED; +} + +IoT_Error_t iot_tls_connect(Network *pNetwork, TLSConnectParams *params) { + int ret = SUCCESS; + TLSDataParams *tlsDataParams = NULL; + char portBuffer[6]; + char info_buf[256]; + + if(NULL == pNetwork) { + return NULL_VALUE_ERROR; + } + + if(NULL != params) { + _iot_tls_set_connect_params(pNetwork, params->pRootCALocation, params->pDeviceCertLocation, + params->pDevicePrivateKeyLocation, params->pDestinationURL, + params->DestinationPort, params->timeout_ms, params->ServerVerificationFlag); + } + + tlsDataParams = &(pNetwork->tlsDataParams); + + mbedtls_net_init(&(tlsDataParams->server_fd)); + mbedtls_ssl_init(&(tlsDataParams->ssl)); + mbedtls_ssl_config_init(&(tlsDataParams->conf)); + +#ifdef CONFIG_MBEDTLS_DEBUG + mbedtls_esp_enable_debug_log(&(tlsDataParams->conf), 4); +#endif + + mbedtls_ctr_drbg_init(&(tlsDataParams->ctr_drbg)); + mbedtls_x509_crt_init(&(tlsDataParams->cacert)); + mbedtls_x509_crt_init(&(tlsDataParams->clicert)); + mbedtls_pk_init(&(tlsDataParams->pkey)); + + ESP_LOGD(TAG, "Seeding the random number generator..."); + mbedtls_entropy_init(&(tlsDataParams->entropy)); + if((ret = mbedtls_ctr_drbg_seed(&(tlsDataParams->ctr_drbg), mbedtls_entropy_func, &(tlsDataParams->entropy), + (const unsigned char *) TAG, strlen(TAG))) != 0) { + ESP_LOGE(TAG, "failed! mbedtls_ctr_drbg_seed returned -0x%x", -ret); + return NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED; + } + + /* Load root CA... + + Certs/keys can be paths or they can be raw data. These use a + very basic heuristic: if the cert starts with '/' then it's a + path, if it's longer than this then it's raw cert data (PEM or DER, + neither of which can start with a slash. */ + if (pNetwork->tlsConnectParams.pRootCALocation[0] == '/') { + ESP_LOGD(TAG, "Loading CA root certificate from file ..."); + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->cacert), pNetwork->tlsConnectParams.pRootCALocation); + } else { + ESP_LOGD(TAG, "Loading embedded CA root certificate ..."); + ret = mbedtls_x509_crt_parse(&(tlsDataParams->cacert), (const unsigned char *)pNetwork->tlsConnectParams.pRootCALocation, + strlen(pNetwork->tlsConnectParams.pRootCALocation)+1); + } + + if(ret < 0) { + ESP_LOGE(TAG, "failed! mbedtls_x509_crt_parse returned -0x%x while parsing root cert", -ret); + return NETWORK_X509_ROOT_CRT_PARSE_ERROR; + } + ESP_LOGD(TAG, "ok (%d skipped)", ret); + + /* Load client certificate... */ + if (pNetwork->tlsConnectParams.pDeviceCertLocation[0] == '/') { + ESP_LOGD(TAG, "Loading client cert from file..."); + ret = mbedtls_x509_crt_parse_file(&(tlsDataParams->clicert), + pNetwork->tlsConnectParams.pDeviceCertLocation); + } else { + ESP_LOGD(TAG, "Loading embedded client certificate..."); + ret = mbedtls_x509_crt_parse(&(tlsDataParams->clicert), + (const unsigned char *)pNetwork->tlsConnectParams.pDeviceCertLocation, + strlen(pNetwork->tlsConnectParams.pDeviceCertLocation)+1); + } + if(ret != 0) { + ESP_LOGE(TAG, "failed! mbedtls_x509_crt_parse returned -0x%x while parsing device cert", -ret); + return NETWORK_X509_DEVICE_CRT_PARSE_ERROR; + } + + /* Parse client private key... */ + if (pNetwork->tlsConnectParams.pDevicePrivateKeyLocation[0] == '/') { + ESP_LOGD(TAG, "Loading client private key from file..."); + ret = mbedtls_pk_parse_keyfile(&(tlsDataParams->pkey), + pNetwork->tlsConnectParams.pDevicePrivateKeyLocation, + ""); + } else { + ESP_LOGD(TAG, "Loading embedded client private key..."); + ret = mbedtls_pk_parse_key(&(tlsDataParams->pkey), + (const unsigned char *)pNetwork->tlsConnectParams.pDevicePrivateKeyLocation, + strlen(pNetwork->tlsConnectParams.pDevicePrivateKeyLocation)+1, + (const unsigned char *)"", 0); + } + if(ret != 0) { + ESP_LOGE(TAG, "failed! mbedtls_pk_parse_key returned -0x%x while parsing private key", -ret); + return NETWORK_PK_PRIVATE_KEY_PARSE_ERROR; + } + + /* Done parsing certs */ + ESP_LOGD(TAG, "ok"); + snprintf(portBuffer, 6, "%d", pNetwork->tlsConnectParams.DestinationPort); + ESP_LOGD(TAG, "Connecting to %s/%s...", pNetwork->tlsConnectParams.pDestinationURL, portBuffer); + if((ret = mbedtls_net_connect(&(tlsDataParams->server_fd), pNetwork->tlsConnectParams.pDestinationURL, + portBuffer, MBEDTLS_NET_PROTO_TCP)) != 0) { + ESP_LOGE(TAG, "failed! mbedtls_net_connect returned -0x%x", -ret); + switch(ret) { + case MBEDTLS_ERR_NET_SOCKET_FAILED: + return NETWORK_ERR_NET_SOCKET_FAILED; + case MBEDTLS_ERR_NET_UNKNOWN_HOST: + return NETWORK_ERR_NET_UNKNOWN_HOST; + case MBEDTLS_ERR_NET_CONNECT_FAILED: + default: + return NETWORK_ERR_NET_CONNECT_FAILED; + }; + } + + ret = mbedtls_net_set_block(&(tlsDataParams->server_fd)); + if(ret != 0) { + ESP_LOGE(TAG, "failed! net_set_(non)block() returned -0x%x", -ret); + return SSL_CONNECTION_ERROR; + } ESP_LOGD(TAG, "ok"); + + ESP_LOGD(TAG, "Setting up the SSL/TLS structure..."); + if((ret = mbedtls_ssl_config_defaults(&(tlsDataParams->conf), MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_config_defaults returned -0x%x", -ret); + return SSL_CONNECTION_ERROR; + } + + mbedtls_ssl_conf_verify(&(tlsDataParams->conf), _iot_tls_verify_cert, NULL); + + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) { + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_REQUIRED); + } else { + mbedtls_ssl_conf_authmode(&(tlsDataParams->conf), MBEDTLS_SSL_VERIFY_OPTIONAL); + } + mbedtls_ssl_conf_rng(&(tlsDataParams->conf), mbedtls_ctr_drbg_random, &(tlsDataParams->ctr_drbg)); + + mbedtls_ssl_conf_ca_chain(&(tlsDataParams->conf), &(tlsDataParams->cacert), NULL); + ret = mbedtls_ssl_conf_own_cert(&(tlsDataParams->conf), &(tlsDataParams->clicert), &(tlsDataParams->pkey)); + if(ret != 0) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_conf_own_cert returned %d", ret); + return SSL_CONNECTION_ERROR; + } + + mbedtls_ssl_conf_read_timeout(&(tlsDataParams->conf), pNetwork->tlsConnectParams.timeout_ms); + + if((ret = mbedtls_ssl_setup(&(tlsDataParams->ssl), &(tlsDataParams->conf))) != 0) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_setup returned -0x%x", -ret); + return SSL_CONNECTION_ERROR; + } + if((ret = mbedtls_ssl_set_hostname(&(tlsDataParams->ssl), pNetwork->tlsConnectParams.pDestinationURL)) != 0) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_set_hostname returned %d", ret); + return SSL_CONNECTION_ERROR; + } + ESP_LOGD(TAG, "SSL state connect : %d ", tlsDataParams->ssl.state); + mbedtls_ssl_set_bio(&(tlsDataParams->ssl), &(tlsDataParams->server_fd), mbedtls_net_send, NULL, + mbedtls_net_recv_timeout); + ESP_LOGD(TAG, "ok"); + + ESP_LOGD(TAG, "SSL state connect : %d ", tlsDataParams->ssl.state); + ESP_LOGD(TAG, "Performing the SSL/TLS handshake..."); + while((ret = mbedtls_ssl_handshake(&(tlsDataParams->ssl))) != 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_handshake returned -0x%x", -ret); + if(ret == MBEDTLS_ERR_X509_CERT_VERIFY_FAILED) { + ESP_LOGE(TAG, " Unable to verify the server's certificate. "); + } + return SSL_CONNECTION_ERROR; + } + } + + ESP_LOGD(TAG, "ok [ Protocol is %s ] [ Ciphersuite is %s ]", mbedtls_ssl_get_version(&(tlsDataParams->ssl)), + mbedtls_ssl_get_ciphersuite(&(tlsDataParams->ssl))); + if((ret = mbedtls_ssl_get_record_expansion(&(tlsDataParams->ssl))) >= 0) { + ESP_LOGD(TAG, " [ Record expansion is %d ]", ret); + } else { + ESP_LOGD(TAG, " [ Record expansion is unknown (compression) ]"); + } + + ESP_LOGD(TAG, "Verifying peer X.509 certificate..."); + + if(pNetwork->tlsConnectParams.ServerVerificationFlag == true) { + if((tlsDataParams->flags = mbedtls_ssl_get_verify_result(&(tlsDataParams->ssl))) != 0) { + ESP_LOGE(TAG, "failed"); + mbedtls_x509_crt_verify_info(info_buf, sizeof(info_buf), " ! ", tlsDataParams->flags); + ESP_LOGE(TAG, "%s", info_buf); + ret = SSL_CONNECTION_ERROR; + } else { + ESP_LOGD(TAG, "ok"); + ret = SUCCESS; + } + } else { + ESP_LOGW(TAG, " Server Verification skipped"); + ret = SUCCESS; + } + + if(LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { + if (mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl)) != NULL) { + ESP_LOGD(TAG, "Peer certificate information:"); + mbedtls_x509_crt_info((char *) info_buf, sizeof(info_buf) - 1, " ", mbedtls_ssl_get_peer_cert(&(tlsDataParams->ssl))); + ESP_LOGD(TAG, "%s", info_buf); + } + } + + return (IoT_Error_t) ret; +} + +IoT_Error_t iot_tls_write(Network *pNetwork, unsigned char *pMsg, size_t len, Timer *timer, size_t *written_len) { + size_t written_so_far; + bool isErrorFlag = false; + int frags, ret = 0; + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + + for(written_so_far = 0, frags = 0; + written_so_far < len && !has_timer_expired(timer); written_so_far += ret, frags++) { + while(!has_timer_expired(timer) && + (ret = mbedtls_ssl_write(&(tlsDataParams->ssl), pMsg + written_so_far, len - written_so_far)) <= 0) { + if(ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + ESP_LOGE(TAG, "failed! mbedtls_ssl_write returned -0x%x", -ret); + /* All other negative return values indicate connection needs to be reset. + * Will be caught in ping request so ignored here */ + isErrorFlag = true; + break; + } + } + if(isErrorFlag) { + break; + } + } + + *written_len = written_so_far; + + if(isErrorFlag) { + return NETWORK_SSL_WRITE_ERROR; + } else if(has_timer_expired(timer) && written_so_far != len) { + return NETWORK_SSL_WRITE_TIMEOUT_ERROR; + } + + return SUCCESS; +} + +IoT_Error_t iot_tls_read(Network *pNetwork, unsigned char *pMsg, size_t len, Timer *timer, size_t *read_len) { + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + mbedtls_ssl_context *ssl = &(tlsDataParams->ssl); + mbedtls_ssl_config *ssl_conf = &(tlsDataParams->conf); + uint32_t read_timeout; + size_t rxLen = 0; + int ret; + + read_timeout = ssl_conf->read_timeout; + + while (len > 0) { + + /* Make sure we never block on read for longer than timer has left, + but also that we don't block indefinitely (ie read_timeout > 0) */ + mbedtls_ssl_conf_read_timeout(ssl_conf, MAX(1, MIN(read_timeout, left_ms(timer)))); + + ret = mbedtls_ssl_read(ssl, pMsg, len); + + /* Restore the old timeout */ + mbedtls_ssl_conf_read_timeout(ssl_conf, read_timeout); + + if (ret > 0) { + rxLen += ret; + pMsg += ret; + len -= ret; + } else if (ret == 0 || (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret != MBEDTLS_ERR_SSL_TIMEOUT)) { + return NETWORK_SSL_READ_ERROR; + } + + // Evaluate timeout after the read to make sure read is done at least once + if (has_timer_expired(timer)) { + break; + } + } + + if (len == 0) { + *read_len = rxLen; + return SUCCESS; + } + + if (rxLen == 0) { + return NETWORK_SSL_NOTHING_TO_READ; + } else { + return NETWORK_SSL_READ_TIMEOUT_ERROR; + } +} + +IoT_Error_t iot_tls_disconnect(Network *pNetwork) { + mbedtls_ssl_context *ssl = &(pNetwork->tlsDataParams.ssl); + int ret = 0; + do { + ret = mbedtls_ssl_close_notify(ssl); + } while(ret == MBEDTLS_ERR_SSL_WANT_WRITE); + + /* All other negative return values indicate connection needs to be reset. + * No further action required since this is disconnect call */ + + return SUCCESS; +} + +IoT_Error_t iot_tls_destroy(Network *pNetwork) { + TLSDataParams *tlsDataParams = &(pNetwork->tlsDataParams); + + mbedtls_net_free(&(tlsDataParams->server_fd)); + + mbedtls_x509_crt_free(&(tlsDataParams->clicert)); + mbedtls_x509_crt_free(&(tlsDataParams->cacert)); + mbedtls_pk_free(&(tlsDataParams->pkey)); + mbedtls_ssl_free(&(tlsDataParams->ssl)); + mbedtls_ssl_config_free(&(tlsDataParams->conf)); + mbedtls_ctr_drbg_free(&(tlsDataParams->ctr_drbg)); + mbedtls_entropy_free(&(tlsDataParams->entropy)); + + return SUCCESS; +} diff --git a/libraries/AWS_IOT_TRUSTX/src/network_platform.h b/libraries/AWS_IOT_TRUSTX/src/network_platform.h new file mode 100644 index 00000000000..1193a409fcb --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/network_platform.h @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include "mbedtls/platform.h" +#include "mbedtls/net.h" +#include "mbedtls/ssl.h" +#include "mbedtls/entropy.h" +#include "mbedtls/ctr_drbg.h" +#include "mbedtls/certs.h" +#include "mbedtls/x509.h" +#include "mbedtls/error.h" +#include "mbedtls/debug.h" +#include "mbedtls/timing.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief TLS Connection Parameters + * + * Defines a type containing TLS specific parameters to be passed down to the + * TLS networking layer to create a TLS secured socket. + */ +typedef struct _TLSDataParams { + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ssl_context ssl; + mbedtls_ssl_config conf; + uint32_t flags; + mbedtls_x509_crt cacert; + mbedtls_x509_crt clicert; + mbedtls_pk_context pkey; + mbedtls_net_context server_fd; +}TLSDataParams; + +#define IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H + +#ifdef __cplusplus +} +#endif + +#endif //IOTSDKC_NETWORK_MBEDTLS_PLATFORM_H_H diff --git a/libraries/AWS_IOT_TRUSTX/src/threads_freertos.c b/libraries/AWS_IOT_TRUSTX/src/threads_freertos.c new file mode 100644 index 00000000000..065ead78cb9 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/threads_freertos.c @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +#include "threads_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initialize the provided mutex + * + * Call this function to initialize the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be initialized + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_init(IoT_Mutex_t *pMutex) { + + pMutex->mutex = xSemaphoreCreateRecursiveMutex(); + return pMutex->mutex ? SUCCESS : MUTEX_INIT_ERROR; +} + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change + * Blocking, thread will block until lock request fails + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_lock(IoT_Mutex_t *pMutex) { + xSemaphoreTakeRecursive(pMutex->mutex, portMAX_DELAY); + return SUCCESS; +} + +/** + * @brief Try to lock the provided mutex + * + * Call this function to attempt to lock the mutex before performing a state change + * Non-Blocking, immediately returns with failure if lock attempt fails + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_trylock(IoT_Mutex_t *pMutex) { + if (xSemaphoreTakeRecursive(pMutex->mutex, 0)) { + return SUCCESS; + } else { + return MUTEX_LOCK_ERROR; + } +} + +/** + * @brief Unlock the provided mutex + * + * Call this function to unlock the mutex before performing a state change + * + * @param IoT_Mutex_t - pointer to the mutex to be unlocked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_unlock(IoT_Mutex_t *pMutex) { + if (xSemaphoreGiveRecursive(pMutex->mutex)) { + return SUCCESS; + } else { + return MUTEX_UNLOCK_ERROR; + } +} + +/** + * @brief Destroy the provided mutex + * + * Call this function to destroy the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be destroyed + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_destroy(IoT_Mutex_t *pMutex) { + vSemaphoreDelete(pMutex->mutex); + return SUCCESS; +} + +#ifdef __cplusplus +} +#endif + diff --git a/libraries/AWS_IOT_TRUSTX/src/threads_interface.h b/libraries/AWS_IOT_TRUSTX/src/threads_interface.h new file mode 100644 index 00000000000..b4bc3705d20 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/threads_interface.h @@ -0,0 +1,108 @@ +/* +* Copyright 2015-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. +* +* Licensed under the Apache License, Version 2.0 (the "License"). +* You may not use this file except in compliance with the License. +* A copy of the License is located at +* +* http://aws.amazon.com/apache2.0 +* +* or in the "license" file accompanying this file. This file 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. +*/ + +/** + * @file threads_interface.h + * @brief Thread interface definition for MQTT client. + * + * Defines an interface that can be used by system components for multithreaded situations. + * Starting point for porting the SDK to the threading hardware layer of a new platform. + */ + +#include "aws_iot_config.h" + +#ifdef _ENABLE_THREAD_SUPPORT_ +#ifndef __THREADS_INTERFACE_H_ +#define __THREADS_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The platform specific timer header that defines the Timer struct + */ +#include "threads_platform.h" + +#include + +/** + * @brief Mutex Type + * + * Forward declaration of a mutex struct. The definition of this struct is + * platform dependent. When porting to a new platform add this definition + * in "threads_platform.h". + * + */ +typedef struct _IoT_Mutex_t IoT_Mutex_t; + +/** + * @brief Initialize the provided mutex + * + * Call this function to initialize the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be initialized + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_init(IoT_Mutex_t *); + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change + * This is a blocking call. + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_lock(IoT_Mutex_t *); + +/** + * @brief Lock the provided mutex + * + * Call this function to lock the mutex before performing a state change. + * This is not a blocking call. + * + * @param IoT_Mutex_t - pointer to the mutex to be locked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_trylock(IoT_Mutex_t *); + +/** + * @brief Unlock the provided mutex + * + * Call this function to unlock the mutex before performing a state change + * + * @param IoT_Mutex_t - pointer to the mutex to be unlocked + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_unlock(IoT_Mutex_t *); + +/** + * @brief Destroy the provided mutex + * + * Call this function to destroy the mutex + * + * @param IoT_Mutex_t - pointer to the mutex to be destroyed + * @return IoT_Error_t - error code indicating result of operation + */ +IoT_Error_t aws_iot_thread_mutex_destroy(IoT_Mutex_t *); + +#ifdef __cplusplus +} +#endif + +#endif /*__THREADS_INTERFACE_H_*/ +#endif /*_ENABLE_THREAD_SUPPORT_*/ diff --git a/libraries/AWS_IOT_TRUSTX/src/threads_platform.h b/libraries/AWS_IOT_TRUSTX/src/threads_platform.h new file mode 100644 index 00000000000..011a1a0b23c --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/threads_platform.h @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#include "threads_interface.h" + +#ifndef AWS_IOTSDK_THREADS_PLATFORM_H +#define AWS_IOTSDK_THREADS_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +/** + * @brief Mutex Type + * + * definition of the Mutex struct. Platform specific + * + */ +struct _IoT_Mutex_t { + SemaphoreHandle_t mutex; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOTSDK_THREADS_PLATFORM_H */ + + diff --git a/libraries/AWS_IOT_TRUSTX/src/timer.c b/libraries/AWS_IOT_TRUSTX/src/timer.c new file mode 100644 index 00000000000..ee4d8b5be16 --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/timer.c @@ -0,0 +1,83 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +/** + * @file timer.c + * @brief FreeRTOS implementation of the timer interface uses ticks. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include "timer_platform.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_log.h" + +const static char *TAG = "aws_timer"; + +bool has_timer_expired(Timer *timer) { + uint32_t now = xTaskGetTickCount(); + bool expired = (now - timer->start_ticks) >= timer->timeout_ticks; + + /* AWS IoT SDK isn't very RTOS friendly because it polls for "done + timers" a lot without ever sleeping on them. So we hack in some + amount of sleeping here: if it seems like the caller is polling + an unexpired timer in a tight loop then we delay a tick to let + things progress elsewhere. + */ + if(!expired && now == timer->last_polled_ticks) { + vTaskDelay(1); + } + timer->last_polled_ticks = now; + return expired; +} + +void countdown_ms(Timer *timer, uint32_t timeout) { + timer->start_ticks = xTaskGetTickCount(); + timer->timeout_ticks = timeout / portTICK_PERIOD_MS; + timer->last_polled_ticks = 0; +} + +uint32_t left_ms(Timer *timer) { + uint32_t now = xTaskGetTickCount(); + uint32_t elapsed = now - timer->start_ticks; + if (elapsed < timer->timeout_ticks) { + return (timer->timeout_ticks - elapsed) * portTICK_PERIOD_MS; + } else { + return 0; + } +} + +void countdown_sec(Timer *timer, uint32_t timeout) { + if (timeout > UINT32_MAX / 1000) { + ESP_LOGE(TAG, "timeout is out of range: %ds", timeout); + } + countdown_ms(timer, timeout * 1000); +} + +void init_timer(Timer *timer) { + timer->start_ticks = 0; + timer->timeout_ticks = 0; + timer->last_polled_ticks = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/libraries/AWS_IOT_TRUSTX/src/timer_interface.h b/libraries/AWS_IOT_TRUSTX/src/timer_interface.h new file mode 100644 index 00000000000..0bef0fc940c --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/timer_interface.h @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2014 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Allan Stockdill-Mander - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * @file timer_interface.h + * @brief Timer interface definition for MQTT client. + * + * Defines an interface to timers that can be used by other system + * components. MQTT client requires timers to handle timeouts and + * MQTT keep alive. + * Starting point for porting the SDK to the timer hardware layer of a new platform. + */ + +#ifndef __TIMER_INTERFACE_H_ +#define __TIMER_INTERFACE_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * The platform specific timer header that defines the Timer struct + */ +#include "timer_platform.h" + +#include +#include + +/** + * @brief Timer Type + * + * Forward declaration of a timer struct. The definition of this struct is + * platform dependent. When porting to a new platform add this definition + * in "timer_.h" and include that file above. + * + */ +typedef struct Timer Timer; + +/** + * @brief Check if a timer is expired + * + * Call this function passing in a timer to check if that timer has expired. + * + * @param Timer - pointer to the timer to be checked for expiration + * @return bool - true = timer expired, false = timer not expired + */ +bool has_timer_expired(Timer *); + +/** + * @brief Create a timer (milliseconds) + * + * Sets the timer to expire in a specified number of milliseconds. + * + * @param Timer - pointer to the timer to be set to expire in milliseconds + * @param uint32_t - set the timer to expire in this number of milliseconds + */ +void countdown_ms(Timer *, uint32_t); + +/** + * @brief Create a timer (seconds) + * + * Sets the timer to expire in a specified number of seconds. + * + * @param Timer - pointer to the timer to be set to expire in seconds + * @param uint32_t - set the timer to expire in this number of seconds + */ +void countdown_sec(Timer *, uint32_t); + +/** + * @brief Check the time remaining on a given timer + * + * Checks the input timer and returns the number of milliseconds remaining on the timer. + * + * @param Timer - pointer to the timer to be set to checked + * @return int - milliseconds left on the countdown timer + */ +uint32_t left_ms(Timer *); + +/** + * @brief Initialize a timer + * + * Performs any initialization required to the timer passed in. + * + * @param Timer - pointer to the timer to be initialized + */ +void init_timer(Timer *); + +#ifdef __cplusplus +} +#endif + +#endif //__TIMER_INTERFACE_H_ diff --git a/libraries/AWS_IOT_TRUSTX/src/timer_platform.h b/libraries/AWS_IOT_TRUSTX/src/timer_platform.h new file mode 100644 index 00000000000..07e7385db3e --- /dev/null +++ b/libraries/AWS_IOT_TRUSTX/src/timer_platform.h @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2015 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * Additions Copyright 2016 Espressif Systems (Shanghai) PTE LTD + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file 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. + */ + +#ifndef AWS_IOT_PLATFORM_H +#define AWS_IOT_PLATFORM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "timer_interface.h" + +/** + * definition of the Timer struct. Platform specific + */ +struct Timer { + uint32_t start_ticks; + uint32_t timeout_ticks; + uint32_t last_polled_ticks; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* AWS_IOT_PLATFORM_H */ diff --git a/tools/sdk/include/trustx/optiga/CryptoLib.h b/tools/sdk/include/trustx/optiga/CryptoLib.h new file mode 100644 index 00000000000..4dcaae72851 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/CryptoLib.h @@ -0,0 +1,175 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file defines APIs, types and data structures for Crypto Wrapper. +* +* \ingroup grCryptoLibWrapper +* @{ +*/ +#ifndef _CRYPTO_LIB_H_ +#define _CRYPTO_LIB_H_ + +#include +#include "optiga/common/Datatypes.h" +#include "optiga/common/CommandLib.h" +/**************************************************************************** + * + * Hash Defines. + * + ****************************************************************************/ + + + +///Requested operation completed without any error +#define CRYPTO_LIB_OK 0x76428903 + +///Null parameter(s) +#define CRYPTO_LIB_NULL_PARAM 0x80003001 + +///Certificate parse failure +#define CRYPTO_LIB_CERT_PARSE_FAIL (CRYPTO_LIB_NULL_PARAM + 1) + +///Signature Verification failure +#define CRYPTO_LIB_VERIFY_SIGN_FAIL (CRYPTO_LIB_NULL_PARAM + 2) + +///SHA256 generation failure +#define CRYPTO_LIB_SHA256_FAIL (CRYPTO_LIB_NULL_PARAM + 3) + +///Length of input is zero +#define CRYPTO_LIB_LENZERO_ERROR (CRYPTO_LIB_NULL_PARAM + 4) + +///Length of Parameters are zero +#define CRYPTO_LIB_LENMISMATCH_ERROR (CRYPTO_LIB_NULL_PARAM + 5) + +///Memory allocation failure +#define CRYPTO_LIB_MEMORY_FAIL (CRYPTO_LIB_NULL_PARAM + 6) + +///Insufficient memory +#define CRYPTO_LIB_INSUFFICIENT_MEMORY (CRYPTO_LIB_NULL_PARAM + 7) + +///Generic error condition +#define CRYPTO_LIB_ERROR 0xF1743903 + +/**************************************************************************** + * + * Common data structure used across all functions. + * + ****************************************************************************/ + +/** + * \brief Structure which holds parsed certificate data. + */ +typedef struct sCertificate_d +{ + ///Public Key + sbBlob_d sPublicKey; + + ///Certificate Signature + sbBlob_d sCertSignature; + + ///Certificate Data start + sbBlob_d sCertData; +}sCertificate_d; + +/** + * \brief Structure which holds data for signature verification + */ +typedef struct sSignatureVector_d +{ + ///Public Key + sbBlob_d *psPublicKey; + + /// Signature + sbBlob_d *psSignature; + + /// Message + sbBlob_d *psMessage; +}sSignatureVector_d; + + +/**************************************************************************** + * + * Definitions for Crypto Libray APIs. + * + ****************************************************************************/ +/** + * \brief Parses raw X509 v3 certificate into a custom defined certificate structure. + */ +LIBRARY_EXPORTS int32_t CryptoLib_ParseCertificate(const sbBlob_d *PpsRawCertificate,sCertificate_d *PpsCertificate); + +/** + * \brief Verifies the signature using the given public key. + */ +LIBRARY_EXPORTS int32_t CryptoLib_VerifySignature(const sSignatureVector_d *PpsSignatureVector); + +/** + * \brief Gets Random bytes of data for a given length. + */ +LIBRARY_EXPORTS int32_t CryptoLib_GetRandom(uint16_t PwRandomDataLength,sCmdResponse_d *PpsResponse); + +/** + * \brief Generates seed by concatenating random and the counter and hashing(SHA256) the concatenated value. + */ +LIBRARY_EXPORTS int32_t CryptoLib_GenerateSeed(puint8_t PpbSeed, uint32_t PdwSeedLength); + +/// @cond hidden +/** + * \brief Gets the crypto version number along with build number + */ + LIBRARY_EXPORTS int32_t CryptoLib_WolfSSlVersion(sbBlob_d *PpsVersion); +/// @endcond + +#endif //_CRYPTO_LIB_H_ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/sdk/include/trustx/optiga/Version.h b/tools/sdk/include/trustx/optiga/Version.h new file mode 100644 index 00000000000..0dc121d755c --- /dev/null +++ b/tools/sdk/include/trustx/optiga/Version.h @@ -0,0 +1,39 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file Version.h +* +* \brief This file defines the Host Library version number. +* +*/ + +#ifndef _VERSION_H_ +#define _VERSION_H_ + +/// Version information of the Host Library +#define VERSION_HOST_LIBRARY "Ver 1.50.1153" + +#endif //_VERSION_H_ + + diff --git a/tools/sdk/include/trustx/optiga/cmd/CommandLib.h b/tools/sdk/include/trustx/optiga/cmd/CommandLib.h new file mode 100644 index 00000000000..d87199a3112 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/cmd/CommandLib.h @@ -0,0 +1,880 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file defines APIs, types and data structures used in the +* Command Library implementation. +* +* \ingroup grCmdLib +* @{ +*/ +#ifndef _CMD_LIB_H_ +#define _CMD_LIB_H_ + +#include +#include "optiga/common/Datatypes.h" +#include "optiga/common/ErrorCodes.h" +#include "optiga/common/AuthLibSettings.h" +#include "optiga/comms/optiga_comms.h" + +/**************************************************************************** + * + * API Error Codes + * + ****************************************************************************/ +///Requested operation completed without any error +#define CMD_LIB_OK 0x75E96B01 + +///Device error, the device error(last error code) is available in the least significant byte e.g. for Invalid OID 0x80010001 is returned. +#define CMD_DEV_ERROR 0x80010000 + +///An error to indicate that command execution failed.It is possibly that the application on security chip is not open or communication failure has occurred. +#define CMD_DEV_EXEC_ERROR 0x80020000 + +///Null parameter(s) +#define CMD_LIB_NULL_PARAM 0x80001001 + +///Invalid or unsupported parameter(s) +#define CMD_LIB_INVALID_PARAM (CMD_LIB_NULL_PARAM + 1) + +///Insufficient memory +#define CMD_LIB_INSUFFICIENT_MEMORY (CMD_LIB_NULL_PARAM + 2) + +///Length of Input is zero +#define CMD_LIB_LENZERO_ERROR (CMD_LIB_NULL_PARAM + 3) + +///Invalid Session ID +#define CMD_LIB_INVALID_SESSIONID (CMD_LIB_NULL_PARAM + 4) + +///Invalid Tag +#define CMD_LIB_INVALID_TAG (CMD_LIB_NULL_PARAM + 5) + +///Invalid Tag Length +#define CMD_LIB_INVALID_TAGLEN (CMD_LIB_NULL_PARAM + 6) + +///Invalid Length +#define CMD_LIB_INVALID_LEN (CMD_LIB_NULL_PARAM + 7) + +///Decryption failed +#define CMD_LIB_DECRYPT_FAILURE (CMD_LIB_NULL_PARAM + 8) + +///Invalid OID +#define CMD_LIB_INVALID_OID (CMD_LIB_NULL_PARAM + 9) + +///Generic error condition +#define CMD_LIB_ERROR 0xF87ECF01 + +///Length of the hash context information for SHA 256 +#define CALC_HASH_SHA256_CONTEXT_SIZE (130) + +///Overhead for all hash calculation operations (Tag and Length size (3 bytes) + APDU header (4 bytes)) +#define CALC_HASH_FIXED_OVERHEAD_SIZE (0x03 + 0x04) + +///Overhead for import or export hash context (Tag and Length size (3 bytes)) +#define CALC_HASH_IMPORT_OR_EXPORT_OVERHEAD_SIZE (0x03) + +///Overhead for import and export hash context +#define CALC_HASH_IMPORT_AND_EXPORT_OVERHEAD_SIZE (0x06) + +/**************************************************************************** + * + * Common data structure used across all functions. + * + ****************************************************************************/ + + +/** + * \brief Structure to receive the response of the device for a command. + */ +typedef struct sCmdResponse_d +{ + ///The length of the buffer + uint16_t wBufferLength; + + ///Pointer to the buffer to copy the response + uint8_t *prgbBuffer; + + ///Actual length of the response + uint16_t wRespLength; +}sCmdResponse_d; + +/** + * \brief Function to send a command and receive response for the command. + */ +typedef int32_t (*pFTransceive)(const void* ctx,const uint8_t *PprgbWriteBuffer, const uint16_t *PpwWriteBufferLen, uint8_t *PprgbReadBuffer, uint16_t *PpwReadBufferLen); + + +/**************************************************************************** + * + * Definitions related to OpenApplication and CloseApplication commands. + * + ****************************************************************************/ +/** + * \brief Enumerations to open the application on security chip. + */ +typedef enum eOpenType_d +{ + ///Initialise a clean application context + eInit = 0x00 +}eOpenType_d; + +/** + * \brief Structure to specify open application command parameters. + */ +typedef struct sOpenApp_d +{ + ///Type of option for Open application + eOpenType_d eOpenType; +}sOpenApp_d; + +/** + * \brief Opens the Security Chip Application. + */ +LIBRARY_EXPORTS int32_t CmdLib_OpenApplication(const sOpenApp_d* PpsOpenApp); + +/// @cond hidden +LIBRARY_EXPORTS void CmdLib_SetOptigaCommsContext(const optiga_comms_t *p_input_optiga_comms); +/// @endcond +/**************************************************************************** + * + * Definitions related to GetDataObject and SetDataObject commands. + * + ****************************************************************************/ + +/** + * \brief Enumeration to specify data or metadata. + */ +typedef enum eDataOrMedata_d +{ + ///Read or write data + eDATA, + + ///Read or write metadata + eMETA_DATA +}eDataOrMedata_d; + + +/** + * \brief Enumeration to specify write option. + */ +typedef enum eWriteOption_d +{ + ///Perform write operation + eWRITE, + + ///Perform erase and write operation + eERASE_AND_WRITE +}eWriteOption_d; + +/** + * \brief Structure to specify GetDataObject command parameters. + */ +typedef struct sGetData_d +{ + ///OID of data object + uint16_t wOID; + + ///Offset within the data object + uint16_t wOffset; + + ///Number of data bytes to read + uint16_t wLength; + + ///To read data or metadata + eDataOrMedata_d eDataOrMdata; +}sGetData_d; + +/** + * \brief Structure to specify SetDataObject command parameters. + */ +typedef struct sSetData_d +{ + ///OID of data object + uint16_t wOID; + + ///Offset within the data object + uint16_t wOffset; + + ///Number of data bytes to write + uint16_t wLength; + + ///Data bytes to be written + uint8_t *prgbData; + + ///To write data or metadata + eDataOrMedata_d eDataOrMdata; + + ///Write option + eWriteOption_d eWriteOption; +}sSetData_d; + + +#ifdef MODULE_ENABLE_READ_WRITE +/** + * \brief Reads the specified data object by issuing GetDataObject command. + */ +LIBRARY_EXPORTS int32_t CmdLib_GetDataObject(const sGetData_d *PpsGDVector, sCmdResponse_d *PpsResponse); + +/** + * \brief Writes to the specified data object by issuing SetDataObject command. + */ +LIBRARY_EXPORTS int32_t CmdLib_SetDataObject(const sSetData_d *PpsSDVector); + +/** + * \brief Reads maximum communication buffer size supported by the security chip. + */ +LIBRARY_EXPORTS uint16_t CmdLib_GetMaxCommsBufferSize(Void); + +#endif +/**************************************************************************** + * + * Definitions related to authentication commands. + * + ****************************************************************************/ + +///Challenge minimum length +#define CHALLENGE_MIN_LEN 0x08 + +///Challenge maximum length +#define CHALLENGE_MAX_LEN 0x100 + +/** + * \brief Enumeration to specify an authentication scheme. + */ +typedef enum eAuthScheme_d +{ + ///The ECDSA signature scheme as specified within FIPS 186-3 (SHA256) + eECDSA = 0x91, + ///DTLS Client scheme with cipher suite as DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 + eDTLSClient = 0x99 +}eAuthScheme_d; + +/** + * \brief Structure to specify authentication command parameters. + */ +typedef struct sAuthMsg_d +{ + ///Challenge length + uint16_t wRndLength; + + ///Challenge + uint8_t *prgbRnd; + + ///Authentication scheme + eAuthScheme_d eAuthScheme; + + ///Private key + uint16_t wOIDDevPrivKey; +}sAuthMsg_d; + +/** + * \brief Structure to specify authentication Scheme parameters. + */ +typedef struct sAuthScheme_d +{ + ///OID of Device Private key + uint16_t wDevicePrivKey; + + ///OID of Session Key + uint16_t wSessionKeyId; + + ///Authentication scheme + eAuthScheme_d eAuthScheme; + +}sAuthScheme_d; + +/** + * \brief Enumeration to specify the type of the random number + */ +typedef enum eRngType_d +{ + ///True random number + eTRNG = 0x00, + ///Deterministic random number + eDRNG = 0x01, +}eRngType_d; + + +/** +* \brief Structure to specify option to generate random number +*/ +typedef struct sRngOptions_d +{ + ///Type of Random number + eRngType_d eRngType; + ///Length of the random number to be generated + uint16_t wRandomDataLen; +}sRngOptions_d; + +/** + * \brief Gets the signature generated by Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_GetSignature(const sAuthMsg_d *PpsAuthMsg, sCmdResponse_d *PpsResponse); + +/** + * \brief Gets the true random bytes generated by Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_GetRandom(const sRngOptions_d *PpsRng, sCmdResponse_d *PpsResponse); + +/** + * \brief Sets the Authentication Scheme by issuing SetAuthScheme command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_SetAuthScheme(const sAuthScheme_d *PpsAuthVector); + +/** + * \brief Enumeration to specify Hashing algorithm. + */ +typedef enum eHashAlg_d +{ + ///SHA256 for Hashing + eSHA256 = 0xE2 +}eHashAlg_d; + +/** + * \brief Enumeration to specify Hashing Sequence. + */ +typedef enum eHashSequence_d +{ + ///Start hashing + eStartHash = 0x00, + + ///Start and Finalize hashing + eStartFinalizeHash = 0x01, + + ///Continue hashing + eContinueHash = 0x02, + + ///Finalize hashing + eFinalizeHash = 0x03, + + ///Terminate hashing + eTerminateHash = 0x04, + + ///Get Intermediate hash + eIntermediateHash = 0x05 + +}eHashSequence_d; + +/** + * \brief Enumeration to specify Hashing data type. + */ +typedef enum eDataType_d +{ + ///Data stream + eDataStream = 0x00, + + ///OID contents + eOIDData = 0x01 +}eDataType_d; + +/** + * \brief Structure to specify OID information. + */ +typedef struct sOIDInfo_d +{ + ///OID of data object + uint16_t wOID; + + ///Offset within the data object + uint16_t wOffset; + + ///Number of data bytes starting from the offset + uint16_t wLength; + +}sOIDInfo_d; + +/** + * \brief Enumeration to specify the action to be performed with the context. + */ +typedef enum eContextAction_d +{ + ///Context data is not used + eUnused = 0x00, + + ///Import hash context to perform the hash + eImport = 0x06, + + ///Export the current active hash context + eExport = 0x07, + + ///Import the provided hash context and export back the hash context after hashing + eImportExport = 0x67 +}eContextAction_d; + +/** + * \brief Structure to specify the information for context information. + */ +typedef struct sContextInfo_d +{ + ///Action on the input context + eContextAction_d eContextAction; + + ///Pointer to memory , to copy to/from context data + uint8_t* pbContextData; + + ///Length of the context while importing/ Length of memory while exporting context + uint16_t dwContextLen; +}sContextInfo_d; + +/** + * \brief Structure to specify parameters for Hash generation. + */ +typedef struct sCalcHash_d +{ + ///Hashing Algorithm + eHashAlg_d eHashAlg; + + ///Hashing sequence + eHashSequence_d eHashSequence; + + ///Type of data for hashing + eDataType_d eHashDataType; + + ///Data stream blob for hashing + sbBlob_d sDataStream; + + ///Object data for hashing + sOIDInfo_d sOIDData; + + ///Output Hash + sCmdResponse_d sOutHash; + + ///Context Information + sContextInfo_d sContextInfo; +}sCalcHash_d; + +/** + * \brief Structure to specify hash information. + */ +typedef struct sHashinfo +{ + ///Hash Algorithm type + eHashAlg_d eHashAlgo; + + ///Hash Length + uint8_t bHashLen; + + ///Hash context data + uint16_t wHashCntx; +}sHashinfo_d; + +/** + * \brief Enumeration for supported Signing scheme. + */ +typedef enum eSignScheme_d +{ + ///ECDSA FIPS 186-3 w/o hash + eECDSA_FIPS_186_3_WITHOUT_HASH = 0x11 +}eSignScheme_d; + +/** + * \brief Enumeration for supported algorithm identifier. + */ +typedef enum eAlgId_d +{ + ///Elliptic curve key on NIST P256 curve + eECC_NIST_P256 = 0x03, + + ///Elliptic curve key on NIST P384 curve + eECC_NIST_P384 = 0x04 +}eAlgId_d; + +/** + * \brief Structure to specify public key for verify hash + */ +typedef struct sPubKeyInput_d +{ + ///Algorithm identifier + eAlgId_d eAlgId; + + ///Data stream containing public key + sbBlob_d sDataStream; +}sPubKeyInput_d; + +/** + * \brief Structure to specify parameters for Signature verification. + */ +typedef struct sVerifyOption_d +{ + ///Signing scheme + eSignScheme_d eSignScheme; + + ///Source of the public key + eDataType_d eVerifyDataType; + + ///Data stream blob for hashing + sPubKeyInput_d sPubKeyInput; + + ///OID of data object + uint16_t wOIDPubKey; +}sVerifyOption_d; + +/** + * \brief Structure to return key pair as output. + */ +typedef struct sOutKeyPair_d +{ + ///Blob pointing to memory to store public key + sbBlob_d sPublicKey; + + ///Blob pointing to memory to store private key + sbBlob_d sPrivateKey; +}sOutKeyPair_d; + +/** + * \brief Enumeration to specify the action on key pair. + */ +typedef enum eKeyExport_d +{ + ///Stores only the private key in the OID and exports the public key + eStorePrivKeyOnly = 0x01, + + ///Exports both public and private key as the output + eExportKeyPair = 0x07 +}eKeyExport_d; + +/** + * \brief Enumeration to specify supported key usage. + */ +typedef enum eKeyUsage_d +{ + ///The key is used for authentication + eAuthentication = 0x01, + + ///The key is used for encryption + eEncryption = 0x02, + + ///The key is used for host firmware update + eHostFwUpdate = 0x04, + + ///The key is used for device management + eDevMgmt = 0x08, + + ///The key is used for signature + eSign = 0x10, + + ///The key agreement + eKeyAgreement = 0x20 +}eKeyUsage_d; + +/** + * \brief Structure to specify parameters for Key Pair Generation. + */ +typedef struct sKeyPairOption_d +{ + ///Algorithm identifier of the key to be generated + eAlgId_d eAlgId; + + ///Option to export/store keys + eKeyExport_d eKeyExport; + + ///OID of the private key/session context to be generated + uint16_t wOIDPrivKey; + + ///Key Usage + eKeyUsage_d eKeyUsage; +}sKeyPairOption_d; + +/** + * \brief Structure to specify parameters for signature generation. + */ +typedef struct sCalcSignOptions_d +{ + ///Signing scheme + eSignScheme_d eSignScheme; + + ///OID of the signature key + uint16_t wOIDSignKey; + + ///Digest to be signed + sbBlob_d sDigestToSign; +}sCalcSignOptions_d; + +/** + * \brief Enumeration to specify supported key agreement primitives + */ +typedef enum eKeyAgreementType_d +{ + ///Elliptic Curve Diffie-Hellman shared secret agreement according to NIST SP-800 56A + eECDH_NISTSP80056A = 0x01 +}eKeyAgreementType_d; + +/** + * \brief Structure to specify parameters for shared secret calculation. + */ +typedef struct sCalcSSecOptions_d +{ + ///Key agreement algorithm + eKeyAgreementType_d eKeyAgreementType; + + ///Algorithm identifier of the public key + eAlgId_d ePubKeyAlgId; + + ///OID of the private key/session context for shared secret calculation + uint16_t wOIDPrivKey; + + ///OID of the session context to store the calculated secret + uint16_t wOIDSharedSecret; + + ///Public key for shared secret calculation + sbBlob_d sPubKey; +}sCalcSSecOptions_d; + +/** + * \brief Enumeration to specify supported key derivation method + */ +typedef enum eKeyDerivationMethod_d +{ + ///IETF 5246 TLS PRF SHA256 + eTLS_PRF_SHA256 = 0x01 +}eKeyDerivationMethod_d; + +/** + * \brief Structure to specify parameters for derive key generation. + */ +typedef struct sDeriveKeyOptions_d +{ + ///Key derivation method + eKeyDerivationMethod_d eKDM; + + ///OID of Shared Secret to derive the new secret from + uint16_t wOIDSharedSecret; + + ///The length of the key to be derived + uint16_t wDerivedKeyLen; + + ///OID to store the Derived key + uint16_t wOIDDerivedKey; + + ///Seed + sbBlob_d sSeed; +}sDeriveKeyOptions_d; + +#ifdef MODULE_ENABLE_TOOLBOX +/** + * \brief Calculates the hash on input data by issuing CalcHash command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_CalcHash(sCalcHash_d* PpsCalcHash); + +/** + * \brief Verify the signature on digest by issuing VerifySign command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_VerifySign(const sVerifyOption_d* PpsVerifySign,const sbBlob_d * PpsDigest,const sbBlob_d * PpsSignature); + +/** + * \brief Generate a key pair by issuing GenKeyPair command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_GenerateKeyPair(const sKeyPairOption_d* PpsKeyPairOption,sOutKeyPair_d* PpsOutKeyPair); + +/** + * \brief Calculate signature on a digest by issuing CalcSign command to the Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_CalculateSign(const sCalcSignOptions_d *PpsCalcSign,sbBlob_d *PpsSignature); + +/** + * \brief Calculate shared secret by issuing CalcSSec command to the Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_CalculateSharedSecret(const sCalcSSecOptions_d *PpsCalcSSec,sbBlob_d *PpsSecret); + +/** + * \brief Derive session key by issuing DeriveKey command to the Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_DeriveKey(const sDeriveKeyOptions_d *PpsDeriveKey,sbBlob_d *PpsKey); +#endif/*MODULE_ENABLE_TOOLBOX*/ + +/**************************************************************************** + * + * Definitions related to (D)TLS commands. + * + ****************************************************************************/ + +///Length of APDU header +#define LEN_APDUHEADER 0x04 + +///Number of bytes in Session ID field +#define BYTES_SESSIONID 0x02 + +///bits per byte +#define BITS_PER_BYTE 0x08 + +///Tag Encoding length +#define LEN_TAG_ENCODING 0x03 + +///Additional bytes for Encryption/Decryption +#define ADDITIONALBYTES_ENCDEC 10 + +///Overhead for all uplink/downlink messages +#define OVERHEAD_UPDOWNLINK (LEN_APDUHEADER + BYTES_SESSIONID + LEN_TAG_ENCODING + ADDITIONALBYTES_ENCDEC) + +//Overhead for Encrypt/Decrypt response +#define OVERHEAD_ENCDEC_RESPONSE (LEN_APDUHEADER + LEN_TAG_ENCODING) +/** + * \brief Enumeration to specify the type of message. + */ +typedef enum eMsgType_d +{ + //Client Side Message type + ///Client Hello + eClientHello = 0x01, + ///Client Hello with Cookie + eClientHelloWithCookie = 0x03, + ///Client Certificate + eClientCertificate = 0x0B, + ///Client Key Exchange + eClientKeyExchange = 0x10, + ///Certificate Verify + eCertificateVerify = 0x0F, + ///Client Finished + eClientFinished = 0x14, + //Server Side Message type + ///Hello Verify Request + eHelloVerifyRequest = 0x03, + ///Server Hello + eServerHello = 0x02, + ///Server Certificate + eServerCertificate = 0x0B, + ///Server Key Exchange + eServerKeyExchange = 0x0C, + ///Certificate Request + eCertificateRequest = 0x0D, + ///Server Hello Done + eServerHelloDone = 0x0E, + ///Server Finished + eServerFinished = 0x14, + ///Change cipher spec + eChangeCipherSpec = 0x13, + ///Close session + eCloseSession = 0x71 +}eMsgType_d; + +/** + * \brief Union to specify message specific parameters + */ +typedef union uMsgParams_d +{ + ///Client Hello Params + struct sMsgParamCH_d + { + ///Unix Time + uint32_t dwUnixTime; + }sMsgParamCH_d; + + ///Certificate Params + struct sMsgParamCert_d + { + ///Certificate OID + uint16_t wCertOID; + }sMsgParamCert_d; + +}uMsgParams_d; + +///Callback to Caller to accept the message +typedef int32_t (*fAcceptMessage)(Void*,const sbBlob_d*); + +/** + * \brief Structure to specify Call back parameters for Get Message. + */ +typedef struct sCallBack_d +{ + ///Callback to Caller to accept the message + fAcceptMessage pfAcceptMessage; + ///Params for Call back + Void* fvParams; +}sCallBack_d; + +/** + * \brief Structure to specify parameters for (D)TLS handshake messages. + */ +typedef struct sProcMsgData_d +{ + ///Pointer to Input Buffer memory + sbBlob_d* psBlobInBuffer; + + ///Type of Message + eMsgType_d eParam; + + ///Session Key OID + uint16_t wSessionKeyOID; + + ///Message related parameters + uMsgParams_d* puMsgParams; + + ///Pointer to a structure containing Callback function and parameter pointer to Caller to accept the message + sCallBack_d *psCallBack; + +}sProcMsgData_d; + + +/** + * \brief Structure to specify ProcUpLink/ProcDownLink command parameters. + */ +typedef struct sProcCryptoData_d +{ + ///Contains data to be encrypted/decrypted.This should contain enough memory to accommodate APDU header, data formatting and the data for encryption/decryption + sbBlob_d sInData; + + ///Actual length of data to be encrypted/decrypted + uint16_t wInDataLength; + + ///Session Key OID + uint16_t wSessionKeyOID; + + ///Holds the output data after encryption/decryption + sCmdResponse_d sOutData; + +}sProcCryptoData_d; + + +#ifdef MODULE_ENABLE_DTLS_MUTUAL_AUTH +/** + * \brief Generates Uplink message by issuing ProcUpLink command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_GetMessage(const sProcMsgData_d *PpsGMsgVector); + +/** + * \brief Process Authentication message by issuing ProcDownLink command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_PutMessage(const sProcMsgData_d *PpsPMsgVector); + +/** + * \brief Encrypts data by issuing ProcUpLink command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_Encrypt(sProcCryptoData_d *PpsEncVector); + +/** + * \brief Decrypts data by issuing ProcDownLink command to Security Chip. + */ +LIBRARY_EXPORTS int32_t CmdLib_Decrypt(sProcCryptoData_d *PpsDecVector); + +/** + * \brief Closes the Security Chip session as indicated by the Session Reference Id. + */ +LIBRARY_EXPORTS int32_t CmdLib_CloseSession(uint16_t PwSessionRefId); +#endif /* MODULE_ENABLE_DTLS_MUTUAL_AUTH*/ +#endif //_CMD_LIB_H_ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/common/AuthLibSettings.h b/tools/sdk/include/trustx/optiga/common/AuthLibSettings.h new file mode 100644 index 00000000000..019ccd22835 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/AuthLibSettings.h @@ -0,0 +1,91 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file is defines the compilation switches to build code with required features. +* +* \ingroup grMutualAuth +* @{ +*/ + + +#ifndef _AUTH_LIB_SETTINGS_H_ +#define _AUTH_LIB_SETTINGS_H_ +/* +To enable a user in choosing required feature set, compilation switches are provided to build the code. +1.FEATURE_ONEWAY_AUTH : One Way authentication feature is enabled. +2.FEATURE_DTLS_MUTUAL_AUTH : Mutual authentication feature is enabled. +3.FEATURE_TOOLBOX : Tool box feature is enabled +4.By default all features are enabled. + +A user can define more switches in the following way and add it before the default #else directive + +//Sample definition for new compile time switch +#ifdef NEW_REQUIRED_FEATURES + #define MODULE_X + #define MODULE_Y + #define MODULE_Z +#endif +*/ + +//If only One Way Authentication feature required +#ifdef FEATURE_ONEWAY_AUTH + #define MODULE_ENABLE_READ_WRITE + #define MODULE_ENABLE_ONE_WAY_AUTH + #define FEATURE_ENABLED +#endif + +//If only Mutual Authentication feature required +#ifdef FEATURE_DTLS_MUTUAL_AUTH + #define MODULE_ENABLE_READ_WRITE + #define MODULE_ENABLE_DTLS_MUTUAL_AUTH + #define FEATURE_ENABLED +#endif + +#ifdef FEATURE_TOOLBOX + #define MODULE_ENABLE_TOOLBOX + #define FEATURE_ENABLED +#endif + +//By Default all are enabled +#ifndef FEATURE_ENABLED + #define MODULE_ENABLE_READ_WRITE +// #define MODULE_ENABLE_ONE_WAY_AUTH +// #define MODULE_ENABLE_DTLS_MUTUAL_AUTH + #define MODULE_ENABLE_TOOLBOX +#endif + +#ifdef OPTIMIZE_CODE + #define OPTIMIZE_CODE(x) #ifndef x + #define OPTIMIZE_CODE_ENDS(x) #endif +#else + #define OPTIMIZE_CODE(x) + #define OPTIMIZE_CODE_ENDS(x) +#endif +#endif /* _AUTH_LIB_SETTINGS_H_*/ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/common/Datatypes.h b/tools/sdk/include/trustx/optiga/common/Datatypes.h new file mode 100644 index 00000000000..7799f76ff30 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/Datatypes.h @@ -0,0 +1,140 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* +* \file +* +* \brief This file contains the type definitions for the fundamental data types. +* +* +* +*/ + +#ifndef _DATATYPES_H_ +#define _DATATYPES_H_ + +/****************************************************************************** +* required includes +* Setup common include order for the used types and definitions +******************************************************************************/ +#include +#include +#include + +/****************************************************************************** +* DataTypes.h +******************************************************************************/ + +/****************************************************************************** +* defines +******************************************************************************/ + +/// @cond hidden +#ifndef _OPTIGA_EXPORTS_DLLEXPORT_H_ +#define _OPTIGA_EXPORTS_DLLEXPORT_H_ + +#if defined(WIN32) || defined(_WIN32) + +#ifdef OPTIGA_LIB_EXPORTS +#define LIBRARY_EXPORTS __declspec(dllexport) +#elif defined(OPTIGA_LIB_EXCLUDE_IMPORT) +#define LIBRARY_EXPORTS +#else +#define LIBRARY_EXPORTS __declspec(dllimport) +#endif // OPTIGA_LIB_EXPORTS + +#else +#define LIBRARY_EXPORTS +#endif //WIN32 + +#endif /*_OPTIGA_EXPORTS_DLLEXPORT_H_*/ +/// @endcond + +/// Definition for false +#ifndef FALSE +#define FALSE 0 +#endif + +/// Definition for true +#ifndef TRUE +#define TRUE 1 +#endif + +/****************************************************************************** +* fundamental typedefs +******************************************************************************/ + +/// Typedef for one byte integer +typedef char char_t; + +/// Typedef for native byte pointer +typedef uint8_t* puint8_t; + +/// Typedef for a 4 byte unsigned integer pointer +typedef uint32_t* puint32_t; + +/// Typedef for unsigned word pointer +typedef uint16_t* puint16_t ; + +/// Typedef for a void +typedef void Void; + +/// Typedef for a double word +typedef double double_t; + +/// Typedef for a float +typedef float float_t; + +/// Typedef for a boolean +typedef uint8_t bool_t; + +/// Typedef for Handle +typedef Void* hdl_t; + +/// typedef for host library status +typedef uint16_t host_lib_status_t; + +/// typedef for OPTIGA host library status (will be updated to different data type in the future to keep in sync with other modules) +typedef int32_t optiga_lib_status_t; + +/** + * \brief Structure to specify a byte stream consisting of length and data + * pointer. + */ +typedef struct sbBlob_d +{ + /// Length of the byte stream + uint16_t wLen; + + /// Pointer to byte array which contains the data stream + uint8_t *prgbStream; +} sbBlob_d; + +/// typedef for application event handler +typedef void (*app_event_handler_t)(void* upper_layer_ctx, host_lib_status_t event); + +#ifndef _STATIC_H +#define _STATIC_H static +#endif +#endif /* __DATATYPES_H__ */ diff --git a/tools/sdk/include/trustx/optiga/common/ErrorCodes.h b/tools/sdk/include/trustx/optiga/common/ErrorCodes.h new file mode 100644 index 00000000000..e32c421b4df --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/ErrorCodes.h @@ -0,0 +1,153 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file defines the error codes. +* +* +* +*/ + +#ifndef _ERRORCODES_H_ +#define _ERRORCODES_H_ + +/****************************************************************************** +* required includes +* Setup common include order for the used types and definitions +******************************************************************************/ + +/****************************************************************************** +* ErrorCodes.h +******************************************************************************/ + +/****************************************************************************** +* defines +******************************************************************************/ + +/// Communication success +#define E_COMMS_SUCCESS 0x00000000 + +/// Error code if protocol exit timeout has occurred +#define E_COMMS_TIMEOUT_OCCURRED 0x80000003 + +/// Error code if soft reset is not supported by slave +#define E_COMMS_SOFT_RESET_NOT_SUPPORTED 0x80000005 + +/// Error code if network layer channel does not mismatch with that of the slave +#define E_COMMS_CHANNEL_NUM_MISMATCH 0x80000006 + +/// Error code if packet size could not be set +#define E_COMMS_PACKET_SIZE_NOT_SET 0x80000007 + +/// Error code for invalid key +#define E_COMMS_INVALID_KEY 0x80000008 + +/// Error code if the input parameter is NULL +#define E_COMMS_PARAMETER_NULL 0x8000000A + +/// Error code for uninitialized protocol +#define E_COMMS_PROTOCOL_NOT_INITIALIZED 0x8000000B + +/// Error code for wrong reset value +#define E_INVALID_RESET_VALUE 0x8000000C + +///Error code for invalid value for a given key +#define E_INVALID_VALUE_FOR_KEY 0x8000000D + +/// Error code for if the slave doesn't have any data ready +#define E_COMMS_SLAVE_NODATA 0x8000000E + +/// Error code if no channel information is present +#define E_COMMS_NO_CHANNEL_INFORMATION 0x8000000F + +/// Error code if APDU is not present +#define E_COMMS_NO_APDU_PRESENT 0x80000010 + +/// Error code for cold reset not supported +#define E_COMMS_COLD_RESET_NOT_SUPPORTED 0x80000011 + +/// Error code for partial frame send +#define E_COMMS_PARTIAL_FRAME_SENT 0x80000012 + +/// Error code for low level I2C write failed +#define E_COMMS_I2C_WRITE_FAILED 0x80000013 + +/// Error code for low level I2C read failed +#define E_COMMS_I2C_READ_FAILED 0x80000014 + +/// Error code for buffer size or memory insufficient +#define E_COMMS_INSUFFICIENT_BUF_SIZE 0x80000015 + +/// Error code when the value of key is not set +#define E_COMMS_KEY_VALUE_NOT_SET 0x80000016 + +/// Error code for setting a property within a session not allowed +#define E_COMMS_SET_PROPERTY_DENIED 0x80000017 + +/// Error code for opening the already opened protocol stack +#define E_COMMS_ALREADY_OPEN 0x80000018 + +/// Error code for frame buffer overflow(at data link layer) +#define E_COMMS_FRAME_BUFFER_OVERFLOW 0x80000019 + +/// Error code for unsupported functionality +#define E_COMMS_FUNCTION_NOT_SUPPORTED 0x8000001A + +/// Error code for invalid channel number +#define E_COMMS_INVALID_CHANNEL_NO 0x8000001B + +/// Error code for insufficient memory +#define E_COMMS_INSUFFICIENT_MEMORY 0x8000001C + +/// Error code if Nack is received +#define E_COMMS_I2C_NACK_RECEIVED 0x8000001D + +/// Error code for I2C Tx/Rx Error +#define E_COMMS_I2C_TXRX_ERROR 0x8000001E + +/// Error code for I2C Tx/Rx Error +#define E_COMMS_I2C_TIMEOUT 0x8000001F + +/// Error code for I2C Tx/Rx Error +#define E_COMMS_INVALID_REQUEST 0x80000020 + +/// Error code for communication failure +#define E_COMMS_FAILURE 0xFFFFFFFF + +/// Indicates no session available at OPTIGA +#define OPTIGA_LIB_NO_SESSION_AVAILABLE 0x0000 + +/// OPTIGA Host Library API status - Success +#define OPTIGA_LIB_SUCCESS 0x0000 + +/// OPTIGA Host Library API status - Busy +#define OPTIGA_LIB_STATUS_BUSY 0x0001 + +/// OPTIGA Host Library API status - Failure +#define OPTIGA_LIB_ERROR 0xFFFF + +#endif /* _ERRORCODES_H_ */ + + diff --git a/tools/sdk/include/trustx/optiga/common/Logger.h b/tools/sdk/include/trustx/optiga/common/Logger.h new file mode 100644 index 00000000000..02d6201065d --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/Logger.h @@ -0,0 +1,362 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* \file +* +* \brief This file contains a light weight logger. +* +* +* \ingroup grLogger +* @{ +* +*/ +#ifndef __LOGGER_H__ +#define __LOGGER_H__ + +#include "optiga/common/Util.h" +#ifdef WIN32 +#include +#endif + +/// @cond hidden +/***************************************************************************** +* Defines +*****************************************************************************/ +/// Flag for transmit direction +#define TX_DIRECTION 0 +/// Flag for reception direction +#define RX_DIRECTION 1 +/// Flag to write to console UART +#define CONSOLE_PORT 0 +/// Flag to write to cuint UART +#define CUNIT_PORT 1 +/// Flag to write to code coverage UART +#define CODE_COVERAGE_PORT 2 + + +//Pointer to writer +typedef int32_t (*pFWriteData)(uint32_t PdwPort, uint8_t* PprgbBuf, uint32_t PdwDataLen); +///This is pointer to log writer +extern pFWriteData pfWriter; + +///This function converts Uint8 value to Hex String +void ConvUint8ToHexString (uint8_t* PprgbHexByteArray, uint8_t* PprgbHexString, \ + uint32_t dwNoOfBytes, uint8_t PbIsSpaceReq); + +///This function converts Uint32 to Hex String +void ConvUint32ToHexString (uint32_t dwVal, uint8_t* PprgbHexString); + + +///This function converts Uint32 value to Decimal String +void ConvUint32ToDecString (uint32_t dwVal, uint8_t* PprgbDecStr, \ + uint8_t bExpStrLen, uint8_t bFillChar); + +/***************************************************************************** +* Exposed Logging APIs +*****************************************************************************/ +/// Logs a new line +void Util_NewLine(uint32_t PdwUartPort); + +/// Logs a string with newline at the end +void Util_LogStringLine(uint32_t PdwUartPort, const char_t *pszString); + +/// Logs a string +void Util_LogString(uint32_t PdwUartPort, const char_t *pszString); + +//Logs Integer +void Util_LogInt(uint32_t PdwUartPort, const char_t *pszMsg, uint32_t dwValue); + +/// Logs a byte array +void Util_LogArray(uint8_t* prgbBuf, uint16_t wLen, bool_t fDirection, bool_t fDumpPacketAnalysis); + +/// Print an array in hex format +void Util_DumpHex(uint8_t* prgbBuf, uint16_t wLen); + +//Print on console port with new line +#define CONSOLE_LOGSTRINGLINE(pszString) Util_LogStringLine(CONSOLE_PORT,pszString); + +//Print on console port +#define CONSOLE_LOGSTRING(pszString) Util_LogString(CONSOLE_PORT,pszString); + +//Print on console port +#define CONSOLE_LOGINTEGER(pszString,value) Util_LogInt(CONSOLE_PORT,pszString,value); + +//Print on Cunit port +#define CUNIT_LOGSTRING(pszString) Util_LogString(CUNIT_PORT,pszString); + +//Print on coverage port +#define COVERAGE_LOGSTRING(pszString,dwLen) pfWriter(CODE_COVERAGE_PORT,pszString,dwLen); + + +/***************************************************************************** +* Level based logging Exposed APIs +*****************************************************************************/ +/// @endcond +/** + * \brief Structure to Logger. + */ +typedef struct sLogger_d +{ + ///Handle to instance of writer + uint32_t* pHdl; + ///Log Writer + hdl_t phfWriter;//This to be replaced by actual type +}sLogger_d; + +#ifdef ENABLE_LOG +//This is second write +typedef int32_t (*pFWriteData2)(void* Hdl, uint8_t* PprgbBuf, uint32_t PdwDataLen); + +//This is the arguement to be passed to pfWriter.It refers to the handle to writer context/structure. +extern void* pHandle; + +//This is second log writer +extern pFWriteData2 pfWriter2; + +///Delimiter to indicate "Seperator Open" +#define DELIMITER_OPEN "[" +///Delimiter to indicate "Seperator Close" +#define DELIMITER_CLOSE "]" +///Format to print time as [hh:mm:ss][level][type] :message newline +#define LOG_FORMAT "[%s]%-4s%-2s:%s%s" + +///Log message Level +///This indicates message is an Information +#define LOGGER_LEVEL_INFO DELIMITER_OPEN"Info"DELIMITER_CLOSE +///This indicates message is a Warning +#define LOGGER_LEVEL_WARNING DELIMITER_OPEN"Warn"DELIMITER_CLOSE +///This indicates message is an Error +#define LOGGER_LEVEL_ERROR DELIMITER_OPEN"Erro"DELIMITER_CLOSE + +///Log message Type +///This indicates message is logged from Handshake Layer +#define LOGGER_TYPE_HANDSHAKE DELIMITER_OPEN"HS"DELIMITER_CLOSE +///This indicates message is logged from Record Layer +#define LOGGER_TYPE_RECORDLAYER DELIMITER_OPEN"RL"DELIMITER_CLOSE +///This indicates message is logged from Transport Layer +#define LOGGER_TYPE_TRANSPORTLAYER DELIMITER_OPEN"TL"DELIMITER_CLOSE + + + +/** +* \brief This structure represents Log Level Types +*/ +typedef enum eLogLevel { + ///Information + eInfo = 1, + ///Warning + eWarning = 2, + ///Error + eError = 3, +}eLogLevel; + +/** +* \brief This structure represents Log Layer Types +*/ +typedef enum eLogLayer { + ///Handshake + eHS = 1, + ///Record + eRL = 2, + ///Transport + eTL = 3, +}eLogLayer; + + +/** +* \brief This structure contains state of logging +*/ +typedef enum eSetState { + ///Enable Logging + eEnable = 0x01, + ///Disable Logging + eDisable = 0x02, + //Invalid + eInvalid = 0xFF +}eSetState; + +/** + * \brief Returns the current state of Logging level. + */ +eSetState Util_GetLogLevelState(eLogLevel level); +/** + * \brief Sets the state of the Logging Level. + */ +void Util_SetLogLevelState(eLogLevel type,eSetState value); +/** + * \brief Returns the current state of the Logging Layer. + */ +eSetState Util_GetLogLayerState(eLogLayer layer); +/** + * \brief Sets the state of the Logging Layer. + */ +void Util_SetLogLayerState(eLogLayer layer,eSetState value); +/** + * \brief Sets the Log Writer and handle. + */ +void Util_SetLogWriter(pFWriteData2 writer,void* pHdl); +/** + * \brief Logs a message with type and level information. + */ +void Util_LogMessage(char* message, eLogLayer layer, eLogLevel level); +/** + * \brief Logs a 4 byte debug value with type and level information. + */ +void Util_LogDebugVal(uint32_t dwDBValue, eLogLayer layer, eLogLevel level); +/** + * \brief Logs a message with type,level information and also the content of the buffer. + */ +void Util_LogMsgArray(char* pzMsg, uint8_t* PrgbBuffer, uint16_t wLen, eLogLayer eLayer, eLogLevel eLevel); +#endif + +///Define ENABLE_LOG to enable logging +#ifdef ENABLE_LOG +///Disable Layer during runtime +#define DISABLE_LOGGING_LAYER(layer) Util_SetLogLayerState(layer,eDisable) +///Enable Layer during runtime +#define ENABLE_LOGGING_LAYER(layer) Util_SetLogLayerState(layer,eEnable) +///Get Layer State +#define GET_LOGGING_LAYER(layer) Util_GetLogLayerState(layer) +///Disable Level during runtime +#define DISABLE_LOGGING_LEVEL(level) Util_SetLogLevelState(level,eDisable) +///Enable Level during runtime +#define ENABLE_LOGGING_LEVEL(level) Util_SetLogLevelState(level,eEnable) +///Get Level State +#define GET_LOGGING_LEVEL(level) Util_GetLogLevelState(level) +#else +/// @cond hidden +#define DISABLE_LOGGING_LAYER(layer) +#define ENABLE_LOGGING_LAYER(layer) +#define GET_LOGGING_LAYER(layer) +#define DISABLE_LOGGING_LEVEL(level) +#define ENABLE_LOGGING_LEVEL(level) +#define GET_LOGGING_LEVEL(level) +/// @endcond +#endif + +/// @cond hidden +#ifdef _OCPDEBUG_WIN32 + #define DEBUG_PRINT(msg) printf("%s",msg); +#else + #define DEBUG_PRINT(msg) +#endif +/// @endcond + + +///Define ENABLE_LOG and ENABLE_HANDSHAKETYPE to enable Handshake layer logging. +///ENABLE_LOG enable logs at top level and logging will not wok if not defined. +#if !defined ENABLE_HANDSHAKETYPE || !defined ENABLE_LOG +/// @cond hidden +#define LOG_HANDSHAKEMSG(msg,level) DEBUG_PRINT("HANDSHAKETYPE Undefined\n") +#define LOG_HANDSHAKEDBVAL(val,level) DEBUG_PRINT("HANDSHAKETYPE Undefined\n") +/// @endcond +#else +///Log Handshake Message +#define LOG_HANDSHAKEMSG(msg,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eHS)) \ + Util_LogMessage(msg,eHS,level); \ + else{DEBUG_PRINT("Handshake Layer Disabled\n");} \ + } + +///Logs Handshake layer 4 byte debug value +#define LOG_HANDSHAKEDBVAL(val,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eHS)) \ + Util_LogDebugVal(val,eHS,level); \ + else{DEBUG_PRINT("Handshake Layer Disabled\n");} \ + } +#endif + +///Define ENABLE_LOG and ENABLE_RECORDLAYERTYPE to enable Record layer logging. +///ENABLE_LOG enable logs at top level and logging will not wok if not defined. +#if !defined ENABLE_RECORDLAYERTYPE || !defined ENABLE_LOG +/// @cond hidden +#define LOG_RECORDLAYERMSG(msg,level) DEBUG_PRINT("RECORDLAYERTYPE Undefined\n") +#define LOG_RECORDLAYERDBVAL(val,level) DEBUG_PRINT("RECORDLAYERTYPE Undefined\n") +/// @endcond +#else +///Log Record Header Message +#define LOG_RECORDLAYERMSG(msg,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eRL)) \ + Util_LogMessage(msg,eRL,level); \ + else{DEBUG_PRINT("Record Layer Disabled\n");} \ + } + +///Logs record layer 4 byte debug value +#define LOG_RECORDLAYERDBVAL(val,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eRL)) \ + Util_LogDebugVal(val,eRL,level); \ + else{DEBUG_PRINT("Record Layer Disabled\n");} \ + } +#endif + +///Define ENABLE_LOG and ENABLE_TRANSPORTTYPE to enable Transport Layer logging. +///ENABLE_LOG enable logs at top level and logging will not wok if not defined. +#if !defined ENABLE_TRANSPORTTYPE || !defined ENABLE_LOG +/// @cond hidden +#define LOG_TRANSPORTMSG(msg,level) DEBUG_PRINT("TRANSPORTTYPE Undefined\n") +#define LOG_TRANSPORTDBVAL(val,level) DEBUG_PRINT("TRANSPORTTYPE Undefined\n") +#define LOG_TRANSPORTDBARY(Msg, buffer, wLen, level) DEBUG_PRINT("TRANSPORTTYPE Undefined\n") +/// @endcond +#else +///Log Transport layer Message +#define LOG_TRANSPORTMSG(msg,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eTL)) \ + Util_LogMessage(msg,eTL,level); \ + else{DEBUG_PRINT("Transport Layer Disabled\n");} \ + } + +///Logs Transport layer 4 byte debug value +#define LOG_TRANSPORTDBVAL(val,level) \ + { \ + if(eEnable==Util_GetLogLayerState(eTL)) \ + Util_LogDebugVal(val,eTL,level); \ + else{DEBUG_PRINT("Transport Layer Disabled\n");} \ + } + +///Logs Transport layer Array value +#define LOG_TRANSPORTDBARY(Msg, buffer, wLen, level) \ + { \ + if(eEnable==Util_GetLogLayerState(eTL)) \ + Util_LogMsgArray(Msg, buffer, wLen, eTL, level); \ + else{DEBUG_PRINT("Transport Layer Disabled\n");} \ + } +#endif + +#ifdef ENABLE_LOG +///Enable the logger handle +#define LOG_SETWRITER(writer,hdl) Util_SetLogWriter((pFWriteData2)writer,hdl); +#else +/// @cond hidden +#define LOG_SETWRITER(writer,hdl) DEBUG_PRINT("Logger is Disabled\n") +/// @endcond +#endif + +#endif //__LOGGER_H__ + /** +* @} +*/ \ No newline at end of file diff --git a/tools/sdk/include/trustx/optiga/common/MemoryMgmt.h b/tools/sdk/include/trustx/optiga/common/MemoryMgmt.h new file mode 100644 index 00000000000..14b06b1e1b7 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/MemoryMgmt.h @@ -0,0 +1,53 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file defines the memory management related macros. +* +* +* \ingroup grMutualAuth +* +*/ + +#ifndef _MEMMGMT_H_ +#define _MEMMGMT_H_ + +///Malloc function to allocate the heap memory +#define OCP_MALLOC(size) malloc(size) + +///Malloc function to allocate the heap memory +#define OCP_CALLOC(block,blocksize) calloc(block,blocksize) + +///To free the allocated memory +#define OCP_FREE(node) free(node) + +///To copy the data from source to destination +#define OCP_MEMCPY(dst,src,size) memcpy(dst,src,size) + +///To copy the data from source to destination +#define OCP_MEMSET(src,val,size) memset(src,val,size) + +#endif /* _MEMMGMT_H_ */ + diff --git a/tools/sdk/include/trustx/optiga/common/Util.h b/tools/sdk/include/trustx/optiga/common/Util.h new file mode 100644 index 00000000000..7f1945b8cab --- /dev/null +++ b/tools/sdk/include/trustx/optiga/common/Util.h @@ -0,0 +1,153 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file contains utility functions +* +* +* +*/ +#ifndef _UTIL_H_ +#define _UTIL_H_ + + +#include "optiga/common/Datatypes.h" + + + +///Greater than +#define GREATER_THAN 0x00000001 + +///Lesser than +#define LESSER_THAN 0x00000002 + +///Equal +#define EQUAL 0x00000000 + +/// Default Low bound value value +#define DEFAULT_LOWBOUND_DOUBLEWORD 0x00000000 + +/// Mask double word value +#define MASK_DOUBLE_WORD 0xFFFFFFFF + +///Word bit size +#define WORD_SIZE 32 + +///Successful Execution +#define UTIL_SUCCESS 0x00000000 + +///Execution Failure +#define UTIL_ERROR 0xFFFFFFFF + +///Least significant bit set to high +#define LEAST_SIGNIFICANT_BIT_HIGH 0x00000001 + +///Least significant bit set to high +#define MOST_SIGNIFICANT_BIT_HIGH 0x80000000 + +/** + * \brief structure to store the record sequence number + */ +typedef struct sUint64 +{ + ///Higher 32 bit of the record sequence number + uint32_t dwHigherByte; + + ///Lower 32 bit of the record sequence number + uint32_t dwLowerByte; +}sUint64; + +/** + * \brief The function compares two uint64 data type.
+ */ +int32_t CompareUint64(const sUint64 *PpsSrc1, const sUint64 *PpsSrc2); + +/** + * \brief Calculates the difference between the two uint64 data.
+ */ +int32_t SubtractUint64(const sUint64 *PpsMinuend, const sUint64 *PpsSubtrahend,sUint64 *PpsDifference); + +/** + * \brief Adds two uint64 data.
+ */ +int32_t AddUint64(const sUint64 *PpsSrc1, const sUint64 *PpsSrc2,sUint64 *PpsDest); + +/** + * \brief Increments uint64 data.
+ */ +int32_t IncrementUint64(sUint64 *PpsSrc1); + +/** + * \brief Converts byte array to uint64.
+ */ +int32_t GetUint64(sUint64 *PpsOut,const uint8_t* pbVal,uint16_t wLen); + +/** + * \brief To Slide the window by shift count.
+ */ +int32_t ShiftLeftUint64(sUint64 *PpsWindow, sUint64 PsShiftCount, uint8_t PWindowSize, uint8_t PbMaxWindowSize); + +/** + * \brief To set a bit in the sUnit64 data.
+ */ +int32_t Utility_SetBitUint64(sUint64* PprgbData, uint8_t bWindowSize, uint8_t bBitPosition); + +/** + * \brief Prepares uint16 [Big endian] type value from the buffer.
+ */ +uint16_t Utility_GetUint16 (const uint8_t* PprgbData); + +/** + * \brief Prepares uint32 [Big endian] type value from the buffer .
+ */ +uint32_t Utility_GetUint32 (const uint8_t* PprgbData); + +/** + * \brief Copies 2 bytes of uint16 type value to the buffer
+ */ +void Utility_SetUint16 (puint8_t PprgbData,uint16_t PwValue); + +/** + * \brief Prepares uint24 [Big endian] type value from the buffer .
+ */ +uint32_t Utility_GetUint24 (const uint8_t* PprgbData); + +/** + * \brief Copies LSB 3 bytes of uint32 [Big endian] type value to the buffer and store.
+ */ +void Utility_SetUint24 (uint8_t* PprgbData,uint32_t Pdwvalue); + +/** + * \brief Copies LSB 4 bytes of uint32 [Big endian] type value to the buffer and store.
+ */ +void Utility_SetUint32 (uint8_t* PprgbData,uint32_t Pdwvalue); + +/** + * \brief Copies the data from source buffer to destination buffer.
+ */ +void Utility_Memmove(puint8_t PprgbDestBuf, const puint8_t PprgbSrcBuf, uint16_t PwLength); + +#endif //_UTIL_H_ + diff --git a/tools/sdk/include/trustx/optiga/comms/optiga_comms.h b/tools/sdk/include/trustx/optiga/comms/optiga_comms.h new file mode 100644 index 00000000000..2738c644483 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/comms/optiga_comms.h @@ -0,0 +1,104 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements optiga comms abstraction layer for IFX I2C Protocol. +* +* \ingroup grOptigaComms +* @{ +*/ + +#ifndef _OPTIGA_COMMS_H_ +#define _OPTIGA_COMMS_H_ +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ +#include "optiga/common/Datatypes.h" + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + +/// Succesfull execution +#define OPTIGA_COMMS_SUCCESS 0x0000 +/// Error in execution +#define OPTIGA_COMMS_ERROR 0x0001 +/// Busy, doing operation +#define OPTIGA_COMMS_BUSY 0x0002 + +/********************************************************************************************************************** + * DATA STRUCTURES + *********************************************************************************************************************/ + +/** @brief optiga comms structure */ +typedef struct optiga_comms +{ + /// Comms structure pointer + void* comms_ctx; + /// Upper layer contect + void* upper_layer_ctx; + /// Upper layer handler + app_event_handler_t upper_layer_handler; + /// Optiga comms state + uint8_t state; +}optiga_comms_t; + +extern optiga_comms_t optiga_comms; + +/********************************************************************************************************************** + * API Prototypes + *********************************************************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Opens the communication channel with OPTIGA. + */ +LIBRARY_EXPORTS host_lib_status_t optiga_comms_open(optiga_comms_t *p_ctx); + +/** + * \brief Resets the OPTIGA. + */ +LIBRARY_EXPORTS host_lib_status_t optiga_comms_reset(optiga_comms_t *p_ctx,uint8_t reset_type); + +/** + * \brief Sends and receives the APDU. + */ +LIBRARY_EXPORTS host_lib_status_t optiga_comms_transceive(optiga_comms_t *p_ctx,const uint8_t* p_data, + const uint16_t* p_data_length, + uint8_t* p_buffer, uint16_t* p_buffer_len); + +/** + * \brief Closes the communication channel with OPTIGA. + */ +LIBRARY_EXPORTS host_lib_status_t optiga_comms_close(optiga_comms_t *p_ctx); + +/** +* @} +*/ + +#endif /*_OPTIGA_COMMS_H_*/ + diff --git a/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c.h b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c.h new file mode 100644 index 00000000000..0c65de9b56b --- /dev/null +++ b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c.h @@ -0,0 +1,104 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file ifx_i2c.h +* +* \brief This file defines the API prototype for IFX I2C protocol v1.65 wrapper. +* +* \ingroup grIFXI2C +* @{ +*/ + +#ifndef _IFXI2C_H_ +#define _IFXI2C_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************************************************************************** + * HEADER FILES + **********************************************************************************************************************/ +#include "optiga/common/Datatypes.h" +#include "optiga/ifx_i2c/ifx_i2c_config.h" +/*********************************************************************************************************************** +* MACROS +***********************************************************************************************************************/ + +/*********************************************************************************************************************** + +* ENUMS +***********************************************************************************************************************/ + +/** @brief IFX I2C Reset types */ +typedef enum ifx_i2c_reset_type +{ + /// Cold reset. Both reset pin and vdd pin are toggled low and then high + IFX_I2C_COLD_RESET = 0U, + /// Soft reset. 0x0000 is written to IFX-I2C Soft reset register + IFX_I2C_SOFT_RESET = 1U, + /// Warm reset. Only reset pin is toggled low and then high + IFX_I2C_WARM_RESET = 2U +} ifx_i2c_reset_type_t; +/*********************************************************************************************************************** +* DATA STRUCTURES +***********************************************************************************************************************/ +/*********************************************************************************************************************** +* API PROTOTYPES +**********************************************************************************************************************/ + +/** + * \brief Initializes the IFX I2C protocol stack for a given context. + */ +host_lib_status_t ifx_i2c_open(ifx_i2c_context_t *p_ctx); + +/** + * \brief Resets the I2C slave. + */ +host_lib_status_t ifx_i2c_reset(ifx_i2c_context_t *p_ctx, ifx_i2c_reset_type_t reset_type); + +/** + * \brief Sends a command and receives a response for the command. + */ +host_lib_status_t ifx_i2c_transceive(ifx_i2c_context_t *p_ctx,const uint8_t* p_data, const uint16_t* p_data_length, + uint8_t* p_buffer, uint16_t* p_buffer_len); + +/** + * \brief Closes the IFX I2C protocol stack for a given context. + */ +host_lib_status_t ifx_i2c_close(ifx_i2c_context_t *p_ctx); + +/** + * \brief Sets the slave address of the target device. + */ +host_lib_status_t ifx_i2c_set_slave_address(ifx_i2c_context_t *p_ctx, uint8_t slave_address, uint8_t persistent); + +#ifdef __cplusplus +} +#endif +#endif /* _IFXI2C_H_ */ +/** + * @} + **/ + diff --git a/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_config.h b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_config.h new file mode 100644 index 00000000000..d5ca7b91548 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_config.h @@ -0,0 +1,310 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file ifx_i2c_config.h +* +* \brief This file defines the structures and macros for the Infineon I2C Protocol. +* +* \ingroup grIFXI2C +* @{ +*/ + +#ifndef _IFX_I2C_CONFIG_H_ +#define _IFX_I2C_CONFIG_H_ +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************************************************************************** +* HEADER FILES +**********************************************************************************************************************/ +// Protocol Stack Includes +#include +#include "optiga/pal/pal_i2c.h" +#include "optiga/pal/pal_gpio.h" +#include "optiga/pal/pal_os_timer.h" + +/*********************************************************************************************************************** +* MACROS +**********************************************************************************************************************/ + +/** @brief I2C slave address of the Infineon device */ +#define IFX_I2C_BASE_ADDR (0x30) + +/** @brief Physical Layer: polling interval in microseconds */ +#define PL_POLLING_INVERVAL_US (1000) +/** @brief Physical layer: maximal attempts */ +#define PL_POLLING_MAX_CNT (200) +/** @brief Physical Layer: data register polling interval in microseconds */ +#define PL_DATA_POLLING_INVERVAL_US (5000) +/** @brief Physical Layer: guard time interval in microseconds */ +#define PL_GUARD_TIME_INTERVAL_US (50) + +/** @brief Data link layer: maximum frame size */ +#define DL_MAX_FRAME_SIZE (300) +/** @brief Data link layer: header size */ +#define DL_HEADER_SIZE (5) +/** @brief Data link layer: maximum number of retries in case of transmission error */ +#define DL_TRANS_REPEAT (3) +/** @brief Data link layer: Trans timeout in milliseconds*/ +#define PL_TRANS_TIMEOUT_MS (10) + +/** @brief Transport layer: Maximum exit timeout in seconds */ +#define TL_MAX_EXIT_TIMEOUT (6) + +/** @brief Reset low time for GPIO pin toggling */ +#define RESET_LOW_TIME_MSEC (2000) +/** @brief Start up time */ +#define STARTUP_TIME_MSEC (12000) + +/** @brief Protocol Stack: Status codes for success */ +#define IFX_I2C_STACK_SUCCESS (0x00) +/** @brief Protocol Stack: Status codes for error */ +#define IFX_I2C_STACK_ERROR (0x01) +/** @brief Protocol Stack: Status codes busy */ +#define IFX_I2C_STACK_BUSY (0x02) +/** @brief Protocol Stack: Memory insufficient */ +#define IFX_I2C_STACK_MEM_ERROR (0x03) +/** @brief Protocol Stack: Fatal error. Used internal to IFX I2C Stack */ +#define IFX_I2C_FATAL_ERROR (0x04) + +/** @brief Offset of Datalink header in tx_frame_buffer */ +#define IFX_I2C_DL_HEADER_OFFSET (0) +/** @brief Offset of Transport header in tx_frame_buffer */ +#define IFX_I2C_TL_HEADER_OFFSET (IFX_I2C_DL_HEADER_OFFSET+3) + +/** @brief Protocol Stack debug switch for physical layer (set to 0 or 1) */ +#define IFX_I2C_LOG_PL 0 +/** @brief Protocol Stack debug switch for data link layer (set to 0 or 1) */ +#define IFX_I2C_LOG_DL 0 +/** @brief Protocol Stack debug switch for transport layer (set to 0 or 1) */ +#define IFX_I2C_LOG_TL 0 + +/** @brief Log ID number for physical layer */ +#define IFX_I2C_LOG_ID_PL 0x00 +/** @brief Log ID number for data link layer */ +#define IFX_I2C_LOG_ID_DL 0x01 +/** @brief Log ID number for transport layer */ +#define IFX_I2C_LOG_ID_TL 0x02 +/** @brief Log ID number for platform abstraction layer */ +#define IFX_I2C_LOG_ID_PAL 0x04 + +/*********************************************************************************************************************** +* ENUMS +***********************************************************************************************************************/ + + +/*********************************************************************************************************************** +* DATA STRUCTURES +***********************************************************************************************************************/ +typedef struct ifx_i2c_context ifx_i2c_context_t; + +/** @brief Event handler function prototype */ +typedef void (*ifx_i2c_event_handler_t)(struct ifx_i2c_context* ctx, host_lib_status_t event, const uint8_t* data, uint16_t data_len); + +/** @brief Physical layer structure */ +typedef struct ifx_i2c_pl +{ + // Physical Layer low level interface variables + + /// Physical layer buffer + uint8_t buffer[DL_MAX_FRAME_SIZE+1]; + /// Tx length + uint16_t buffer_tx_len; + /// Rx length + uint16_t buffer_rx_len; + /// Action on register, read/write + uint8_t register_action; + /// i2c read/i2c write + uint8_t i2c_cmd; + /// Retry counter + uint16_t retry_counter; + + // Physical Layer high level interface variables + + /// Action of frame. Tx/Rx + uint8_t frame_action; + /// Frame state + uint8_t frame_state ; + /// Pointer to data to be sent + uint8_t * p_tx_frame; + /// Length of data to be sent + uint16_t tx_frame_len; + // Upper layer handler + ifx_i2c_event_handler_t upper_layer_event_handler; + + // Physical Layer negotiation/soft reset variables + + /// Negotiation state + uint8_t negotiate_state; + /// Soft reset requested + uint8_t request_soft_reset; +} ifx_i2c_pl_t; + +/** @brief Datalink layer structure */ +typedef struct ifx_i2c_dl +{ + // Data Link layer internal state variables + + /// Datalink layer state + uint8_t state; + /// Tx sequence number + uint8_t tx_seq_nr; + // Rx sequence number + uint8_t rx_seq_nr; + /// Indicate only Rx required + uint8_t action_rx_only; + /// Retransmit counter + uint8_t retransmit_counter; + /// Error occured + uint8_t error; + /// Resynced + uint8_t resynced; + /// Timeout value + uint32_t data_poll_timeout; + /// Transmit buffer size + uint16_t tx_buffer_size; + /// Receive buffer size + uint16_t rx_buffer_size; + /// Pointer to main transmit buffers + uint8_t* p_tx_frame_buffer; + /// Pointer to main receive buffers + uint8_t* p_rx_frame_buffer; + ///Start time of sending frame + uint32_t frame_start_time; + // Upper layer Event handler + ifx_i2c_event_handler_t upper_layer_event_handler; +} ifx_i2c_dl_t; + +/** @brief Transport layer structure */ +typedef struct ifx_i2c_tl +{ + // Transport Layer state and buffer + + /// Transport layer state + uint8_t state; + /// Pointer to packet provided by user + uint8_t* p_actual_packet; + /// Total received data + uint16_t total_recv_length; + /// Actual length of user provided packet + uint16_t actual_packet_length; + /// Offset till which data is sent from p_actual_packet + uint16_t packet_offset; + /// Maximum length of packet at transport layer + uint16_t max_packet_length; + /// Pointer to user provided receive buffer + uint8_t* p_recv_packet_buffer; + /// Length of receive buffer + uint16_t* p_recv_packet_buffer_length; + /// Start time of the transport layer API + uint32_t api_start_time; + ///Chaining error coutn from slave + uint8_t chaining_error_count; + ///Chaining error count for master + uint8_t master_chaining_error_count; + ///State to check last chaining state + uint8_t previous_chaining; + /// transmission done + uint8_t transmission_completed; + /// Error event state + uint8_t error_event; + + /// Upper layer event handler + ifx_i2c_event_handler_t upper_layer_event_handler; +} ifx_i2c_tl_t; + +/** @brief IFX I2C context structure */ +typedef struct ifx_i2c_context +{ + /// I2C Slave address + uint8_t slave_address; + /// Frequency of i2c master + uint16_t frequency; + /// Data link layer frame size + uint16_t frame_size; + /// Pointer to pal gpio context for vdd + pal_gpio_t* p_slave_vdd_pin; + /// Pointer to pal gpio context for reset + pal_gpio_t* p_slave_reset_pin; + /// Pointer to pal i2c context + pal_i2c_t* p_pal_i2c_ctx; + + /// Upper layer event handler + app_event_handler_t upper_layer_event_handler; + /// Upper layer context + void* p_upper_layer_ctx; + /// Pointer to upper layer rx buffer + uint8_t* p_upper_layer_rx_buffer; + /// Pointer to length of upper layer rx buffer + uint16_t* p_upper_layer_rx_buffer_len; + + /// Protocol variables + /// ifx i2c wrapper apis state + uint8_t state; + /// ifx i2c wrapper api status + uint8_t status; + /// reset states + uint8_t reset_state; + /// type of reset + uint8_t reset_type; + /// init pal + uint8_t do_pal_init; + + /// Transport layer context + ifx_i2c_tl_t tl; + /// Datalink layer context + ifx_i2c_dl_t dl; + /// Physical layer context + ifx_i2c_pl_t pl; + + /// IFX I2C tx frame of max length + uint8_t tx_frame_buffer[DL_MAX_FRAME_SIZE]; + /// IFX I2C rx frame of max length + uint8_t rx_frame_buffer[DL_MAX_FRAME_SIZE]; + +} ifx_i2c_context_t; + +/*********************************************************************************************************************** +* GLOBAL +***********************************************************************************************************************/ + +/** @brief IFX I2C Instance */ +extern ifx_i2c_context_t ifx_i2c_context_0; + +/*********************************************************************************************************************** +* LOCAL ROUTINES +***********************************************************************************************************************/ + +/*********************************************************************************************************************** +* API PROTOTYPES +**********************************************************************************************************************/ + +/** + * @} + **/ +#ifdef __cplusplus +} +#endif +#endif /* _IFX_I2C_CONFIG_H_ */ diff --git a/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_data_link_layer.h b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_data_link_layer.h new file mode 100644 index 00000000000..41a5c667046 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_data_link_layer.h @@ -0,0 +1,117 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file ifx_i2c_data_link_layer.h +* +* \brief This file defines the API prototype for data link layer of the Infineon I2C Protocol Stack library. +* +* \ingroup grIFXI2C +* @{ +*/ + +#ifndef _IFX_I2C_DATA_LINK_LAYER_H_ +#define _IFX_I2C_DATA_LINK_LAYER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************************************************************************** +* HEADER FILES +**********************************************************************************************************************/ +#include "optiga/ifx_i2c/ifx_i2c_config.h" + +/*********************************************************************************************************************** +* MACROS +**********************************************************************************************************************/ +/** @brief Error event propagated to upper layer */ +#define IFX_I2C_DL_EVENT_ERROR (0x01) +/** @brief Transmit success event propagated to upper layer (bit field 1) */ +#define IFX_I2C_DL_EVENT_TX_SUCCESS (0x02) +/** @brief Receive success event propagated to upper layer (bit field 3)*/ +#define IFX_I2C_DL_EVENT_RX_SUCCESS (0x04) + +/*********************************************************************************************************************** +* ENUMS +**********************************************************************************************************************/ +/*********************************************************************************************************************** +* DATA STRUCTURES +***********************************************************************************************************************/ +/*********************************************************************************************************************** +* API PROTOTYPES +**********************************************************************************************************************/ +/** + * @brief Function for initializing the module. + * + * Function initializes and enables the module and registers + * an event handler to receive events from this module. + * @attention This function must be called before using the module. + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] handler Function pointer to the event handler of the upper layer. + * + * @retval IFX_I2C_STACK_SUCCESS If initialization was successful. + * @retval IFX_I2C_STACK_ERROR If the module is already initialized. + */ +host_lib_status_t ifx_i2c_dl_init(ifx_i2c_context_t *p_ctx,ifx_i2c_event_handler_t handler); + +/** + * @brief Function for sending a frame. + * + * Asynchronous function to send a frame. The function returns immediately. + * One of the following events is propagated to the event handler registered + * with @ref ifx_i2c_dl_init. + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] frame_len Frame length. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If the module is busy. + */ +host_lib_status_t ifx_i2c_dl_send_frame(ifx_i2c_context_t *p_ctx,uint16_t frame_len); + +/** + * @brief Function for receiving a frame. + * + * Asynchronous function to receive a frame. The function returns immediately. + * One of the following events is propagated to the event handler registered + * with @ref ifx_i2c_dl_init. + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If the module is busy. + */ +host_lib_status_t ifx_i2c_dl_receive_frame(ifx_i2c_context_t *p_ctx); + +#ifdef __cplusplus +} +#endif + +#endif /* _IFX_I2C_DATA_LINK_LAYER_H_ */ + + +/** + * @} + **/ diff --git a/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_physical_layer.h b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_physical_layer.h new file mode 100644 index 00000000000..56db0586bbb --- /dev/null +++ b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_physical_layer.h @@ -0,0 +1,124 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file ifx_i2c_physical_layer.h +* +* \brief This file defines the API prototype for physical layer of the Infineon I2C Protocol Stack library. +* +* \ingroup grIFXI2C +* @{ +*/ + +#ifndef _IFX_I2C_PHYSICAL_LAYER_H_ +#define _IFX_I2C_PHYSICAL_LAYER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************************************************************************** +* HEADER FILES +**********************************************************************************************************************/ +#include "optiga/ifx_i2c/ifx_i2c_config.h" + +/*********************************************************************************************************************** +* MACROS +**********************************************************************************************************************/ +/*********************************************************************************************************************** +* ENUMS +**********************************************************************************************************************/ +/*********************************************************************************************************************** +* DATA STRUCTURES +***********************************************************************************************************************/ +/*********************************************************************************************************************** +* API PROTOTYPES +**********************************************************************************************************************/ +/** + * @brief Function for initializing the module. + * + * Function initializes and enables the module and registers + * an event handler to receive events from this module. + * @attention This function must be called before using the module. + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] handler Function pointer to the event handler of the upper layer. + * + * @retval IFX_I2C_STACK_SUCCESS If initialization was successful. + * @retval IFX_I2C_STACK_ERROR If the module is already initialized. + */ +host_lib_status_t ifx_i2c_pl_init(ifx_i2c_context_t *p_ctx,ifx_i2c_event_handler_t handler); + +/** + * @brief Function for sending a frame. + * + * Asynchronous function to send a frame. The function returns immediately. + * One of the following events is propagated to the event handler registered + * with @ref ifx_i2c_pl_init + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] p_frame Buffer containing the frame. + * @param[in] frame_len Frame length. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If the module is busy. + */ +host_lib_status_t ifx_i2c_pl_send_frame(ifx_i2c_context_t *p_ctx,uint8_t* p_frame, uint16_t frame_len); + +/** + * @brief Function for receiving a frame. + * + * Asynchronous function to receive a frame. The function returns immediately. + * One of the following events is propagated to the event handler registered + * with @ref ifx_i2c_pl_init + * + * @param[in] p_ctx Pointer to ifx i2c context. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If the module is busy. + */ +host_lib_status_t ifx_i2c_pl_receive_frame(ifx_i2c_context_t *p_ctx); + + +/** + * @brief Function for setting slave address. + * + * Synchronous function to set slave address. + * + * @param[in] p_ctx Pointer to ifx i2c context. + *\param[in] slave_address Holds new slave address[7 Bit] to be set. + * \param[in] persistent 0 - To set the Slave address until next reset.
+ * Non-zero - To set the slave address to persistent memory. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If setting slave address fails. + */ +host_lib_status_t ifx_i2c_pl_write_slave_address(ifx_i2c_context_t *p_ctx, uint8_t slave_address, uint8_t storage_type); +/** + * @} + **/ + +#ifdef __cplusplus +} +#endif +#endif /* _IFX_I2C_PHYSICAL_LAYER_H_ */ diff --git a/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_transport_layer.h b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_transport_layer.h new file mode 100644 index 00000000000..d26a02c8b1d --- /dev/null +++ b/tools/sdk/include/trustx/optiga/ifx_i2c/ifx_i2c_transport_layer.h @@ -0,0 +1,98 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file ifx_i2c_transport_layer.h +* +* \brief This file defines the API prototype for transport layer of the Infineon I2C Protocol Stack library. +* +* \ingroup grIFXI2C +* @{ +*/ + +#ifndef _IFX_I2C_TRANSPORT_LAYER_H_ +#define _IFX_I2C_TRANSPORT_LAYER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/*********************************************************************************************************************** +* HEADER FILES +**********************************************************************************************************************/ +#include "optiga/ifx_i2c/ifx_i2c_config.h" +#include "optiga/pal/pal_os_timer.h" + +/*********************************************************************************************************************** +* MACROS +**********************************************************************************************************************/ +/*********************************************************************************************************************** +* ENUMS +**********************************************************************************************************************/ +/*********************************************************************************************************************** +* DATA STRUCTURES +***********************************************************************************************************************/ +/*********************************************************************************************************************** +* API PROTOTYPES +**********************************************************************************************************************/ +/** + * @brief Function for initializing the module. + * + * Function initializes and enables the module and registers + * an event handler to receive events from this module. + * @attention This function must be called before using the module. + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] handler Function pointer to the event handler of the upper layer. + * + * @retval IFX_I2C_STACK_SUCCESS If initialization was successful. + * @retval IFX_I2C_STACK_ERROR If the module is already initialized. + */ +host_lib_status_t ifx_i2c_tl_init(ifx_i2c_context_t *p_ctx,ifx_i2c_event_handler_t handler); + +/** + * @brief Function to transmit and receive a packet. + * + * Asynchronous function to send and receive a packet. + * The function returns immediately. One of the following events is + * propagated to the event handler registered with @ref ifx_i2c_tl_init + * + * @param[in,out] p_ctx Pointer to ifx i2c context. + * @param[in] p_packet Buffer containing the packet header. + * @param[in] packet_len Packet header length. + * @param[in] p_recv_packet Buffer containing the packet payload. + * @param[in] recv_packet_len Packet payload length. + * + * @retval IFX_I2C_STACK_SUCCESS If function was successful. + * @retval IFX_I2C_STACK_ERROR If the module is busy. + */ +host_lib_status_t ifx_i2c_tl_transceive(ifx_i2c_context_t *p_ctx,uint8_t* p_packet, uint16_t packet_len, + uint8_t* p_recv_packet, uint16_t* recv_packet_len); + +/** + * @} + **/ +#ifdef __cplusplus +} +#endif +#endif /* IFX_I2C_TRANSPORT_LAYER_H__ */ diff --git a/tools/sdk/include/trustx/optiga/optiga_crypt.h b/tools/sdk/include/trustx/optiga/optiga_crypt.h new file mode 100644 index 00000000000..95b66211b20 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/optiga_crypt.h @@ -0,0 +1,527 @@ +/** +* \copyright +* Copyright© 2018, Infineon Technologies AG +* All rights reserved. +* +* This software is provided with terms and conditions as specified in OPTIGA™ Trust X Evaluation Kit License Agreement. +* \endcopyright +* +* \author Infineon AG +* +* \file +* +* \brief This file implements the prototype declarations of OPTIGA Crypt. +* +* \ingroup grOptigaCrypt +* @{ +*/ +#ifndef _OPTIGA_CRYPT_H_ +#define _OPTIGA_CRYPT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +//#include "datatypes.h" +#include "optiga/cmd/CommandLib.h" + +/** + * OPTIGA crypt module return values + */ +///OPTIGA crypt API execution is successful +#define OPTIGA_CRYPT_SUCCESS (0x0000) +///OPTIGA crypt module in busy state +#define OPTIGA_CRYPT_BUSY (0x0001) +///OPTIGA crypt API failed +#define OPTIGA_CRYPT_ERROR (0x0402) +///OPTIGA crypt API called with invalid inputs +#define OPTIGA_CRYPT_ERROR_INVALID_INPUT (0x0403) +///OPTIGA crypt API called with insufficient memory buffer +#define OPTIGA_CRYPT_ERROR_MEMORY_INSUFFICIENT (0x0404) +///OPTIGA crypt API called when, a request of same instance is already in service +#define OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE (0x0405) + +/** + * \brief Typedef for Key IDs + * The KEY_STORE_ID_xxx holds only private key + * + * The SESSION_ID_xxx can hold private key, premaster secret(ECDH) and master secret(PRF). + * But only one at a time. + */ +typedef enum optiga_key_id +{ + /// Key from key store (non-volatile) + OPTIGA_KEY_STORE_ID_E0F0 = 0xE0F0, + /// Key from key store (non-volatile) + OPTIGA_KEY_STORE_ID_E0F1 = 0xE0F1, + /// Key from key store (non-volatile) + OPTIGA_KEY_STORE_ID_E0F2 = 0xE0F2, + /// Key from key store (non-volatile) + OPTIGA_KEY_STORE_ID_E0F3 = 0xE0F3, + + /// Key from Session context id 1 (volatile) + OPTIGA_SESSION_ID_E100 = 0xE100, + /// Key from Session context id 2 (volatile) + OPTIGA_SESSION_ID_E101 = 0xE101, + /// Key from Session context id 3 (volatile) + OPTIGA_SESSION_ID_E102 = 0xE102, + /// Key from Session context id 4 (volatile) + OPTIGA_SESSION_ID_E103 = 0xE103, +} optiga_key_id_t; + + +/** + * OPTIGA Random Generation types + */ +typedef enum optiga_rng_types +{ + /// Generate Random data using TRNG + OPTIGA_RNG_TYPE_TRNG = 0x00, + /// Generate Random data using DRNG + OPTIGA_RNG_TYPE_DRNG = 0x01, +} optiga_rng_types_t; + +typedef enum optiga_ecc_curve +{ + //TBD: values to be aligned as per the specification + /// + OPTIGA_ECC_NIST_P_256 = 0x03, + /// + OPTIGA_ECC_NIST_P_384 = 0x04, +} optiga_ecc_curve_t; + +typedef enum optiga_key_usage +{ + /// This enables the private key for the signature generation as part of authentication commands + OPTIGA_KEY_USAGE_AUTHENTICATION = 0x01, + /// This enables the private key for the signature generation + OPTIGA_KEY_USAGE_SIGN = 0x10, + /// This enables the private key for key agreement (e.g. ecdh operations) + OPTIGA_KEY_USAGE_KEY_AGREEMENT = 0x20, +} optiga_key_usage_t; + +/** + * \brief To specify the hash context information. + */ +typedef struct optiga_hash_context +{ + ///buffer to hold the context data + uint8_t * context_buffer; + ///context length + uint16_t context_buffer_length; + ///hashing algorithm + uint8_t hash_algo; +} optiga_hash_context_t; + +/** + * \brief Specifies the hashing algorithm type in OPTIGA. + */ +typedef enum optiga_hash_type +{ + /// Hash algorithm type SHA256 + OPTIGA_HASH_TYPE_SHA_256 = 0xE2 +} optiga_hash_type_t; + +/** + * \brief To specify the data coming from the host for hashing. + */ +typedef struct hash_data_from_host +{ + /// data for hashing + const uint8_t * buffer; + /// data length for hashing + uint32_t length; +} hash_data_from_host_t; + +/** + * \brief To specify the data object for hashing. + */ +typedef struct hash_data_in_optiga +{ + ///OID of data object + uint16_t oid; + ///Offset within the data object + uint16_t offset; + ///Number of data bytes starting from the offset + uint16_t length; +} hash_data_in_optiga_t; + +/** + * \brief To specifiy the Public Key details (key, size and algorithm) + */ +typedef struct public_key_from_host +{ + /// Public Key + uint8_t * public_key; + /// Length of public key + uint16_t length; + /// Public key algorithm + uint8_t curve; +} public_key_from_host_t; + +/** + * \brief To specify the OID which holds the shared secret. + */ +typedef struct key_from_opitga +{ + /// + uint16_t key_oid; +} optiga_shared_secret_t; + +/** + * @brief Generates a random number. + * + * Generates the requested random stream of data for the user provided length.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Invokes #optiga_cmd_get_random API, based on the input arguments to retrieve random data .
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The maximum value of the random_data_length parameter is size of buffer random_data. + * In case the value is greater than buffer size, memory corruption can occur.
+ * + * \param[in] rng_type Type of random data generator. + * - The input must be from #optiga_rng_type. + * - Argument check for rng_type is not done since OPTIGA will provide an error for invalid rng_type. + * \param[in,out] random_data Pointer to the buffer into which random data is stored, must not be NULL. + * \param[in] random_data_length Length of random data to be generated. + * - Range should be 8 - 256 bytes. + * - Length validation is not done, since OPTIGA will provide an error for invalid random_data_length. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_random(optiga_rng_types_t rng_type, + uint8_t * random_data, + uint16_t random_data_length); + + + + /** + * + * @brief Initializes a hash context. + * + * Sets up a hash context and exports it.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details:
+ * - Initializes a new hash context.
+ * - Exports the hash context to caller.
+ * + *Notes:
+ + * - Error codes from lower layer will be returned as it is.
+ * - User must save the output hash context for further usage as OPTIGA does not store it internally.
+ * + *
+ * \param[inout] hash_ctx Pointer to #optiga_hash_context_t to store the hash context from OPTIGA + * - The input hash_algo in hash_ctx must be from #optiga_hash_type. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_hash_start(optiga_hash_context_t * hash_ctx); + + + /** + * + * @brief Updates a hash context with the input data. + * + * Updates hashing for the given data and hash context then export the updated hash context.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * - #optiga_hash_context_t from #optiga_crypt_hash_start or #optiga_crypt_hash_update must be available. + * + *API Details:
+ * - Update the input hash context.
+ * - Exports the hash context to caller.
+ * + *Notes:
+ + * - Error codes from lower layer will be returned as it is.
+ * - User must save the output hash context for further usage as OPTIGA does not store it internally.
+ * + *
+ * \param[in] hash_ctx Pointer to #optiga_hash_context_t containing hash context from OPTIGA, must not be NULL + * \param[in] source_of_data_to_hash Data from host / Data in optiga. Must be one of the below + * - #OPTIGA_CRYPT_HOST_DATA,if source of data is from Host. + * - #OPTIGA_CRYPT_OID_DATA,if the source of data is from OPITGA. + * \param[in] data_to_hash Data for hashing either in #hash_data_from_host or in #hash_data_in_optiga + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_hash_update(optiga_hash_context_t * hash_ctx, + uint8_t source_of_data_to_hash, + void * data_to_hash); + + /** + * + * @brief Finalizes and exports the hash output. + * + * Finalizes the hash context and returns hash as output.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * - #optiga_hash_context_t from #optiga_crypt_hash_start or #optiga_crypt_hash_update must be available. + * + *API Details:
+ * - Finalize the hash from the input hash context + * - Exports the finalized hash. + * + *Notes:
+ + * - Error codes from lower layer will be returned as it is.
+ * - hash context is not updated by this API. This can be used later to fulfill intermediate hash use-cases
+ * - User must save the output hash context for further usage as OPTIGA does not store it internally.
+ * + *
+ * \param[in] hash_ctx Pointer to #optiga_hash_context_t containing hash context from OPTIGA, must not be NULL + * \param[inout] hash_output Output Hash + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_hash_finalize(optiga_hash_context_t * hash_ctx, + uint8_t * hash_output); + + + +/** + * + * @brief Generates an key pair based on ECC curves. + * + * Generates an ECC key-pair based on the type of the key.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Generate an ECC key pair using OPTIGA.
+ * - If export is requested, Exports the private key else stores it in the input private key OID.
+ * - Exports the public key always.
+ *
+ * + *Notes: + + * - Error codes from lower layers will be returned as it is.
+ * + * \param[in] curve_id ECC curve id. + * \param[in] key_usage Key usage defined by #optiga_key_usage_t. + * - Values from #optiga_key_usage can be logically ORed and passed.
+ * - It is ignored if export_private_key is FALSE (0). + * \param[in] export_private_key TRUE (1) - Exports both private key and public key to the host.
+ * FALSE (0) - Exports only public key to the host. The input key_usage is ignored. + * \param[in] private_key Buffer to store private key or private key OID of OPTIGA, must not be NULL. + * - If export_private_key is TRUE, assign pointer to a buffer to store private key. + * - The size of the buffer must be sufficient enough to accommodate the key type and additional DER encoding formats. + * - If export_private_key is FALSE, assign pointer to variable of type #optiga_key_id_t. + * \param[in,out] public_key Buffer to store public key, must not be NULL. + * \param[in] public_key_length Initially set as length of public_key, later updated as actual length of public_key. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_ecc_generate_keypair(optiga_ecc_curve_t curve_id, + uint8_t key_usage, + bool_t export_private_key, + void * private_key, + uint8_t * public_key, + uint16_t * public_key_length); + + + /** + * + * @brief Generates a signature for the given digest. + * + * Generates a signature for the given digest using private key stored in OPTIGA.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
. + * + *API Details: + * - Generated signature for the input digest.
+ * - Exports the generated signature.
+ *
+ * + *Notes: + + * - Error codes from lower layers will be returned as it is.
+ * + * \param[in] digest Digest on which signature is generated. + * \param[in] digest_length Length of the input digest. + * \param[in] private_key Private key OID to generate signature. + * \param[in,out] signature Generated signature, must not be NULL. + * - The size of the buffer must be sufficient enough to accommodate the additional DER encoding formatting for R and S components of signature. + * \param[in] signature_length Length of signature.Intial value set as length of buffer, later updated as the actual length of generated signature. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_ecdsa_sign(uint8_t * digest, + uint8_t digest_length, + optiga_key_id_t private_key, + uint8_t * signature, + uint8_t * signature_length); + +/** + * + * @brief Verifies the signature over the given digest. + * + * Verifies the signature over a given digest provided with the input data.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application.
+ * + *API Details: + * - Verifies the signature over the given provided with the input data using public key. + * - It invokes the callback handler of the instance, when it is asynchronously completed. + * + *Notes: + * - Error codes from lower layers will be returned as it is to the application.
+ * + * \param[in] digest Pointer to a given digest buffer, must not be NULL. + * \param[in] digest_length Length of digest + * \param[in] signature Pointer to a given signature buffer, must not be NULL. + * \param[in] signature_length Length of signature + * \param[in] public_key_source_type Public key from host / public key of certificate OID from OPTIGA. Value must be one of the below + * - #OPTIGA_CRYPT_OID_DATA, if the public key is to used from the certificate data object from OPTIGA. + * - #OPTIGA_CRYPT_HOST_DATA, if the public key is provided by host. + * \param[in] public_key Public key from host / public key of certificate OID. Value must be one of the below + * - For certificate OID, pointer OID value must be passed. + * - For Public key from host, pointer to #public_key_from_host_t instance. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_ecdsa_verify(uint8_t * digest, + uint8_t digest_length, + uint8_t * signature, + uint16_t signature_length, + uint8_t public_key_source_type, + void * public_key); + + /** + * @brief Calculates the shared secret using ECDH algorithm. + * + * Calculates the shared secret using ECDH algorithm.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application.
+ * - There must be a secret available in the "session context / data object OID" provided as input parameter.
+ * + *API Details: + * - Calculates the shared secret based on input private key object ID and public key.
+ * - Based on user request(export_to_host), the shared secret can either be exported to the host or be stored in the acquired session object ID.
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The buffer size for shared secret should be appropriately provided by the user + * - If the user provides private_key as session based and export_to_host as FALSE,
+ * then the shared secret generated will overwrite the private key stored in the session object ID + * + * \param[in] private_key Object ID of the private key stored in OPTIGA.
+ * - Possible values are from the #optiga_key_id_t
+ * - Argument check for private_key is not done since OPTIGA will provide an error for invalid private_key. + * \param[in] public_key Pointer to the public key structure for shared secret generation with its properties, must not be NULL.
+ * - Provide the inputs according to the structure type #public_key_from_host_t + * \param[in] export_to_host TRUE (1) - Exports the generated shared secret to Host.
+ * FALSE (0) - Stores the generated shared secret into the session object ID acquired by the instance. + * \param[in,out] shared_secret Pointer to the shared secret buffer, only if export_to_host is TRUE.
+ * Otherwise supply NULL as input. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_ecdh(optiga_key_id_t private_key, + public_key_from_host_t * public_key, + bool_t export_to_host, + uint8_t * shared_secret); + +/** + * @brief Derives a key. + * + * Derives a key using the secret stored in OPTIGA.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application.
+ * - There must be a secret available in the "session context / data object OID" provided as input parameter.
+ * - An instance of #optiga_crypt_t must be created using the #optiga_crypt_create API. + * + *API Details: + * - Derives a key using the secret stored in OPTIGA. + * - Provides the options to store the derived key into OPTIGA session context or export to the host. + * - It invokes the callback handler of the instance, when it is asynchronously completed. + * + *Notes: + * - At present, the minimum length of the output derived key is 16. + * - Error codes from lower layers will be returned as it is to the application.
+ * + * \param[in] secret Object ID of the secret stored in OPTIGA. + * - #OPTIGA_KEY_ID_SESSION_BASED from #optiga_key_id_t, indicates the secret is available. + * in the session context acquired by the instance. + * - or any OPTIGA data object ID(16 bit OID) which holds the secret. + * \param[in] label Pointer to the label, can be NULL if not applicable. + * \param[in] label_length Length of the label. + * \param[in] seed Valid pointer to the seed, must not be NULL. + * \param[in] seed_length Length of the seed. + * \param[in] derived_key_length Length of derived key. + * \param[in] export_to_host TRUE (1) - Exports the derived key to Host.
+ * FALSE (0) - Stores the derived key into the session object ID acquired by the instance. + * \param[in,out] derived_key Pointer to the valid buffer with a minimum size of derived_key_length, + * in case of exporting the key to host(export_to_host= TRUE). Otherwise set to NULL. + * + * \retval #OPTIGA_CRYPT_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_CRYPT_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_CRYPT_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing is used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_crypt_tls_prf_sha256(uint16_t secret, + uint8_t * label, + uint16_t label_length, + uint8_t * seed, + uint16_t seed_length, + uint16_t derived_key_length, + bool_t export_to_host, + uint8_t * derived_key); + + +#ifdef __cplusplus +} +#endif + +#endif //_OPTIGA_CRYPT_H_ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/optiga_util.h b/tools/sdk/include/trustx/optiga/optiga_util.h new file mode 100644 index 00000000000..0ee5faa705f --- /dev/null +++ b/tools/sdk/include/trustx/optiga/optiga_util.h @@ -0,0 +1,344 @@ +/** +* \copyright +* Copyright� 2018, Infineon Technologies AG +* All rights reserved. +* +* This software is provided with terms and conditions as specified in OPTIGA� Trust X Evaluation Kit License Agreement. +* \endcopyright +* +* \author Infineon AG +* +* \file +* +* \brief This file defines APIs, types and data structures used in the OPTIGA UTIL module. +* +* \ingroup grOptigaUtil +* @{ +*/ + +#ifndef _H_OPTIGA_UTIL_H_ +#define _H_OPTIGA_UTIL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "optiga/cmd/CommandLib.h" +#include "optiga/common/AuthLibSettings.h" + +///Requested operation completed without any error +#define INT_LIB_OK 0x75AB1C02 + +///Null parameter(s) +#define INT_LIB_NULL_PARAM 0x80002001 + +///Invalid data in response +#define INT_LIB_INVALID_RESPONSE (INT_LIB_NULL_PARAM + 1) + +///Invalid access condition +#define INT_LIB_INVALID_AC (INT_LIB_NULL_PARAM + 2) + +///Length of input is zero +#define INT_LIB_ZEROLEN_ERROR (INT_LIB_NULL_PARAM + 3) + +///Invalid or unsupported parameter(s) +#define INT_LIB_INVALID_PARAM (INT_LIB_NULL_PARAM + 4) + +///Invalid Length +#define INT_LIB_INVALID_LENGTH (INT_LIB_NULL_PARAM + 5) + +///Malloc Failures +#define INT_LIB_MALLOC_FAILURE (INT_LIB_NULL_PARAM + 6) + +///General error +#define INT_LIB_ERROR 0xFE5A5502 + + +/// Option to only write the data object +#define OPTIGA_UTIL_WRITE_ONLY (0x00) +/// Option to erase and write the data object +#define OPTIGA_UTIL_ERASE_AND_WRITE (0x40) + + +/** + * OPTIGA util module return values + */ +///OPTIGA util API execution is successful +#define OPTIGA_UTIL_SUCCESS (0x0000) +///OPTIGA util module in busy state +#define OPTIGA_UTIL_BUSY (0x0001) +///OPTIGA util API failed +#define OPTIGA_UTIL_ERROR (0x0302) +///OPTIGA util API called with invalid inputs +#define OPTIGA_UTIL_ERROR_INVALID_INPUT (0x0303) +///OPTIGA util API called with insufficient memory buffer +#define OPTIGA_UTIL_ERROR_MEMORY_INSUFFICIENT (0x0304) +///OPTIGA util API called when, a request of same instance is already in service +#define OPTIGA_UTIL_ERROR_INSTANCE_IN_USE (0x0305) + + +/** + * \brief Typedef for OIDs + */ +typedef enum eOID_d +{ + /// Global Life Cycle State + eLCS_G = 0xE0C0, + /// Global Security Status + eSECURITY_STATUS_G = 0xE0C1, + /// Coprocessor UID + eCOPROCESSOR_UID = 0xE0C2, + /// Global Life Cycle State + eSLEEP_MODE_ACTIVATION_DELAY = 0xE0C3, + /// Current limitation + eCURRENT_LIMITATION = 0xE0C4, + /// Security Event Counter + eSECURITY_EVENT_COUNTER = 0xE0C5, + /// Device Public Key Certificate issued by IFX + eDEVICE_PUBKEY_CERT_IFX = 0xE0E0, + /// Project-Specific device Public Key Certificate + eDEVICE_PUBKEY_CERT_PRJSPC_1 = 0xE0E1, + /// Project-Specific device Public Key Certificate + eDEVICE_PUBKEY_CERT_PRJSPC_2 = 0xE0E2, + /// Project-Specific device Public Key Certificate + eDEVICE_PUBKEY_CERT_PRJSPC_3 = 0xE0E3, + /// First Device Private Key + eFIRST_DEVICE_PRIKEY_1 = 0xE0F0, + /// First Device Private Key + eFIRST_DEVICE_PRIKEY_2 = 0xE0F1, + /// First Device Private Key + eFIRST_DEVICE_PRIKEY_3 = 0xE0F2, + /// First Device Private Key + eFIRST_DEVICE_PRIKEY_4 = 0xE0F3, + /// Application Life Cycle Status + eLCS_A = 0xF1C0, + /// Application Security Status + eSECURITY_STATUS_A = 0xF1C1, + /// Error codes + eERROR_CODES = 0xF1C2 +} eOID_d; + +/** + * @brief Initializes the communication with optiga. + * + * Initializes the communication with OPTIGA for the given instance.
+ * + *Pre Conditions: + * - An instance of type #optiga_util_t using #optiga_util_create must be available. + * - The optiga_comms_open() function should be called + * + *API Details: + * - Sets up optiga comms channel.
+ * - Initiate open application command to optiga.
+ * - Perform application restore and manage context session restore operations.
+ *
+ * + *Notes:
+ * Initialisation flow example: + * + * optiga_comms_t optiga_comms = {(void*)&ifx_i2c_context_0, NULL, NULL, 0}; + * static host_lib_status_t optiga_comms_status; + * + * static void optiga_comms_event_handler(void* upper_layer_ctx, host_lib_status_t event) + * { + * optiga_comms_status = event; + * } + * + * static int32_t optiga_init(void) + * { + * int32_t status = (int32_t) OPTIGA_LIB_ERROR; + * + * do + * { + * // OPTIGA Initialisation phase + * //Invoke optiga_comms_open to initialize the IFX I2C Protocol and security chip + * optiga_comms_status = OPTIGA_COMMS_BUSY; + * optiga_comms.upper_layer_handler = optiga_comms_event_handler; + * status = optiga_comms_open(&optiga_comms); + * if(E_COMMS_SUCCESS != status) + * { + * configPRINTF( ("Failure: optiga_comms_open(): 0x%04X\n\r", status) ); + * break; + * } + * + * //Wait until IFX I2C initialization is complete + * while(optiga_comms_status == OPTIGA_COMMS_BUSY) + * { + * pal_os_timer_delay_in_milliseconds(1); + * } + * + * if((OPTIGA_COMMS_SUCCESS != status) || (optiga_comms_status == OPTIGA_COMMS_ERROR)) + * { + * configPRINTF( ("Failure: optiga_comms_status(): 0x%04X\n\r", status) ); + * break; + * } + * + * status = optiga_util_open_application(&optiga_comms); + * if(OPTIGA_LIB_SUCCESS != status) + * { + * configPRINTF( ("Failure: CmdLib_OpenApplication(): 0x%04X\n\r", status) ); + * break; + * } + * + * status = OPTIGA_LIB_SUCCESS; + * } while(0); + * + * return status; + * } + * + * \param[in] p_comms Pointer to the communication parameters initialised before + * - Error codes from lower layer will be returned as it is.
+ * + * \retval #OPTIGA_UTIL_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_UTIL_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_UTIL_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_util_open_application(optiga_comms_t* p_comms); + +/** + * @brief Reads data from optiga. + * + * Retrieves the requested data that is stored from the user provided data object.
+ * + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Invokes #optiga_cmd_get_data_object API, based on the input arguments to read the data from the data object.
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The maximum value of the *bytes_to_read parameter is size of buffer p_buffer. In case the value is greater than buffer size, memory corruption can occur.
+ * - *bytes_to_read parameter is set to 0, in case any errors occurs while retrieving the data. initially contains the value of the user provided data length to be read. When the data is successfully retrieved, this value is updated with actual data length retrieved.
+ * In case of any errors, the value is set to 0.
+ * + * \param[in] optiga_oid OID of data object + * - It should be a valid data object, otherwise OPTIGA returns an error.
+ * \param[in] offset Offset from within data object + * - It must be valid offset from within data object, otherwise OPTIGA returns an error.
+ * \param[in,out] buffer Valid pointer to the buffer to which data is read + * \param[in,out] bytes_to_read Valid pointer to the length of data to be read from data object + * - When the data is successfully retrieved, it is updated with actual data length retrieved + * + * \retval #OPTIGA_UTIL_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_UTIL_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_UTIL_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_util_read_data(uint16_t optiga_oid, + uint16_t offset, + uint8_t * buffer, + uint16_t * bytes_to_read); + +/** + * @brief Reads metadata of a data object from optiga. + * + * Reads the metadata of the user provided data object.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Invokes #optiga_cmd_get_data_object API, based on the input arguments to read the metadata from the data object.
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The metadata returned will be in TLV format.
+ * - The maximum value of the *bytes_to_read parameter is size of buffer p_buffer. In case the value is greater than buffer size, memory corruption can occur.
+ * + * \param[in] optiga_oid OID of data object + * - It should be a valid data object, otherwise OPTIGA returns an error.
+ * \param[in,out] buffer Valid pointer to the buffer to which metadata is read + * \param[in,out] bytes_to_read Valid pointer to the length of metadata to be read from data object + * - When the metadata is successfully retrieved, it is updated with actual metadata length retrieved + * + * \retval #OPTIGA_UTIL_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_UTIL_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_UTIL_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_util_read_metadata(uint16_t optiga_oid, + uint8_t * buffer, + uint16_t * bytes_to_read); + +/** + * @brief Writes data to optiga. + * + * Writes the data provided by the user into the specified data object.
+ * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Invokes #optiga_cmd_set_data_object API, based on the input arguments to write the data to the data object.
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The maximum value of the bytes_to_write parameter is size of buffer p_buffer. In case the value is greater than buffer size, incorrect values can get written into the data object in OPTIGA.
+ * - In case the write_type provided is other than erase and write(0x00) or write only(0x40), the function returns #OPTIGA_UTIL_ERROR.
+ * + * \param[in] optiga_oid OID of data object + * - It should be a valid data object, otherwise OPTIGA returns an error.
+ * \param[in] write_type Type of the write operation. Can be OPTIGA_UTIL_ERASE_AND_WRITE or OPTIGA_UTIL_WRITE_ONLY + * \param[in] offset Offset from within data object + * - It must be valid offset from within data object, otherwise OPTIGA returns an error.
+ * \param[in,out] buffer Valid pointer to the buffer with user data to write + * \param[in] bytes_to_write Length of data to be written + * + * \retval #OPTIGA_UTIL_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_UTIL_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_UTIL_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing used + * \retval #OPTIGA_CMD_ERROR_MEMORY_INSUFFICIENT Length of the buffer to copy the metadata is less than actual length of metadata + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_util_write_data(uint16_t optiga_oid, + uint8_t write_type, + uint16_t offset, + uint8_t * buffer, + uint16_t bytes_to_write); + +/** + * @brief Writes metadata for the user provided data object. + * + * Writes metadata for the user provided data object. + * + *Pre Conditions: + * - The application on OPTIGA must be opened using #optiga_util_open_application before using this API.
+ * + *API Details: + * - Invokes #optiga_cmd_set_data_object API, based on the input arguments to write metadata to the data object.
+ *
+ * + *Notes: + * - Error codes from lower layers will be returned as it is.
+ * - The maximum value of the bytes_to_write parameter is size of buffer p_buffer. + * In case the value is greater than buffer size, incorrect values can get written into the meta data of the data object in OPTIGA.
+ * - The metadata to be written must be in TLV format
+ * + * \param[in] optiga_oid OID of data object + * - It should be a valid data object, otherwise OPTIGA returns an error.
+ * \param[in,out] buffer Valid pointer to the buffer with metadata to write + * \param[in] bytes_to_write Length of metadata to be written + * + * \retval #OPTIGA_UTIL_SUCCESS Successful invocation of optiga cmd module + * \retval #OPTIGA_UTIL_ERROR_INVALID_INPUT Wrong Input arguments provided + * \retval #OPTIGA_UTIL_ERROR_INSTANCE_IN_USE Same instance with ongoing request servicing used + * \retval #OPTIGA_DEVICE_ERROR Command execution failure in OPTIGA and the LSB indicates the error code.(Refer Solution Reference Manual) + */ +optiga_lib_status_t optiga_util_write_metadata(uint16_t optiga_oid, + uint8_t * buffer, + uint8_t bytes_to_write); +#ifdef __cplusplus +} +#endif + +#endif //_H_OPTIGA_UTIL_H_ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/pal/pal.h b/tools/sdk/include/trustx/optiga/pal/pal.h new file mode 100644 index 00000000000..b6db6573456 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal.h @@ -0,0 +1,74 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements the prototype declarations of platform abstraction layer +* +* \ingroup grPAL +* @{ +*/ + + +#ifndef _PAL_H_ +#define _PAL_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ +#include "optiga/common/Datatypes.h" + +/********************************************************************************************************************** + * pal.h + *********************************************************************************************************************/ + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + +/// PAL API execution is successful +#define PAL_STATUS_SUCCESS (0x0000) +/// PAL API execution failed +#define PAL_STATUS_FAILURE (0x0001) +/// PAL I2C is busy +#define PAL_STATUS_I2C_BUSY (0x0002) + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ +/** + * \brief PAL return status. + */ +typedef uint16_t pal_status_t; + +/********************************************************************************************************************** + * API Prototypes + *********************************************************************************************************************/ + +#endif /* _PAL_H_ */ + +/** +* @} +*/ + diff --git a/tools/sdk/include/trustx/optiga/pal/pal_gpio.h b/tools/sdk/include/trustx/optiga/pal/pal_gpio.h new file mode 100644 index 00000000000..93c97cfa506 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal_gpio.h @@ -0,0 +1,93 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements the prototype declarations of pal gpio +* +* \ingroup grPAL +* @{ +*/ + +#ifndef _PAL_GPIO_H_ +#define _PAL_GPIO_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ + +#include "optiga/pal/pal.h" + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * DATA STRUCTURES + *********************************************************************************************************************/ + +/** + * \brief Structure defines the PAL GPIO configuration. + */ +typedef struct pal_gpio +{ + /// Pointer to gpio platform specific context/structure + void* p_gpio_hw; + +} pal_gpio_t; + +/********************************************************************************************************************** + * API Prototypes + *********************************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Sets the gpio pin to high state. + */ +void pal_gpio_set_high(const pal_gpio_t* p_gpio_context); + +/** + * \brief Sets the gpio pin to Low state. + */ +void pal_gpio_set_low(const pal_gpio_t* p_gpio_context); + +#ifdef __cplusplus +} +#endif + +#endif /* _PAL_GPIO_H_ */ + +/** +* @} +*/ + diff --git a/tools/sdk/include/trustx/optiga/pal/pal_i2c.h b/tools/sdk/include/trustx/optiga/pal/pal_i2c.h new file mode 100644 index 00000000000..dd46454257b --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal_i2c.h @@ -0,0 +1,107 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements the prototype declarations of pal i2c +* +* \ingroup grPAL +* @{ +*/ + +#ifndef _PAL_I2C_H_ +#define _PAL_I2C_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ + +#include "optiga/pal/pal.h" + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ +/// Event returned when I2C master completes execution +#define PAL_I2C_EVENT_SUCCESS (0x0000) +/// Event returned when I2C master operation fails +#define PAL_I2C_EVENT_ERROR (0x0001) +/// Event returned when lower level I2C bus is busy +#define PAL_I2C_EVENT_BUSY (0x0002) + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ + +/********************************************************************************************************************** + * DATA STRUCTURES + *********************************************************************************************************************/ +/** @brief PAL I2C context structure */ +typedef struct pal_i2c +{ + /// Pointer to I2C master platform specific context + void* p_i2c_hw_config; + /// I2C slave address + uint8_t slave_address; + /// Pointer to store the callers context information + void* upper_layer_ctx; + /// Pointer to store the callers handler + void* upper_layer_event_handler; + +} pal_i2c_t; + +/********************************************************************************************************************** + * API Prototypes + *********************************************************************************************************************/ +/** + * @brief Initializes the I2C master. + */ +pal_status_t pal_i2c_init(const pal_i2c_t* p_i2c_context); + +/** + * @brief Sets the I2C Master bitrate + */ +pal_status_t pal_i2c_set_bitrate(const pal_i2c_t* p_i2c_context, uint16_t bitrate); + +//Dileep: "write on I2C bus" --> "write to I2C bus" +/** + * @brief Writes on I2C bus. + */ +pal_status_t pal_i2c_write(pal_i2c_t* p_i2c_context, uint8_t* p_data , uint16_t length); + +/** + * @brief Reads from I2C bus. + */ +pal_status_t pal_i2c_read(pal_i2c_t* p_i2c_context, uint8_t* p_data , uint16_t length); + +/** + * @brief De-initializes the I2C master. + */ +pal_status_t pal_i2c_deinit(const pal_i2c_t* p_i2c_context); + +#endif /* _PAL_I2C_H_ */ + +/** +* @} +*/ + diff --git a/tools/sdk/include/trustx/optiga/pal/pal_ifx_i2c_config.h b/tools/sdk/include/trustx/optiga/pal/pal_ifx_i2c_config.h new file mode 100644 index 00000000000..0edf2b2b87a --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal_ifx_i2c_config.h @@ -0,0 +1,64 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements the platform abstraction layer extern declarations for ifx i2c. +* +* \ingroup grPAL +* @{ +*/ + +#ifndef _PAL_IFX_I2C_CONFIG_H_ +#define _PAL_IFX_I2C_CONFIG_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ +#include "optiga/pal/pal.h" +#include "optiga/pal/pal_i2c.h" +#include "optiga/pal/pal_gpio.h" + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ + +/********************************************************************************************************************** + * PAL extern definitions for IFX I2C + *********************************************************************************************************************/ +extern pal_i2c_t optiga_pal_i2c_context_0; +extern pal_gpio_t optiga_vdd_0; +extern pal_gpio_t optiga_reset_0; + + +#endif /* _PAL_IFX_I2C_CONFIG_H_ */ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/pal/pal_os_event.h b/tools/sdk/include/trustx/optiga/pal/pal_os_event.h new file mode 100644 index 00000000000..70539d3d064 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal_os_event.h @@ -0,0 +1,65 @@ +/** +* \copyright +* Copyright (c) 2018, Infineon Technologies AG +* All rights reserved. +* +* This software is provided with terms and conditions as specified in OPTIGA? Trust X Evaluation Kit License Agreement. +* \endcopyright +* +* \author Infineon AG +* +* \file +* +* \brief This file implements the prototype declarations of pal os event +* +* \ingroup grPAL +* @{ +*/ + + +#ifndef _PAL_OS_EVENT_H_ +#define _PAL_OS_EVENT_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ + +#include "optiga/common/Datatypes.h" + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ + +/********************************************************************************************************************** + * PAL extern definitions + *********************************************************************************************************************/ + +/** + * @brief typedef for Callback function when timer elapses. + */ +typedef void (*register_callback)(void*); + +#ifdef PAL_OS_HAS_EVENT_INIT +/** + * @brief Platform specific event init function. + */ +pal_status_t pal_os_event_init(void); +#endif + +/** + * @brief Callback registration function to trigger once when timer expires. + */ +void pal_os_event_register_callback_oneshot(register_callback callback, void* callback_args, uint32_t time_us); + + + +#endif //_PAL_OS_EVENT_H_ + +/** +* @} +*/ diff --git a/tools/sdk/include/trustx/optiga/pal/pal_os_timer.h b/tools/sdk/include/trustx/optiga/pal/pal_os_timer.h new file mode 100644 index 00000000000..c02f7561a51 --- /dev/null +++ b/tools/sdk/include/trustx/optiga/pal/pal_os_timer.h @@ -0,0 +1,90 @@ +/** +* MIT License +* +* Copyright (c) 2018 Infineon Technologies AG +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in all +* copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +* SOFTWARE +* +* +* \file +* +* \brief This file implements the prototype declarations of pal os timer functionalities. +* +* \ingroup grPAL +* @{ +*/ +#ifndef _PAL_OS_TIMER_H_ +#define _PAL_OS_TIMER_H_ + +/********************************************************************************************************************** + * HEADER FILES + *********************************************************************************************************************/ + +#include "optiga/pal/pal.h" + +/********************************************************************************************************************* + * pal_os_timer.h +*********************************************************************************************************************/ + + +/********************************************************************************************************************** + * MACROS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * ENUMS + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * DATA STRUCTURES + *********************************************************************************************************************/ + + +/********************************************************************************************************************** + * API Prototypes + *********************************************************************************************************************/ + +#ifdef __cplusplus +extern "C" { +#endif + + +/** + * @brief Gets tick count value in milliseconds + */ +uint32_t pal_os_timer_get_time_in_milliseconds(void); + +/** + * @brief Waits or delay until the supplied milliseconds + */ +void pal_os_timer_delay_in_milliseconds(uint16_t milliseconds); + + +#ifdef __cplusplus +} +#endif + +#endif /* _PAL_OS_TIMER_H_ */ + +/** +* @} +*/ + diff --git a/tools/sdk/lib/libmbedtls.a b/tools/sdk/lib/libmbedtls.a index 652fd64b986..550e8ea6546 100644 Binary files a/tools/sdk/lib/libmbedtls.a and b/tools/sdk/lib/libmbedtls.a differ diff --git a/tools/sdk/lib/libtrustx.a b/tools/sdk/lib/libtrustx.a new file mode 100644 index 00000000000..fe7d1d0b5ed Binary files /dev/null and b/tools/sdk/lib/libtrustx.a differ