How to create a sha256 has (SAS Token) with BearSSL

593 Views Asked by At

I am trying to generate a SAS token to authenticate to Azure IoT Hub using the PubSub Client in the Tasmota framework https://tasmota.github.io/.

Following an example: https://github.com/Azure/azure-sdk-for-c/blob/master/sdk/samples/iot/aziot_esp8266/aziot_esp8266.ino, I am able to create a signature for only a 4 character secret when compared to a known good Python example. When I increase or decrease the length of the secret, the hash does not match. Any suggestion on what I am doing wrong would be great.

It looks like it is only hashing and encoding the first 4 characters (note how the C code 'mark' = 'markmark').

C Code (run on an ESP8266):

#include <bearssl\bearssl.h>  // from https://github.com/arendst/Tasmota/tree/development/lib/lib_ssl
#include <base64.hpp>         // from https://github.com/arendst/Tasmota/tree/development/lib/lib_ssl

const char *PSK = "MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=";
const char *dataToSign = "mark";

br_sha256_context sha256_context;
br_hmac_key_context hmac_key_context;
br_hmac_context hmac_context;

unsigned char decodedPSK[32];
unsigned char encryptedSignature[100];
unsigned char encodedSignature[100];

// need to base64 decode the Preshared key and the length
int base64_decoded_device_length = decode_base64((unsigned char*)PSK, decodedPSK);

// create the sha256 hmac and hash the data
br_sha256_init(&sha256_context);
br_hmac_key_init(&hmac_key_context, sha256_context.vtable, decodedPSK, base64_decoded_device_length);
br_hmac_init(&hmac_context, &hmac_key_context, 32);
br_hmac_update(&hmac_context, dataToSign, sizeof(dataToSign));
br_hmac_out(&hmac_context, encryptedSignature);

// base64 decode the HMAC to a char
encode_base64(encryptedSignature, br_hmac_size(&hmac_context), encodedSignature);

printf("PSK is %s \n", PSK);
printf("decodedPSK is %s \n", decodedPSK);
printf("dataToSign is %s \n", dataToSign);
printf("encryptedSignature is %s \n", encryptedSignature);
printf("encodedSignature is %s \n", encodedSignature);

Python code:

from base64 import b64encode, b64decode
from hashlib import sha256
from urllib.parse import quote_plus, urlencode
from hmac import HMAC
PSK="MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4="
sign_key = "mark".encode('utf-8')
signature = b64encode(HMAC(b64decode(PSK), sign_key, sha256).digest())
print('PSK = ' + PSK)
print('sign_key = ' + str(sign_key))
print('signature = ' + str(signature))

If set dataToSign to mark, the hash matches:

C output:

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is mark
encryptedSignature is ␝�8)����jLQ��␃b!��x␞Ǿ2��1�W␗S4���?���?�␕�? 
encodedSignature is Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

Python output:

C:/Python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'mark'
signature = b'Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

If I change the dataToSign to 'markmark', they do not match:

C Output:

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is markmark
encryptedSignature is ␝�8)����jLQ��␃b!��x␞Ǿ2��1�W␗S4���?���?�␕�? 
encodedSignature is Hak4Kaic0vtqTFGSlQNiIeeD6ngex74yoOExgVcXUzQ=

Python Output:

C:/Python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'markmark'
signature = b'XSf//iSjx9hB6lOQe5lcYkxLGWY+g5oXM8AX0XVGIh4='

This problem also happens if I make the dataToSign shorter, example 'joe'.

C Output:

PSK is MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
ecodedPSK is 0␕��f�3D␙�Ӿ�␄��Gm␓�ST!^␌Q��␆��
dataToSign is joe
encryptedSignature is ~����␕�␡
encodedSignature is fp21m4IVnH8Arfnpb1SNY3HifxunD5se9QwF/KA3z2E=

Python:

& C:/Python38-64/python.exe d:/Git/createsastoken/generate/test.py
PSK = MBXjqeJm6jNEGa7TvqIEkbRHbRP+U1QhXgxR1+QGoM4=
sign_key = b'joe'
signature = b'69FWap6f/xD4JauDwia5y9fqYdq8qnSH5F7Yo5aa4FI='
1

There are 1 best solutions below

0
On

The sizeof

const char *dataToSign = "mark";

will always be four because it is a pointer (at least on a 32 bit system). Thus it works for 'mark' but fails for 'joe' and 'markmark'. Declare it as

const char dataToSign[] = "whatever...";

Then sizeof will give you the array size but this will include the terminating zero so you will need to subtract one.