Abhik Chakraborty
Abhik Chakraborty

Reputation: 44844

Hashing algorithm Node js vs Python

I was trying to convert a hash algorithm which is written on Python to node.js

The python code looks something as

import uuid
import hashlib
import struct

CLIENT_ID = uuid.UUID('c5f92e0d-e762-32cd-98cb-8c546c410dbe')
SECRET = uuid.UUID('2cf26ff5-bd06-3245-becf-4d5a3baa704f')

data = CLIENT_ID.bytes_le + SECRET.bytes_le + struct.pack("I", 2017) + struct.pack("I", 9) + struct.pack("I", 2)

token = str(uuid.UUID(bytes_le=hashlib.sha256(data).digest()[0:16])) 

The token generated is 32d86f00-eb49-2739-e957-91513d2b9969

Here the date values struct.pack values are generated using datetime but for convenient I have hard coded here.

I tried to convert the same by looking at the python doc for the respective libraries and did so far as

let CLIENT_ID = new Buffer('c5f92e0d-e762-32cd-98cb-8c546c410dbe');
let SECRET = new Buffer('2cf26ff5-bd06-3245-becf-4d5a3baa704f');
let d = new Buffer(2);
let m = new Buffer(9);
let y = new Buffer(2017); 

let data = CLIENT_ID+SECRET+y+m+d;
const uuidv4 = require('uuid/v4');
const hash = crypto.createHash('sha256');

let token = uuidv4({random: hash.update(data, 'utf8').digest().slice(0, 16)}, 0);

And the hash it generates is b7b82474-eab4-4295-8318-cc258577ff9b

So, basically I am miserably missing something for the nodejs part.

Could you please guide me on where what went wrong. Thanks for the help

Upvotes: 4

Views: 486

Answers (2)

Nik Markin
Nik Markin

Reputation: 981

There's a lot of missed parts actually it tuned out.

###node parts:

  • new Buffer('c5')

    does not represent <Buffer c5>, but <Buffer 63 35>.

    To write c5 you would need to use Buffer.from([0xc5]) or Buffer.from([197]) (dec).

  • new Buffer(2)

    does not represent <Buffer 02>, it just allocates 2 bytes.

  • CLIENT_ID+SECRET+y+m+d

    concatenation of buffers does not work that way.

    Use array of buffers and Buffer.concat([buffers]) to concatenate buffers.

###uuid parts:

  • it turned out that uuid operates modified version of buffers (bytes_le part in python code)

#the most interesting part:

  • in the python version of uuid, if no version argument is passed to uuid.UUID(...), uuid would generate an ID without fixing bits

    According to the RFC-4122 4.4 uuid should fix that bits.

    uuid.py skips RFC-4122 4.4

    node-uuid/v4.js fixes required bits

    that way even with the same results for sha256 hashing, the results between python and node implementation still would differ

     python: 32d86f00-eb49-2739-e957-91513d2b9969
     node:   32d86f00-eb49-4739-a957-91513d2b9969
                           ^    ^
    

So, I see here 2 options

  • to pass version to python uuid (only for the last uuid call uuid.UUID(bytes_le=..., version=4)), that way python would return 32d86f00-eb49-4739-a957-91513d2b9969
  • if there's no way to change source code in python project, I guess there's an option to fork uuid and remove two lines of code in node-uuid/v4.js?

##See node version of your code below:

const uuidv4 = require('uuid/v4');
const crypto = require('crypto');
const hash = crypto.createHash('sha256');

const client_id_hex_str = "c5f92e0d-e762-32cd-98cb-8c546c410dbe".replace(/-/g, "");
const secret_hex_str = "2cf26ff5-bd06-3245-becf-4d5a3baa704f".replace(/-/g, "");

let CLIENT_ID = Buffer.from(to_bytes_le(to_bytes(client_id_hex_str, null, 16, 'big')));
let SECRET = Buffer.from(to_bytes_le(to_bytes(secret_hex_str, null, 16, 'big')));
let d = Buffer.from(to_bytes(null, 2, 4));
let m = Buffer.from(to_bytes(null, 9, 4));
let y = Buffer.from(to_bytes(null, 2017, 4));

let data = Buffer.concat([CLIENT_ID, SECRET, y, m, d]);
let hashBytes = hash.update(data, 'utf8').digest().slice(0, 16);
hashBytes = [].slice.call(hashBytes, 0);
hashBytes = Buffer.from(to_bytes_le(hashBytes));

let token = uuidv4({random: hashBytes});

console.log(token);


// https://stackoverflow.com/questions/16022556/has-python-3-to-bytes-been-back-ported-to-python-2-7
function to_bytes(hexString, number, length, endianess) {
    if (hexString == null && number == null) {
        throw new Error("Missing hex string or number.");
    }
    if (!length || isNaN(length)) {
        throw new Error("Missing or invalid bytes array length number.");
    }
    if (hexString && typeof hexString != "string") {
        throw new Error("Invalid format for hex value.");
    }
    if (hexString == null) {
        if (isNaN(number)) {
            throw new Error("Invalid number.");
        }
        hexString = number.toString(16);
    }
    let byteArray = [];

    if (hexString.length % 2 !== 0) {
        hexString = '0' + hexString;
    }
    const bitsLength = length * 2
    hexString = ("0".repeat(bitsLength) + hexString).slice(-1 * bitsLength);
    for (let i = 0; i < hexString.length; i += 2) {
        const byte = hexString[i] + hexString[i + 1];
        byteArray.push(parseInt(byte, 16));
    }

    if (endianess !== "big") {
        byteArray = byteArray.reverse();
    }
    return byteArray;
}
// https://github.com/python/cpython/blob/master/Lib/uuid.py#L258
function to_bytes_le(bytes) {
    const p1 = bytes.slice(0, 4).reverse();
    const p2 = bytes.slice(4, 6).reverse();
    const p3 = bytes.slice(6, 8).reverse();
    const p4 = bytes.slice(8);
    const bytes_le = [].concat.apply([], [p1, p2, p3, p4]);
    return bytes_le;
}

Upvotes: 2

ajafik
ajafik

Reputation: 145

Do you want the hashing of the data to be the same as the Python code above? If not, you can take a look at the the Sha256 module below in NodeJS

https://www.npmjs.com/package/sha256

Upvotes: -1

Related Questions