Skip to content
This repository was archived by the owner on Aug 15, 2018. It is now read-only.

Adding a shell script to convert the library to arduino compatible library #6

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 59 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ Happy experimenting!

Trying the samples is a good way to get started with using the SDK.

Getting the samples working has the following steps: setting up the DynamoDB table, importing the SDK and copying over the sample, creating the `keys.h` and `keys.cpp` files, and setting up the hardware. These steps are outlined for the samples for both the Spark core and Intel Galileo, so be sure you are following only the directions corresponding to the device and sample you are using.
Getting the samples working has the following steps: setting up the DynamoDB table, importing the SDK and copying over the sample, creating the `keys.h` and `keys.cpp` files, and setting up the hardware. These steps are outlined for the samples for both the Spark core and Intel Galileo, so be sure you are following only the directions corresponding to the device and sample you are using.

If you are using a device other than Spark or Galileo, you may want to read through these steps anyway before implementing the interfaces in `DeviceIndependentInterfaces.cpp`/`.h` for your device.

### Step 1: Setting up the DynamoDB Table

For either device you will need to set up a DynamoDB table with the same name, hash key, and range key as in the sample you are using. These values are defined as constants in the sample, i.e. `HASH_KEY_NAME` and `TABLE_NAME`.
For either device you will need to set up a DynamoDB table with the same name, hash key, and range key as in the sample you are using. These values are defined as constants in the sample, i.e. `HASH_KEY_NAME` and `TABLE_NAME`.

You can follow the steps below to get the tables set up with the right values, chosing the right set of instructions based on which sample you are using.

Expand Down Expand Up @@ -65,7 +65,7 @@ This step is different for the Spark Core and Intel Galileo.

#### Connected Maraca Sample (Edison/SparkCore/MediaTek)

follow the step by step guide: http://bit.ly/aws-iot-hackseries
follow the step by step guide: http://bit.ly/aws-iot-hackseries

#### Intel Galileo/Edison Sample

Expand All @@ -80,11 +80,11 @@ Create a new sketch with the Arduino IDE and copy and paste the sample code into

#### Spark IO Core Sample

