Created
July 6, 2022 20:14
-
-
Save witnessmenow/d6f3c4d92c0585df9464756bd41d9f1e to your computer and use it in GitHub Desktop.
OauthSignature ESP32 Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "mbedtls/md.h" | |
#include "mbedtls/base64.h" | |
#include <UrlEncode.h> | |
String createNonce() { | |
String n; | |
n.reserve(32); | |
while (n.length() < 32) { | |
char c = random(255); | |
if (isAlphaNumeric(c)) { | |
n += c; | |
} | |
} | |
return n; | |
} | |
const char* _consumerKey = "Meow1"; | |
const char* _consumerSecret = "Woof"; | |
const char* _accessToken = "Moo"; | |
const char* _accessTokenSecret = "Baa"; | |
String _nonce = createNonce(); | |
String _signingKey; | |
void setSigningKey(){ | |
_signingKey.reserve(strlen(_consumerSecret) + strlen(_accessTokenSecret) + 1); | |
_signingKey += _consumerSecret; | |
_signingKey += "&"; | |
_signingKey += _accessTokenSecret; | |
} | |
static int strcmp_pointer(const void* a, const void* b) { | |
return strcmp(*(const char**)a, *(const char**)b); | |
} | |
String calculateSignature(const char* method, const char* url, unsigned long time, const char* queryParams, const char* bodyParams) | |
{ | |
// This function is long due to the complexity of the OAuth signature. | |
// It must collect all the parameters from the oauth, query, and body params, | |
// then sort the param key values lexicographically. After these steps the | |
// signature can be calculated. | |
// calculate the OAuth params | |
String oauthParams; | |
oauthParams += "oauth_consumer_key="; | |
oauthParams += _consumerKey; | |
oauthParams += "&oauth_nonce="; | |
oauthParams += _nonce; | |
oauthParams += "&oauth_signature_method=HMAC-SHA1&oauth_timestamp="; | |
oauthParams += String(time); | |
oauthParams += "&oauth_token="; | |
oauthParams += _accessToken; | |
oauthParams += "&oauth_version=1.0"; | |
// calculate the length of all of the params | |
int paramsLength = oauthParams.length(); | |
int queryParamsLength = strlen(queryParams); | |
int bodyParamsLength = strlen(bodyParams); | |
if (queryParams) { | |
paramsLength += (1 + queryParamsLength); | |
} | |
if (bodyParams) { | |
paramsLength += (1 + bodyParamsLength); | |
} | |
// copy the parameters to a buffer | |
char params[paramsLength + 1]; | |
char* temp = params; | |
temp = strcpy(temp, oauthParams.c_str()); | |
temp += oauthParams.length(); | |
if (queryParams) { | |
*temp++ = '&'; | |
strcpy(temp, queryParams); | |
temp += queryParamsLength; | |
} | |
if (bodyParams) { | |
*temp++ = '&'; | |
strcpy(temp, bodyParams); | |
temp += bodyParamsLength; | |
} | |
*temp = '\0'; | |
// calculate the number of parameters | |
int numParams = 0; | |
for (int i = 0; i < paramsLength; i++) { | |
if (params[i] == '=') { | |
numParams++; | |
} | |
} | |
// collect the keys of the parameters to an array | |
// and also replace the = and & characters with \0 | |
// this will help with the sorting later | |
const char* paramKeys[numParams]; | |
int paramIndex = 0; | |
const char* lastKey = params; | |
temp = params; | |
while (1) { | |
char c = *temp; | |
if (c == '\0') { | |
break; | |
} else if (c == '=') { | |
paramKeys[paramIndex++] = lastKey; | |
*temp = '\0'; | |
} else if (c == '&') { | |
lastKey = (temp + 1); | |
*temp = '\0'; | |
} | |
temp++; | |
} | |
// sort the param keys | |
qsort(paramKeys, numParams, sizeof(uintptr_t), strcmp_pointer); | |
String payload = String(method); | |
payload += "&"; | |
payload += urlEncode(url); | |
payload += "&"; | |
for (int i = 0; i < numParams; i++) { | |
const char* paramKey = paramKeys[i]; | |
int keyLength = strlen(paramKey); | |
const char* paramValue = paramKey + keyLength + 1; | |
payload += urlEncode(paramKey); | |
payload += "%3D"; //urlEncode("=") | |
payload += urlEncode(paramValue); | |
if ((i + 1) < numParams) { | |
payload += "%26"; //urlEncode("&") | |
} | |
} | |
byte shaResult[20]; | |
Serial.println("payload:"); | |
Serial.println(payload.c_str()); | |
mbedtls_md_context_t ctx; | |
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA1; | |
const size_t payloadLength = strlen(payload.c_str()); | |
const size_t keyLength = strlen(_signingKey.c_str()); | |
Serial.println("_signingKey:"); | |
Serial.println(_signingKey.c_str()); | |
mbedtls_md_init(&ctx); | |
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); | |
mbedtls_md_hmac_starts(&ctx, (const unsigned char *) _signingKey.c_str(), keyLength); | |
mbedtls_md_hmac_update(&ctx, (const unsigned char *) payload.c_str(), payloadLength); | |
mbedtls_md_hmac_finish(&ctx, shaResult); | |
mbedtls_md_free(&ctx); | |
//Serial.print("Raw: "); | |
//Serial.println(shaResult); | |
Serial.print("Hash: "); | |
for (int i = 0; i < sizeof(shaResult); i++) { | |
char str[3]; | |
sprintf(str, "%02x", (int)shaResult[i]); | |
Serial.print(str); | |
} | |
int rawSignatureLength = sizeof(shaResult); | |
Serial.println(""); | |
Serial.print("rawSignatureLength: "); | |
Serial.println(rawSignatureLength); | |
//int signatureLength = (rawSignatureLength * 8) / 6; | |
int signatureLength = 50; | |
char signature[signatureLength + 1]; | |
size_t outlen; | |
int result = mbedtls_base64_encode((unsigned char*)signature, signatureLength, &outlen, shaResult, rawSignatureLength); | |
if(result == 0){ | |
signature[outlen + 1] = '\0'; | |
Serial.println(urlEncode(signature)); | |
return urlEncode(signature); | |
} else { | |
Serial.println("Base64 failed"); | |
Serial.print(result); | |
return ""; | |
} | |
} | |
//String OAuthClient::calculateOauthAuthorization(const String& signature, unsigned long timestamp) { | |
// String authorization; | |
// | |
// authorization += "OAuth "; | |
// authorization += "oauth_consumer_key=\""; | |
// authorization += _consumerKey; | |
// authorization += "\",oauth_nonce=\""; | |
// authorization += _nonce; | |
// authorization += "\",oauth_signature=\""; | |
// authorization += signature; | |
// authorization += "\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\""; | |
// authorization += timestamp; | |
// authorization += "\",oauth_token=\""; | |
// authorization += _accessToken; | |
// authorization += "\",oauth_version=\"1.0\""; | |
// | |
// return authorization; | |
//} | |
void shaExample() { | |
char *payload = "Hello SHA 1!"; | |
byte shaResult[20]; | |
mbedtls_md_context_t ctx; | |
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA1; | |
const size_t payloadLength = strlen(payload); | |
mbedtls_md_init(&ctx); | |
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 0); | |
mbedtls_md_starts(&ctx); | |
mbedtls_md_update(&ctx, (const unsigned char *) payload, payloadLength); | |
mbedtls_md_finish(&ctx, shaResult); | |
mbedtls_md_free(&ctx); | |
Serial.print("Hash: "); | |
for (int i = 0; i < sizeof(shaResult); i++) { | |
char str[3]; | |
sprintf(str, "%02x", (int)shaResult[i]); | |
Serial.print(str); | |
} | |
} | |
void shaHmacExample() { | |
char *payload = "Hello SHA 1!"; | |
char *key = "hello"; | |
byte shaResult[20]; | |
mbedtls_md_context_t ctx; | |
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA1; | |
const size_t payloadLength = strlen(payload); | |
const size_t keyLength = strlen(key); | |
mbedtls_md_init(&ctx); | |
mbedtls_md_setup(&ctx, mbedtls_md_info_from_type(md_type), 1); | |
mbedtls_md_hmac_starts(&ctx, (const unsigned char *) key, keyLength); | |
mbedtls_md_hmac_update(&ctx, (const unsigned char *) payload, payloadLength); | |
mbedtls_md_hmac_finish(&ctx, shaResult); | |
mbedtls_md_free(&ctx); | |
Serial.print("Hash: "); | |
for (int i = 0; i < sizeof(shaResult); i++) { | |
char str[3]; | |
sprintf(str, "%02x", (int)shaResult[i]); | |
Serial.print(str); | |
} | |
} | |
void setup() { | |
Serial.begin(115200); | |
setSigningKey(); | |
//shaHmacExample(); | |
//const char* method, const char* url, unsigned long time, const char* queryParams, const char* bodyParams | |
calculateSignature("POST", "https://api.twitter.com/2/tweets", millis(), "", ""); | |
} | |
void loop() { | |
// put your main code here, to run repeatedly: | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment