Skip to content

Commit 080cf51

Browse files
committed
Sort params by key before calculating the OAuth signature
1 parent 01ead04 commit 080cf51

File tree

2 files changed

+135
-30
lines changed

2 files changed

+135
-30
lines changed

src/OAuthClient.cpp

Lines changed: 134 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -191,15 +191,35 @@ int OAuthClient::httpRequest(const char* method, const char* path, const char* c
191191
url += _ip[3];
192192
}
193193

194-
url += path;
194+
const char* queryParams = NULL;
195+
const char* bodyParams = NULL;
196+
197+
// split the path from query params if necessary
198+
char* questionMark = strchr(path, '?');
199+
if (questionMark != NULL) {
200+
queryParams = (questionMark + 1);
201+
202+
const char* temp = path;
203+
204+
while (temp != questionMark) {
205+
url += *temp++;
206+
}
207+
} else {
208+
url += path;
209+
}
210+
211+
if (strcmp(contentType, "application/x-www-form-urlencoded") == 0) {
212+
// only use the body as params if the body is URL encoded
213+
bodyParams = body;
214+
}
195215

196216
unsigned long time = 0;
197217

198218
if (_onGetTimeCallback) {
199219
time = _onGetTimeCallback();
200220
}
201221

202-
String signature = calculateSignature(method, url.c_str(), time, body);
222+
String signature = calculateSignature(method, url.c_str(), time, queryParams, bodyParams);
203223
String authorization = calculateOauthAuthorization(signature, time);
204224

205225
_httpClient.beginRequest();
@@ -237,29 +257,120 @@ String OAuthClient::createNonce() {
237257
return n;
238258
}
239259

240-
String OAuthClient::calculateSignature(const char* method, const char* url, unsigned long time, const char* body)
260+
static int strcmp_pointer(const void* a, const void* b) {
261+
return strcmp(*(const char**)a, *(const char**)b);
262+
}
263+
264+
String OAuthClient::calculateSignature(const char* method, const char* url, unsigned long time, const char* queryParams, const char* bodyParams)
241265
{
266+
// This function is long due to the complexity of the OAuth signature.
267+
// It must collect all the parameters from the oauth, query, and body params,
268+
// then sort the param key values lexographically. After these steps the
269+
// signature can be calculated.
270+
271+
// calculate the OAuth params
272+
String oauthParams;
273+
274+
oauthParams += "oauth_consumer_key=";
275+
oauthParams += _consumerKey;
276+
oauthParams += "&oauth_nonce=";
277+
oauthParams += _nonce;
278+
oauthParams += "&oauth_signature_method=HMAC-SHA1&oauth_timestamp=";
279+
oauthParams += String(time);
280+
oauthParams += "&oauth_token=";
281+
oauthParams += _accessToken;
282+
oauthParams += "&oauth_version=1.0";
283+
284+
// calculate the length of all of the params
285+
int paramsLength = oauthParams.length();
286+
int queryParamsLength = strlen(queryParams);
287+
int bodyParamsLength = strlen(bodyParams);
288+
289+
if (queryParams) {
290+
paramsLength += (1 + queryParamsLength);
291+
}
292+
293+
if (bodyParams) {
294+
paramsLength += (1 + bodyParamsLength);
295+
}
296+
297+
// copy the parameters to a buffer
298+
char params[paramsLength + 1];
299+
char* temp = params;
300+
301+
temp = strcpy(temp, oauthParams.c_str());
302+
temp += oauthParams.length();
303+
304+
if (queryParams) {
305+
*temp++ = '&';
306+
strcpy(temp, queryParams);
307+
temp += queryParamsLength;
308+
}
309+
310+
if (bodyParams) {
311+
*temp++ = '&';
312+
strcpy(temp, bodyParams);
313+
temp += bodyParamsLength;
314+
}
315+
316+
*temp = '\0';
317+
318+
// caculate the number of parameters
319+
int numParams = 0;
320+
for (int i = 0; i < paramsLength; i++) {
321+
if (params[i] == '=') {
322+
numParams++;
323+
}
324+
}
325+
326+
// collect the keys of the parameters to an array
327+
// and also replace the = and & characters with \0
328+
// this will help with the sorting later
329+
const char* paramKeys[numParams];
330+
int paramIndex = 0;
331+
const char* lastKey = params;
332+
333+
temp = params;
334+
while (1) {
335+
char c = *temp;
336+
337+
if (c == '\0') {
338+
break;
339+
} else if (c == '=') {
340+
paramKeys[paramIndex++] = lastKey;
341+
342+
*temp = '\0';
343+
} else if (c == '&') {
344+
lastKey = (temp + 1);
345+
346+
*temp = '\0';
347+
}
348+
349+
temp++;
350+
}
351+
352+
// sort the param keys
353+
qsort(paramKeys, numParams, sizeof(uintptr_t), strcmp_pointer);
354+
355+
// calculate the signature
242356
SHA1.beginHmac(_signingKey);
243357
SHA1.print(method);
244358
SHA1.print("&");
245359
SHA1.print(URLEncoder.encode(url));
246360
SHA1.print("&");
361+
for (int i = 0; i < numParams; i++) {
362+
const char* paramKey = paramKeys[i];
363+
int keyLength = strlen(paramKey);
364+
const char* paramValue = paramKey + keyLength + 1;
365+
366+
SHA1.print(URLEncoder.encode(paramKey));
367+
SHA1.print(URLEncoder.encode("="));
368+
SHA1.print(URLEncoder.encode(paramValue));
247369

248-
SHA1.print(URLEncoder.encode("oauth_consumer_key="));
249-
SHA1.print(URLEncoder.encode(_consumerKey));
250-
SHA1.print(URLEncoder.encode("&"));
251-
SHA1.print(URLEncoder.encode("oauth_nonce="));
252-
SHA1.print(URLEncoder.encode(_nonce));
253-
SHA1.print(URLEncoder.encode("&"));
254-
SHA1.print(URLEncoder.encode("oauth_signature_method=HMAC-SHA1&"));
255-
SHA1.print(URLEncoder.encode("oauth_timestamp="));
256-
SHA1.print(URLEncoder.encode(String(time)));
257-
SHA1.print(URLEncoder.encode("&"));
258-
SHA1.print(URLEncoder.encode("oauth_token="));
259-
SHA1.print(URLEncoder.encode(_accessToken));
260-
SHA1.print(URLEncoder.encode("&"));
261-
SHA1.print(URLEncoder.encode("oauth_version=1.0&"));
262-
SHA1.print(URLEncoder.encode(body));
370+
if ((i + 1) < numParams) {
371+
SHA1.print(URLEncoder.encode("&"));
372+
}
373+
}
263374
SHA1.endHmac();
264375

265376
int rawSignatureLength = SHA1.available();
@@ -283,21 +394,15 @@ String OAuthClient::calculateOauthAuthorization(const String& signature, unsigne
283394
authorization += "OAuth ";
284395
authorization += "oauth_consumer_key=\"";
285396
authorization += _consumerKey;
286-
authorization += "\",";
287-
authorization += "oauth_nonce=\"";
397+
authorization += "\",oauth_nonce=\"";
288398
authorization += _nonce;
289-
authorization += "\",";
290-
authorization += "oauth_signature=\"";
399+
authorization += "\",oauth_signature=\"";
291400
authorization += signature;
292-
authorization += "\",";
293-
authorization += "oauth_signature_method=\"HMAC-SHA1\",";
294-
authorization += "oauth_timestamp=\"";
401+
authorization += "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"";
295402
authorization += timestamp;
296-
authorization += "\",";
297-
authorization += "oauth_token=\"";
403+
authorization += "\",oauth_token=\"";
298404
authorization += _accessToken;
299-
authorization += "\",";
300-
authorization += "oauth_version=\"1.0\"";
405+
authorization += "\",oauth_version=\"1.0\"";
301406

302407
return authorization;
303408
}

src/OAuthClient.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ class OAuthClient {
6464
int httpRequest(const char* method, const char* path, const char* contentType, const char* body);
6565

6666
String createNonce();
67-
String calculateSignature(const char* method, const char* url, unsigned long time, const char* body);
67+
String calculateSignature(const char* method, const char* url, unsigned long time, const char* queryParams, const char* bodyParams);
6868
String calculateOauthAuthorization(const String& signature, unsigned long timestamp);
6969

7070
private:

0 commit comments

Comments
 (0)