Skip to content

Instantly share code, notes, and snippets.

@witnessmenow
Created July 6, 2022 20:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save witnessmenow/d6f3c4d92c0585df9464756bd41d9f1e to your computer and use it in GitHub Desktop.
Save witnessmenow/d6f3c4d92c0585df9464756bd41d9f1e to your computer and use it in GitHub Desktop.
OauthSignature ESP32 Example
#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