NodeJS convert to Byte Array code return different results compare to python

Question:

I got the following Javascript code and I need to convert it to Python(I’m not an expert in hashing so sorry for my knowledge on this subject)

function generateAuthHeader(dataToSign) {
    let apiSecretHash = new Buffer("Rbju7azu87qCTvZRWbtGqg==", 'base64');
    let apiSecret = apiSecretHash.toString('ascii');
    var hash = CryptoJS.HmacSHA256(dataToSign, apiSecret);
    return hash.toString(CryptoJS.enc.Base64);
}

when I ran generateAuthHeader("abc") it returned +jgBeooUuFbhMirhh1KmQLQ8bV4EXjRorK3bR/oW37Q=

So I tried writing the following Python code:

def generate_auth_header(data_to_sign):
    api_secret_hash = bytearray(base64.b64decode("Rbju7azu87qCTvZRWbtGqg=="))
    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()

But when I ran generate_auth_header("abc") it returned a different result aOGo1XCa5LgT1CIR8C1a10UARvw2sqyzWWemCJBJ1ww=

Can someone tell me what is wrong with my Python code and what I need to change?

The base64 is the string I generated myself for this post

UPDATE:
this is the document I’m working with

//Converting the Rbju7azu87qCTvZRWbtGqg== (key) into byte array 
//Converting the data_to_sign into byte array 
//Generate the hmac signature

it seems like apiSecretHash and api_secret_hash is different, but I don’t quite understand as the equivalent of new Buffer() in NodeJS is bytearray() in python

Asked By: Linh Nguyen

||

Answers:

It took me 2 days to look it up and ask for people in python discord and I finally got an answer. Let me summarize the problems:

  • API secret hash from both return differents hash of the byte array
    javascript

Javascript

apiSecret = "E8nm,ns:u0002NvQY;F*"

Python

api_secret_hash = b'Exb8xeexedxacxeexf3xbax82Nxf6QYxbbFxaa'

once we replaced the hash with python code it return the same result

def generate_auth_header(data_to_sign):
    api_secret_hash = "E8nm,ns:u0002NvQY;F*".encode()

    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()

encoding for ASCII in node.js you can find here https://github.com/nodejs/node/blob/a2a32d8beef4d6db3a8c520572e8a23e0e51a2f8/src/string_bytes.cc#L636-L647

case ASCII:
  if (contains_non_ascii(buf, buflen)) {
    char* out = node::UncheckedMalloc(buflen);
    if (out == nullptr) {
      *error = node::ERR_MEMORY_ALLOCATION_FAILED(isolate);
      return MaybeLocal<Value>();
    }
    force_ascii(buf, out, buflen);
    return ExternOneByteString::New(isolate, out, buflen, error);
  } else {
    return ExternOneByteString::NewFromCopy(isolate, buf, buflen, error);
  }

there is this force_ascii() function that is called when the data contains non-ASCII characters which is implemented here https://github.com/nodejs/node/blob/a2a32d8beef4d6db3a8c520572e8a23e0e51a2f8/src/string_bytes.cc#L531-L573

so we need to check for the hash the same as NodeJS one, so we get the final version of the Python code:

def generate_auth_header(data_to_sign):
    # convert to bytearray so the for loop below can modify the values
    api_secret_hash = bytearray(base64.b64decode("Rbju7azu87qCTvZRWbtGqg=="))
    
    # "force" characters to be in ASCII range
    for i in range(len(api_secret_hash)):
        api_secret_hash[i] &= 0x7f;

    hash = hmac.new(api_secret_hash, data_to_sign.encode(), digestmod=hashlib.sha256).digest()
    return base64.b64encode(hash).decode()

now it returned the same result as NodeJS one

Thank you Mark from the python discord for helping me understand and fix this!

Hope anyone in the future trying to convert byte array from javascript to python know about this different of NodeJS Buffer() function

Answered By: Linh Nguyen