This assumes you already have your Spark set up and are able to program it with Spark Build. If you do not, head over to [Spark's website](http://docs.spark.io/).
This assumes you already have your Spark set up and are able to program it with Spark Build. If you do not, head over to [Spark's website](http://docs.spark.io/).

Open up the Spark Build web page and create a new app. Name it whatever you would like.

Copy the contents of the sample you are using into the `.ino` file of your new app.
Copy the contents of the sample you are using into the `.ino` file of your new app.

Next you need to import the SDK. Because the Spark Build IDE isn't local to your machine, you can't just `cp` the files over. Instead use the "+" tab in the top right corner of the Spark Build page to create a new file for each `.cpp`/`.h` file in the `src/` directory, except `GalileoAWSImplementations` and `AmazonKinesisClient`. Then copy and paste the contents of each file.

Expand All @@ -99,7 +99,7 @@ You will need to create and add `keys.h` and `keys.cpp` into the `AWSArduinoSDK`
#ifndef KEYS_H_
#define KEYS_H_

extern const char* awsKeyID; // Declare these variables to
extern const char* awsKeyID; // Declare these variables to
extern const char* awsSecKey; // be accessible by the sketch

#endif
Expand Down Expand Up @@ -129,7 +129,7 @@ Both spark samples use just one button connected to the D2 pin.

#### Intel Galileo

This sample uses five buttons and a RGB LED.
This sample uses five buttons and a RGB LED.

The RGB LED has the red leg connected to pin 6, the green leg connected to pin 9, and the blue leg connected to pin 10.

Expand All @@ -145,4 +145,56 @@ For Galileo/Edison, after the wiring is finished, you should be able to connect

For Spark, after the wiring is finished, you should be able to connect it to your computer via USB, and *Flash* the code. Be sure to refer to the comments in the samples for help.

#### ESP8266

You can use these libraries with the [Arduino ESP8266](https://github.com/esp8266/arduino):.

```
#include <ESP8266WiFi.h>
#include <AmazonIOTClient.h>
#include "Esp8266AWSImplementations.h"

Esp8266HttpClient httpClient;
Esp8266DateTimeProvider dateTimeProvider;

AmazonIOTClient iotClient;
ActionError actionError;

void setup() {
Serial.begin(115200);
delay(10);

// Connect to WAP
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());

iotClient.setAWSRegion("eu-west-1");
iotClient.setAWSEndpoint("amazonaws.com");
iotClient.setAWSDomain("foobar.iot.eu-west-1.amazonaws.com");
iotClient.setAWSPath("/things/example-1/shadow");
iotClient.setAWSKeyID("ID");
iotClient.setAWSSecretKey("SECRET");
iotClient.setHttpClient(&httpClient);
iotClient.setDateTimeProvider(&dateTimeProvider);
}

void loop(){
char* shadow = "{\"state\":{\"reported\": {\"foobar\": "bar"}}}";

char* result = iotClient.update_shadow(shadow, actionError);
Serial.print(result);

delay(60000);
}

```
2 changes: 1 addition & 1 deletion src/common/AWSClient2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ char* AWSClient2::createRequest(MinimalString &reqPayload) {
if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0
|| httpClient == 0 || dateTimeProvider == 0)
return 0;

createRequestInit(reqPayload);
char* request = headersToRequest();
createRequestCleanup();
Expand Down
16 changes: 8 additions & 8 deletions src/common/AWSClient2.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@
#include "AWSFoundationalTypes.h"

/* Total number of headers. */
static const int HEADER_COUNT = 7;
static const int HEADER_COUNT2 = 7;
/* Size of the awsDate string. */
static const int AWS_DATE_LEN = 8;
static const int AWS_DATE_LEN2 = 8;
/* Size of the awsTime string. */
static const int AWS_TIME_LEN = 6;
static const int AWS_TIME_LEN2 = 6;
/* Size of sha hashes and signatures in hexidecimal. */
static const int HASH_HEX_LEN = 64;
static const int HASH_HEX_LEN2 = 64;

/* Base class for an AWS Service Client. Creates http and https request in raw
* http format or as a curl command. */
Expand All @@ -32,15 +32,15 @@ class AWSClient2 {
/* The user's AWS Access Key ID for accessing the AWS Resource. */
char* awsKeyID;
/* GMT date in yyyyMMdd format. */
char awsDate[AWS_DATE_LEN + 1];
char awsDate[AWS_DATE_LEN2 + 1];
/* GMT time in HHmmss format. */
char awsTime[AWS_TIME_LEN + 1];
char awsTime[AWS_TIME_LEN2 + 1];
/* Number of headers created. */
int headersCreated;
/* Array of the created http headers. */
char* headers[HEADER_COUNT];
char* headers[HEADER_COUNT2];
/* Array of string lengths of the headers in the "headers" array. */
int headerLens[HEADER_COUNT];
int headerLens[HEADER_COUNT2];
/* The payload of the httprequest to be created */
MinimalString payload;

Expand Down
230 changes: 230 additions & 0 deletions src/common/AWSClient4.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
/*
* AWSClient.cpp
*
* See AWSClient.h for description.
* See http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
*
*/

#include "AWSClient4.h"
#include "Utils.h"
#include "DeviceIndependentInterfaces.h"
#include "AWSFoundationalTypes.h"
#include "sha256.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

AWSClient4::AWSClient4() {
/* Null until set in init method. */
awsRegion = 0;
awsEndpoint = 0;
awsSecKey = 0;
awsKeyID = 0;
httpClient = 0;
dateTimeProvider = 0;
method = 0;
uri = 0;
}

void AWSClient4::setAWSRegion(const char * awsRegion) {
int len = strlen(awsRegion) + 1;
this->awsRegion = new char[len]();
strcpy(this->awsRegion, awsRegion);
}
void AWSClient4::setAWSEndpoint(const char * awsEndpoint) {
int len = strlen(awsEndpoint) + 1;
this->awsEndpoint = new char[len]();
strcpy(this->awsEndpoint, awsEndpoint);
}
void AWSClient4::setAWSDomain(const char * awsDomain) {
int len = strlen(awsDomain) + 1;
this->awsDomain = new char[len]();
strcpy(this->awsDomain, awsDomain);
}
void AWSClient4::setAWSPath(const char * awsPath) {
int len = strlen(awsPath) + 1;
this->awsPath = new char[len]();
strcpy(this->awsPath, awsPath);
}
void AWSClient4::setAWSSecretKey(const char * awsSecKey) {
int len = strlen(awsSecKey) + 1;
this->awsSecKey = new char[len]();
strcpy(this->awsSecKey, awsSecKey);
}
void AWSClient4::setAWSKeyID(const char * awsKeyID) {
int len = strlen(awsKeyID) + 1;
this->awsKeyID = new char[len]();
strcpy(this->awsKeyID, awsKeyID);
}
void AWSClient4::setHttpClient(IHttpClient* httpClient) {
this->httpClient = httpClient;
}
void AWSClient4::setDateTimeProvider(IDateTimeProvider* dateTimeProvider) {
this->dateTimeProvider = dateTimeProvider;
}

AWSClient4::~AWSClient4() {
if (awsRegion != 0)
delete[] awsRegion;
if (awsEndpoint != 0)
delete[] awsEndpoint;
if (awsSecKey != 0)
delete[] awsSecKey;
if (awsKeyID != 0)
delete[] awsKeyID;
}


char* AWSClient4::createCanonicalHeaders() {
// headers, alphabetically sorted, lowercase, eg: key:value
// content-type:x
// host:host
// x-amz-content-sha256:hash
// x-amz-date:date
char canonical_headers[500] = "";
sprintf(canonical_headers, "%scontent-type:%s\n", canonical_headers, contentType);
sprintf(canonical_headers, "%shost:%s\n", canonical_headers, awsDomain);
// sprintf(canonical_headers, "%srange:bytes=0-9\n", canonical_headers); // s3
sprintf(canonical_headers, "%sx-amz-content-sha256:%s\n", canonical_headers, payloadHash);
sprintf(canonical_headers, "%sx-amz-date:%sT%sZ\n\n", canonical_headers, awsDate, awsTime);
return canonical_headers;
}

char* AWSClient4::createRequestHeaders(char* signature) {
char headers[1000] = "";
sprintf(headers, "%sContent-Type: %s\r\n", headers, contentType);
sprintf(headers, "%sConnection: close\r\n", headers);
sprintf(headers, "%sContent-Length: %d\r\n", headers, strlen(payload.getCStr()));
sprintf(headers, "%sHost: %s\r\n", headers, awsDomain);
sprintf(headers, "%sx-amz-content-sha256: %s\r\n", headers, payloadHash);
sprintf(headers, "%sx-amz-date: %sT%sZ\r\n", headers, awsDate, awsTime);
sprintf(headers, "%sAuthorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request,SignedHeaders=%s,Signature=%s\r\n", headers, awsKeyID, awsDate, awsRegion, awsService, signedHeaders, signature);
return headers;
}

char* AWSClient4::createStringToSign(char* canonical_request) {
// return canonical_request;
SHA256* sha256 = new SHA256();
char* hashed = (*sha256)(canonical_request, strlen(canonical_request));
delete sha256;
// return canonical_request;

char string_to_sign[700] = "";
sprintf(string_to_sign, "%sAWS4-HMAC-SHA256\n", string_to_sign);
sprintf(string_to_sign, "%s%sT%sZ\n", string_to_sign, awsDate, awsTime);
sprintf(string_to_sign, "%s%s/%s/%s/aws4_request\n", string_to_sign, awsDate, awsRegion, awsService);

sprintf(string_to_sign, "%s%s", string_to_sign, hashed);

return string_to_sign;
}

char* AWSClient4::createCanonicalRequest() {
char canonical_request[800] = "";
sprintf(canonical_request, "%s%s\n", canonical_request, method); // VERB
sprintf(canonical_request, "%s%s\n", canonical_request, awsPath); // URI
sprintf(canonical_request, "%s%s\n", canonical_request, queryString); // queryString

char* headers = createCanonicalHeaders();

sprintf(canonical_request, "%s%s", canonical_request, headers); // headers
sprintf(canonical_request, "%s%s\n", canonical_request, signedHeaders); // signed_headers
sprintf(canonical_request, "%s%s", canonical_request, payloadHash); // payload

return canonical_request;
}


char* AWSClient4::createSignature(const char* toSign) {

/* Allocate memory for the signature */
char* signature = new char[HASH_HEX_LEN4 + 1]();

/* Create the signature key */
/* + 4 for "AWS4" */
int keyLen = strlen(awsSecKey) + 4;
char* key = new char[keyLen + 1]();
sprintf(key, "AWS4%s", awsSecKey);

/* repeatedly apply hmac with the appropriate values. See
* http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
* for algorithm. */
char* k1 = hmacSha256(key, keyLen, awsDate, strlen(awsDate));
delete[] key;
char* k2 = hmacSha256(k1, SHA256_DEC_HASH_LEN, awsRegion,
strlen(awsRegion));
delete[] k1;
char* k3 = hmacSha256(k2, SHA256_DEC_HASH_LEN, awsService,
strlen(awsService));
delete[] k2;
char* k4 = hmacSha256(k3, SHA256_DEC_HASH_LEN, "aws4_request", 12);
delete[] k3;
char* k5 = hmacSha256(k4, SHA256_DEC_HASH_LEN, toSign, strlen(toSign));
delete[] k4;

/* Convert the chars in hash to hex for signature. */
for (int i = 0; i < SHA256_DEC_HASH_LEN; ++i) {
sprintf(signature + 2 * i, "%02lx", 0xff & (unsigned long) k5[i]);
}
delete[] k5;
return signature;
}


char* AWSClient4::createRequest(MinimalString &reqPayload) {
/* Check that all values have been initialized. */
if (awsRegion == 0 || awsEndpoint == 0 || awsSecKey == 0 || awsKeyID == 0
|| httpClient == 0 || dateTimeProvider == 0)
return 0;

// set date and time
// @TODO: find out why sprintf doesn't work
const char* dateTime = dateTimeProvider->getDateTime();
strncpy(awsDate, dateTime, 8);
awsDate[9] = '\0';
strncpy(awsTime, dateTime + 8, 6);
awsTime[7] = '\0';

SHA256* sha256 = new SHA256();
payloadHash = (*sha256)(reqPayload.getCStr(), reqPayload.length());
delete sha256;

payload = reqPayload;

// create the canonical request, we need to copy the results
// @TODO: figure out why the reference doesn't work
char *canonical_request_return = createCanonicalRequest();
char canonical_request[1000];
strcpy(canonical_request, canonical_request_return);
// return canonical_request;

// create the signing string, we need to copy the results
// @TODO: figure out why the reference doesn't work
char *return_string_to_sign = createStringToSign(canonical_request);
char string_to_sign[500];
strcpy(string_to_sign, return_string_to_sign);

// create the signature
char *signature = createSignature(string_to_sign);

// create the headers
char *headers = createRequestHeaders(signature);

// get the host/domain
// char *host = createHost();

// create the request with all the vars
char* request = new char[strlen(method) + strlen(awsDomain) + strlen(awsPath) + strlen(headers) + strlen(reqPayload.getCStr()) + 16]();
sprintf(request, "%s %s HTTP/1.1\r\n%s\r\n%s\r\n\r\n", method, awsPath, headers, reqPayload.getCStr());

return request;
}

char* AWSClient4::sendData(const char* data) {
// char* server = createHost();
int port = httpS ? 443 : 80;
char* response = httpClient->send(data, awsDomain, port);
// delete[] server;
return response;
}
Loading