2
2
* AWSClient.cpp
3
3
*
4
4
* See AWSClient.h for description.
5
+ * See http://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-header-based-auth.html
5
6
*
6
7
*/
7
8
13
14
#include < string.h>
14
15
#include < stdlib.h>
15
16
#include < stdio.h>
16
- #include < map>
17
17
18
18
//
19
19
// /* Constants string, formats, and lengths. */
@@ -54,6 +54,8 @@ AWSClient4::AWSClient4() {
54
54
awsKeyID = 0 ;
55
55
httpClient = 0 ;
56
56
dateTimeProvider = 0 ;
57
+ method = 0 ;
58
+ uri = 0 ;
57
59
}
58
60
59
61
void AWSClient4::setAWSRegion (const char * awsRegion) {
@@ -92,235 +94,111 @@ void AWSClient4::setHttpClient(IHttpClient* httpClient) {
92
94
void AWSClient4::setDateTimeProvider (IDateTimeProvider* dateTimeProvider) {
93
95
this ->dateTimeProvider = dateTimeProvider;
94
96
}
95
- //
96
- // AWSClient4::~AWSClient4() {
97
- // if (awsRegion != 0)
98
- // delete[] awsRegion;
99
- // if (awsEndpoint != 0)
100
- // delete[] awsEndpoint;
101
- // if (awsSecKey != 0)
102
- // delete[] awsSecKey;
103
- // if (awsKeyID != 0)
104
- // delete[] awsKeyID;
105
- // }
106
- //
107
- // void AWSClient4::initSignedHeaders() {
108
- // /* For each of the formats for unsigned headers, determine the size of the
109
- // * formatted string, allocate that much space in the next available element
110
- // * in the headers array, create the string, and add it's length to the
111
- // * headerLens array. */
112
- //
113
- // int contentLen = payload.length();
114
- // int len = CONTENT_LENGTH_HEADER_LEN + digitCount(contentLen);
115
- // headers[headersCreated] = new char[len + 1]();
116
- // sprintf(headers[headersCreated], CONTENT_LENGTH_HEADER, contentLen);
117
- // headerLens[headersCreated++] = len;
118
- //
119
- // len = CONTENT_TYPE_HEADER_LEN + strlen(contentType);
120
- // headers[headersCreated] = new char[len + 1]();
121
- // sprintf(headers[headersCreated], CONTENT_TYPE_HEADER, contentType);
122
- // headerLens[headersCreated++] = len;
123
- //
124
- // char* host = createHostString();
125
- // len = HOST_HEADER_LEN + strlen(host);
126
- // headers[headersCreated] = new char[len + 1]();
127
- //
128
- // sprintf(headers[headersCreated], HOST_HEADER, host);
129
- // headerLens[headersCreated++] = len;
130
- //
131
- // len = X_AMZ_DATE_HEADER_LEN + AWS_DATE_LEN2 + AWS_TIME_LEN2;
132
- // headers[headersCreated] = new char[len + 1]();
133
- // sprintf(headers[headersCreated], X_AMZ_DATE_HEADER, awsDate, awsTime);
134
- // headerLens[headersCreated++] = len;
135
- // }
136
- //
137
- // char* AWSClient4::createHostString(void) {
138
- // if(awsDomain[0] != '\0') {
139
- // return awsDomain;
140
- // } else {
141
- // char* host = new char[200]();
142
- // sprintf(host, "%s.%s.%s", awsService, awsRegion, awsEndpoint);
143
- // return host;
144
- // }
145
- // }
146
- //
147
- // char* AWSClient4::createStringToSign(void) {
148
- // SHA256* sha256 = new SHA256();
149
- // char* hashed;
150
- // /* Calculate length of canonicalForm string. */
151
- // int canonicalFormLen = CANONICAL_FORM_POST_LINE_LEN;
152
- // for (int i = 0; i < headersCreated; i++) {
153
- // /* +1 for newlines */
154
- // canonicalFormLen += *(headerLens + i) + 1;
155
- // }
156
- // /* +2 for newlines. */
157
- // canonicalFormLen += SIGNED_HEADERS_LEN + HASH_HEX_LEN2 + 2;
158
- //
159
- // char* canonicalForm = new char[canonicalFormLen + 1]();
160
- //
161
- // /* Write the cannonicalForm string. */
162
- // int canonicalFormWritten = 0;
163
- // canonicalFormWritten += strlen(
164
- // strcpy(canonicalForm + canonicalFormWritten,
165
- // CANONICAL_FORM_POST_LINE));
166
- // for (int i = 0; i < headersCreated; i++) {
167
- // canonicalFormWritten += sprintf(canonicalForm + canonicalFormWritten,
168
- // "%s\n", *(headers + i));
169
- // }
170
- // canonicalFormWritten += sprintf(canonicalForm + canonicalFormWritten,
171
- // "\n%s\n", SIGNED_HEADERS);
172
- // hashed = (*sha256)(payload.getCStr(), payload.length());
173
- // strcpy(canonicalForm + canonicalFormWritten, hashed);
174
- // delete[] hashed;
175
- // canonicalFormWritten += HASH_HEX_LEN2;
176
- //
177
- // /* Hash the canonicalForm string. */
178
- // hashed = (*sha256)(canonicalForm, canonicalFormWritten);
179
- // delete sha256;
180
- //
181
- // delete[] canonicalForm;
182
- //
183
- // /* Determine the size to the string to sign. */
184
- // int toSignLen = TO_SIGN_TEMPLATE_LEN + 2 * AWS_DATE_LEN2 + AWS_TIME_LEN2
185
- // + strlen(awsRegion) + strlen(awsService) + HASH_HEX_LEN2;
186
- //
187
- // /* Create and return the string to sign. */
188
- // char* toSign = new char[toSignLen + 1]();
189
- // sprintf(toSign, TO_SIGN_TEMPLATE, awsDate, awsTime, awsDate, awsRegion,
190
- // awsService, hashed);
191
- // delete[] hashed;
192
- // return toSign;
193
- //
194
- // }
195
- // char* AWSClient4::createSignature(const char* toSign) {
196
- //
197
- // /* Allocate memory for the signature */
198
- // char* signature = new char[HASH_HEX_LEN2 + 1]();
199
- //
200
- // /* Create the signature key */
201
- // /* + 4 for "AWS4" */
202
- // int keyLen = strlen(awsSecKey) + 4;
203
- // char* key = new char[keyLen + 1]();
204
- // sprintf(key, "AWS4%s", awsSecKey);
205
- //
206
- // /* repeatedly apply hmac with the appropriate values. See
207
- // * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
208
- // * for algorithm. */
209
- // char* k1 = hmacSha256(key, keyLen, awsDate, strlen(awsDate));
210
- // delete[] key;
211
- // char* k2 = hmacSha256(k1, SHA256_DEC_HASH_LEN, awsRegion,
212
- // strlen(awsRegion));
213
- // delete[] k1;
214
- // char* k3 = hmacSha256(k2, SHA256_DEC_HASH_LEN, awsService,
215
- // strlen(awsService));
216
- // delete[] k2;
217
- // char* k4 = hmacSha256(k3, SHA256_DEC_HASH_LEN, "aws4_request", 12);
218
- // delete[] k3;
219
- // char* k5 = hmacSha256(k4, SHA256_DEC_HASH_LEN, toSign, strlen(toSign));
220
- // delete[] k4;
221
- //
222
- // /* Convert the chars in hash to hex for signature. */
223
- // for (int i = 0; i < SHA256_DEC_HASH_LEN; ++i) {
224
- // sprintf(signature + 2 * i, "%02lx", 0xff & (unsigned long) k5[i]);
225
- // }
226
- // delete[] k5;
227
- // return signature;
228
- // }
229
- //
230
- // void AWSClient4::initUnsignedHeaders(const char* signature) {
231
- // int len = AUTHORIZATION_HEADER_LEN + strlen(awsKeyID) + AWS_DATE_LEN2
232
- // + strlen(awsRegion) + strlen(awsService) + SIGNED_HEADERS_LEN
233
- // + HASH_HEX_LEN2;
234
- // headers[headersCreated] = new char[len + 1]();
235
- // sprintf(headers[headersCreated], AUTHORIZATION_HEADER, awsKeyID, awsDate,
236
- // awsRegion, awsService, SIGNED_HEADERS, signature);
237
- // headerLens[headersCreated++] = len;
238
- // /*
239
- // len = CONNECTION_HEADER_LEN;
240
- // headers[headersCreated] = new char[len + 1]();
241
- // strcpy(headers[headersCreated], CONNECTION_HEADER);
242
- // headerLens[headersCreated++] = len;
243
- // */
244
- // }
245
- //
246
- // void AWSClient4::createRequestInit(MinimalString &reqPayload) {
247
- // //initialize object-scoped variables
248
- // const char* dateTime = dateTimeProvider->getDateTime();
249
- //
250
- // // @TODO: not sure yet why sprintf doesn't work here
251
- // strncpy(awsDate, dateTime, 8);
252
- // awsDate[9] = '\0';
253
- // strncpy(awsTime, dateTime + 8, 6);
254
- // awsTime[7] = '\0';
255
- //
256
- // Serial.println(awsDate);
257
- // Serial.println(awsTime);
258
- //
259
- // payload = reqPayload;
260
- // headersCreated = 0;
261
- //
262
- // //Create signature and headers
263
- // initSignedHeaders();
264
- // char* toSign = createStringToSign();
265
- // char* signature = createSignature(toSign);
266
- // delete[] toSign;
267
- // initUnsignedHeaders(signature);
268
- // delete[] signature;
269
- // }
270
- //
271
- // void AWSClient4::createRequestCleanup() {
272
- // /* Free each header */
273
- // for (int i = 0; i < headersCreated; i++) {
274
- // delete[] headers[i];
275
- // }
276
- // }
277
- //
278
- // char* AWSClient4::headersToRequest() {
279
- // /* Determine whether to use https or http postLine values. */
280
- // int postLineLen =
281
- // httpS ? HTTPS_REQUEST_POST_LINE_LEN : HTTP_REQUEST_POST_LINE_LEN;
282
- // const char* postLine =
283
- // httpS ? HTTPS_REQUEST_POST_LINE : HTTP_REQUEST_POST_LINE;
284
- //
285
- // /* Calculate length of httpRequest string. */
286
- // char* host = createHostString();
287
- // int httpRequestLen;
288
- //
289
- // // if a path is set, append it to the post header
290
- // if(awsPath[0] != '\0') {
291
- // httpRequestLen = postLineLen + strlen(host) + strlen(awsPath);
292
- // } else {
293
- // httpRequestLen = postLineLen + strlen(host);
294
- // }
295
- //
296
- // for (int i = 0; i < headersCreated; i++) {
297
- // /* +1 for newline. */
298
- // httpRequestLen += *(headerLens + i) + 1;
299
- // }
300
- // /* +1 for newline. */
301
- // httpRequestLen += payload.length() + 1;
302
- //
303
- // /* Create and write to the httpRequest string. */
304
- // char* httpRequest = new char[httpRequestLen + 1]();
305
- // int httpRequestWritten = 0;
306
- //
307
- // // if a path is set, append it to the post header
308
- // if(awsPath[0] != '\0') {
309
- // httpRequestWritten += sprintf(httpRequest + httpRequestWritten, postLine, host, awsPath);
310
- // } else {
311
- // httpRequestWritten += sprintf(httpRequest + httpRequestWritten, postLine, host);
312
- // }
313
- // for (int i = 0; i < headersCreated; i++) {
314
- // httpRequestWritten += sprintf(httpRequest + httpRequestWritten, "%s\n",
315
- // *(headers + i));
316
- // }
317
- // httpRequestWritten += sprintf(httpRequest + httpRequestWritten, "\n%s",
318
- // payload.getCStr());
319
- //
320
- // return httpRequest;
321
- // }
322
97
98
+ AWSClient4::~AWSClient4 () {
99
+ if (awsRegion != 0 )
100
+ delete[] awsRegion;
101
+ if (awsEndpoint != 0 )
102
+ delete[] awsEndpoint;
103
+ if (awsSecKey != 0 )
104
+ delete[] awsSecKey;
105
+ if (awsKeyID != 0 )
106
+ delete[] awsKeyID;
107
+ }
108
+
109
+ char * AWSClient4::createHost () {
110
+ // return "example.com";
111
+ return awsDomain;
112
+ }
113
+
114
+ char * AWSClient4::createCanonicalHeaders () {
115
+ // headers, alphabetically sorted, lowercase, eg: key:value
116
+ // content-type:x
117
+ // host:host
118
+ // x-amz-content-sha256:hash
119
+ // x-amz-date:date
120
+ char canonical_headers[500 ] = " " ;
121
+ sprintf (canonical_headers, " %scontent-type:%s\n " , canonical_headers, contentType);
122
+ sprintf (canonical_headers, " %shost:%s\n " , canonical_headers, createHost ());
123
+ sprintf (canonical_headers, " %sx-amz-content-sha256:%s\n " , canonical_headers, payloadHash);
124
+ sprintf (canonical_headers, " %sx-amz-date:%sT%sZ\n " , canonical_headers, awsDate, awsTime);
125
+ return canonical_headers;
126
+ }
127
+
128
+ char * AWSClient4::createRequestHeaders (char * signature) {
129
+ char headers[1000 ] = " " ;
130
+ sprintf (headers, " %sContent-Type: %s\n " , headers, contentType);
131
+ sprintf (headers, " %sHost: %s\n " , headers, createHost ());
132
+ sprintf (headers, " %sx-amz-content-sha256: %s\n " , headers, payloadHash);
133
+ sprintf (headers, " %sx-amz-date: %sT%sZ\n " , headers, awsDate, awsTime);
134
+ sprintf (headers, " %sAuthorization: AWS4-HMAC-SHA256 Credential=%s/%s/%s/%s/aws4_request,SignedHeaders=%s,Signature=%s\n " , headers, awsKeyID, awsDate, awsRegion, awsService, signedHeaders, signature);
135
+ return headers;
136
+ }
137
+
138
+ char * AWSClient4::createStringToSign (char * canonical_request) {
139
+ char string_to_sign[700 ] = " AWS4-HMAC-SHA256\n " ;
140
+ sprintf (string_to_sign, " %s%sT%sZ\n " , canonical_request, awsDate, awsTime);
141
+ sprintf (string_to_sign, " %s%s/%s/%s/aws4_request\n " , canonical_request, awsDate, awsRegion, awsService);
142
+
143
+ SHA256* sha256 = new SHA256 ();
144
+ char * hashed = (*sha256)(canonical_request, strlen (canonical_request));
145
+ delete sha256;
146
+
147
+ sprintf (string_to_sign, " %s%s" , canonical_request, hashed);
148
+
149
+ return string_to_sign;
150
+ }
151
+
152
+ char * AWSClient4::createCanonicalRequest () {
153
+ char canonical_request[700 ] = " " ;
154
+ sprintf (canonical_request, " %s%s\n " , canonical_request, method); // VERB
155
+ sprintf (canonical_request, " %s%s\n " , canonical_request, uri); // URI
156
+ sprintf (canonical_request, " %s%s\n " , canonical_request, queryString); // queryString
323
157
158
+ char * headers = createCanonicalHeaders ();
159
+
160
+ sprintf (canonical_request, " %s%s" , canonical_request, headers); // headers
161
+ sprintf (canonical_request, " %s%s\n " , canonical_request, signedHeaders); // signed_headers
162
+ sprintf (canonical_request, " %s%s\n " , canonical_request, payload.getCStr ()); // payload
163
+
164
+ return canonical_request;
165
+ }
166
+
167
+
168
+ char * AWSClient4::createSignature (const char * toSign) {
169
+
170
+ /* Allocate memory for the signature */
171
+ char * signature = new char [HASH_HEX_LEN4 + 1 ]();
172
+
173
+ /* Create the signature key */
174
+ /* + 4 for "AWS4" */
175
+ int keyLen = strlen (awsSecKey) + 4 ;
176
+ char * key = new char [keyLen + 1 ]();
177
+ sprintf (key, " AWS4%s" , awsSecKey);
178
+
179
+ /* repeatedly apply hmac with the appropriate values. See
180
+ * http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
181
+ * for algorithm. */
182
+ char * k1 = hmacSha256 (key, keyLen, awsDate, strlen (awsDate));
183
+ delete[] key;
184
+ char * k2 = hmacSha256 (k1, SHA256_DEC_HASH_LEN, awsRegion,
185
+ strlen (awsRegion));
186
+ delete[] k1;
187
+ char * k3 = hmacSha256 (k2, SHA256_DEC_HASH_LEN, awsService,
188
+ strlen (awsService));
189
+ delete[] k2;
190
+ char * k4 = hmacSha256 (k3, SHA256_DEC_HASH_LEN, " aws4_request" , 12 );
191
+ delete[] k3;
192
+ char * k5 = hmacSha256 (k4, SHA256_DEC_HASH_LEN, toSign, strlen (toSign));
193
+ delete[] k4;
194
+
195
+ /* Convert the chars in hash to hex for signature. */
196
+ for (int i = 0 ; i < SHA256_DEC_HASH_LEN; ++i) {
197
+ sprintf (signature + 2 * i, " %02lx" , 0xff & (unsigned long ) k5[i]);
198
+ }
199
+ delete[] k5;
200
+ return signature;
201
+ }
324
202
325
203
326
204
char * AWSClient4::createRequest (MinimalString &reqPayload) {
@@ -329,10 +207,31 @@ char* AWSClient4::createRequest(MinimalString &reqPayload) {
329
207
|| httpClient == 0 || dateTimeProvider == 0 )
330
208
return 0 ;
331
209
332
- map<string,string> headers;
333
- headers[' host' ] = " bar" ;
210
+ // set date and time
211
+ // @TODO: find out why sprintf doesn't work
212
+ const char * dateTime = dateTimeProvider->getDateTime ();
213
+ strncpy (awsDate, dateTime, 8 );
214
+ awsDate[9 ] = ' \0 ' ;
215
+ strncpy (awsTime, dateTime + 8 , 6 );
216
+ awsTime[7 ] = ' \0 ' ;
217
+
218
+ SHA256* sha256 = new SHA256 ();
219
+ payloadHash = (*sha256)(reqPayload.getCStr (), reqPayload.length ());
220
+ delete sha256;
221
+
222
+ payload = reqPayload;
223
+
224
+ char *canonical_request = createCanonicalRequest ();
225
+ char *string_to_sign = createStringToSign (canonical_request);
226
+ char *signature = createSignature (string_to_sign);
227
+
228
+ char *headers = createRequestHeaders (signature);
229
+
230
+ char *host = createHost ();
231
+ char * request = new char [strlen (method) + strlen (host) + strlen (awsPath) + strlen (headers) + strlen (reqPayload.getCStr ()) + 4 ]();
232
+ sprintf (request, " %s %s%s\n %s\n %s" , method, createHost (), awsPath, headers, reqPayload.getCStr ());
334
233
335
- return headers[ ' host ' ] ;
234
+ return request ;
336
235
337
236
// createRequestInit(reqPayload);
338
237
// char* request = headersToRequest();
0 commit comments