4

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

2
  • what is the hash in your nodejs code where you are using it like: hash.update. Commented Sep 2, 2017 at 15:51
  • Sorry I missed that const hash = crypto.createHash('sha256'); let me update the question Commented Sep 2, 2017 at 15:52

2 Answers 2

2

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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

that was some kick-ass stuff you did. I tried and mostly it worked and as pointed out there is still a a difference. I have in fact pass the version number on python something as CLIENT_ID = uuid.UUID('C5F92E0D-E762-32CD-98CB-8C546C410DBE', version=4) SECRET = uuid.UUID('2CF26FF5-BD06-3245-BECF-4D5A3BAA704F', version=4) and finally token = str(uuid.UUID(bytes_le=hashlib.sha256(data).digest()[0:16], version=4)) however it did not work and gives a different hash altogether.
@AbhikChakraborty try to apply version argument only for the last uuid call uuid.UUID(bytes_le= it should return 32d86f00-eb49-4739-a957-91513d2b9969
-1

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

2 Comments

Thats the whole point, else I can generate the hashing in millions of ways. The existing hash with python is for token generation and should be same from nodejs
ok. You can check this out if it can be of help. github.com/PythonJS/PythonJS

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.