python uuid5 equivalent in javascript nodejs - javascript

I am trying to convert python code to node js. Need help in converting this code to JS.
```
import uuid
uuid_salt = '9909fa72-b690-55dd-ab71-a987953bb438'
x = 'hello'
uuid_salt = uuid.UUID(uuid_salt)
salted_uuid = lambda x: str(uuid.uuid5(uuid_salt, x))
print salted_uuid(x)
```
Expected Output - 3e735408-7f83-53cf-b7ce-f9ef69e5ca43
I tried writing this way but output does not match
var uuid_salt = '9909fa72-b690-55dd-ab71-a987953bb438'
var x = 'hello'
var hmac = crypto.createHmac('sha1', uuid_salt);
hmac.setEncoding('hex');
hmac.end(x, function () {
hash = hmac.read();
console.log('hash >>> ', hash);
});
here is the actual python function copy paste from library
def uuid5(namespace, name):
"""Generate a UUID from the SHA-1 hash of a namespace UUID and a name."""
from hashlib import sha1
hash = sha1(namespace.bytes + name).digest()
return UUID(bytes=hash[:16], version=5)
I guess the bytes part is missing i tried created bytes array in JavaScript/Node but still the output is off.
Please help.
Thanks in advance!

Related

How can I mimick OpenLDAP's slappasswd using NodeJS?

My goal is to use NodeJS to create LDAP password hashes that are similar to what comes out of the slappasswd command-line tool.
Here's how LDAP passwords can be produced with command-line:
slappasswd -h '{SSHA}' -s 'P#ssw0rd'
{SSHA}1RHPt8m4AWLjK8Px1MT6FEBJOBJpdzqT
The result is a base64 encoded, salted SHA1 password.
Here's what I tried initially to recreate it:
#!/usr/bin/env node
import sha1 from 'crypto-js/sha1.js';
let password = 'P#ssW0rd';
let salt = btoa(0xA5); // Not random, just a proof of concept temporary value.
let hash = sha1(password + salt);
console.log('{SSHA}' + btoa(hash));
But, I got a much longer string than what the slappasswd command produced and I'm not sure why.
{SSHA}NDVkN2JjODQ2ZDk3Yjc2YmViNTU3MzUzYjBiNzExN2ZmYzMxYWY5ZA==
I did some digging around on the net and found this on an LDAP password generator web page:
<script src="lib/cryptojs/core.js"></script>
<script src="lib/cryptojs/sha1.js"></script>
<script src="lib/cryptojs/enc-base64.js"></script>
<script>
function slappasswd(password) {
var salt = CryptoJS.lib.WordArray.random(128/8).toString().substr(0,4);
var hash = CryptoJS.SHA1(password + salt);
var base = CryptoJS.enc.Latin1.parse(hash.toString(CryptoJS.enc.Latin1) + salt).toString(CryptoJS.enc.Base64);
return '{SSHA}' + base;
}
...
The web page produces a string that is the same length as what comes out of slappasswd, so I assume it's an accurate recreation of the slappasswd logic.
Using this information, my next attempt looks like this:
#!/usr/bin/env node
import * as CryptoJS from 'crypto-js';
let password = 'P#ssW0rd';
let salt = CryptoJS.lib.WordArray.random(128/8).toString().substr(0,4);
let hash = sha1(password + salt);
let base = CryptoJS.enc.Latin1.parse(hash.toString(CryptoJS.enc.Latin1) + salt).toString(CryptoJS.enc.Base64);
console.log('{SSHA}' + base);
However, I get errors.
First, there is TypeError: Cannot read properties of undefined (reading 'WordArray')
If I replace let salt = with let salt = btoa(0xA5) from my first attempt code, I then get the error: ReferenceError: sha1 is not defined
My feeling is that I've got the import wrong somehow.
I'm trying to do the ES6 module equivalent of var CryptoJS = require("crypto-js");, but failing somewhere.
So my question is two-fold:
Can my first attempt be made to produce a string length similar to what slappassword outputs?
If not, what can I do to fix the errors I'm getting in the second attempt?
Ideally, I'd like to understand where I went wrong in my first attempt rather than simply copying and pasting someone else's code (second attempt) without fully grasping it.
Here is alternative of python/php implementations for NodeJS.
Import Crypto module
const crypto = require('crypto');
It will be used to create LDAP password hashes (SSHA)
function generate_hash(passwd, salt) {
if (!salt) {
const buf = crypto.randomBytes(4);
salt = buf.toString('base64');
}
let ctx = crypto.createHash('sha1');
ctx.update(passwd, 'utf-8');
ctx.update(salt, 'binary');
let digest = ctx.digest('binary');
return '{SSHA}' + Buffer.from(digest + salt, 'binary').toString('base64');
}
It will be used to verify hash
function verify_hash(passwd, hash) {
let bhash = Buffer.from(hash.substr(6), 'base64');
let salt = bhash.toString('binary', 20);
let newssha = generate_hash(passwd, salt);
return hash === newssha;
}
Test it together
const hash = generate_hash("qwe1234");
let test = verify_hash("qwe1234", hash);
console.log(test); //Output: true
let test = verify_hash("XXXX", hash);
console.log(test); //Output: false
Hope it help you. Please let me know.
Try Now

Need JS equivalent

Python Code:
signature = hmac.new(bytearray.fromhex(key), data.encode('utf-8'), hashlib.sha256).hexdigest()
Solutions That I have tried
var compute_hmac = crypto.createHmac('sha256', key).update(data).digest('hex');
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex').toString()).update(data).digest('hex');
const hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex'))
Trying to validate webhook signatures of the following API
https://developer.close.com/topics/webhooks/
data is the payload received, the same thing is passed to python and JS code. But somehow, hex digest of python code is validated and hex code of JS code is entirely different.
Please refer to API link mentioned above (webhook signatures) to understand what I'm trying to achieve
Pass directly the keybuffer instead of adding .toString() to it
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex')).update(data).digest('hex');
py code
import hashlib
import hmac
key ="A1FF92";
data = "hello"
signature = hmac.new(bytearray.fromhex(key), data.encode('utf-8'), hashlib.sha256).hexdigest()
//78a1151ddd4f298a134e4625362af2ab8ef4bd49719e17053ec1eadd4cbf1bab
node code
var crypto = require("crypto")
var key = "A1FF92"
var data="hello";
var compute_hmac = crypto.createHmac('sha256', Buffer.from(key, 'hex')).update(data).digest('hex');
// 78a1151ddd4f298a134e4625362af2ab8ef4bd49719e17053ec1eadd4cbf1bab

Python bytestring => Javascript?

I'm attempting to port some Python code to Javascript. Here is the Python code:
# Python
import codecs
from Crypto.Cipher import AES
key = b"\xc3\x99\xff\xff\xc3\x99\xff\xff\xc3\x99\xff\xff\xc3\x99\xff\xff"
...
aes = AES.new(key, AES.MODE_ECB)
token = aes.encrypt("HELLO\x00\x00".encode("utf-8"))
token_hex = codecs.encode(token, "hex").decode("utf-8")
I'm not exactly sure how to port my Python key variable. Should it be UInt16Array...or a string?
This is my Javascript so far:
// Javascript
const crypto = require('crypto');
const key = '???' // <-- This is one place I am stuck. String? Byte array?
....
const cipher = crypto.createCipher('aes-128-ecb', key);
let tokenHex = cipher.update('HELLO\x00\x00', 'utf8', 'hex');
tokenHex = tokenHex.toString('utf8')
I appreciate any insight you can provide as to how I can get a matching tokenHex in Javascript.
Thank you!
What you are after is Buffer, which represents a collection of bytes.
You probably want to instantiate the key variable similar to this:
let key = Buffer.from("c399ff...", "hex");

Hash_hmac equivalent in Node.js

I have code that is working in my PHP app. In the PHP I sign the url with the following code:
private static function __getHash($string)
{
return hash_hmac('sha1', $string, self::$__secretKey, true);
}
I am attempting to sign the URL in the same way in a Node.js application. This is what I'm trying:
S3.prototype.getHash = function(string){
var key = this.secret_key;
var hmac = crypto.createHash('sha1', key);
hmac.update(string);
return hmac.digest('binary');
};
However, I am getting the following error:
The request signature we calculated does not match the signature you provided. Check your key and signing method.
Do these pieces of code do the same thing? Am I missing something?
This answer from Chris is good if you are porting hash_hmac with the last parameter being true. In this case, binary is produced, as is the case with Chris's javascript.
To add to that, this example:
$sign = hash_hmac('sha512', $post_data, $secret);
Would be ported with a function like so in nodejs:
const crypto = require("crypto");
function signHmacSha512(key, str) {
let hmac = crypto.createHmac("sha512", key);
let signed = hmac.update(Buffer.from(str, 'utf-8')).digest("hex");
return signed
}
The difference here being that when you leave off the last argument to hash_hmac (or set it to something not true), it behaves as defined in the PHP docs:
When set to TRUE, outputs raw binary data. FALSE outputs lowercase hexits.
In order to do this with node.js we use digest('hex') as you can see in the snippet.
The primary problem here is that you are using createHash which creates a hash, rather than createHmac which creates an HMAC.
Change createHash to createHmac and you should find it produces the same result.
This is the output you should expect:
chris /tmp/hmac $ cat node.js
var crypto = require('crypto');
var key = 'abcd';
var data = 'wxyz';
function getHash(string){
var hmac = crypto.createHmac('sha1', key);
hmac.update(string);
return hmac.digest('binary');
};
process.stdout.write(getHash(data));
chris /tmp/hmac $ cat php.php
<?php
$key = "abcd";
$data = "wxyz";
function __getHash($string)
{
global $key;
return hash_hmac('sha1', $string, $key, true);
}
echo utf8_encode(__getHash($data));
chris /tmp/hmac $ node node.js | base64
WsOKw4xgw4jDlFHDl3jDuEPDuCfCmsOFwoDCrsK/w6ka
chris /tmp/hmac $ php php.php | base64
WsOKw4xgw4jDlFHDl3jDuEPDuCfCmsOFwoDCrsK/w6ka

How can I get the sha1 hash of a string in node.js?

I'm trying to create a websocket server written in node.js
To get the server to work I need to get the SHA1 hash of a string.
What I have to do is explained in Section 5.2.2 page 35 of the docs.
NOTE: As an example, if the value of the "Sec-WebSocket-Key"
header in the client's handshake were "dGhlIHNhbXBsZSBub25jZQ==", the server would append thestring "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" to form the
string "dGhlIHNhbXBsZSBub25jZQ==258EAFA5-E914-47DA-95CA-C5AB0DC85B11". The server would then take the SHA-1 hash of this string, giving the value 0xb3 0x7a 0x4f 0x2c 0xc0 0x62 0x4f 0x16 0x90 0xf6 0x46 0x06 0xcf 0x38 0x59 0x45 0xb2 0xbe 0xc4 0xea. This value is then base64-encoded, to give the value "s3pPLMBiTxaQ9kYGzzhZRbK+xOo=", which would be returned
in the "Sec-WebSocket-Accept" header.
See the crypto.createHash() function and the associated hash.update() and hash.digest() functions:
var crypto = require('crypto')
var shasum = crypto.createHash('sha1')
shasum.update('foo')
shasum.digest('hex') // => "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
Obligatory: SHA1 is broken, you can compute SHA1 collisions for 45,000 USD (and even less since this answer was written). You should use sha256:
var getSHA256ofJSON = function(input){
return crypto.createHash('sha256').update(JSON.stringify(input)).digest('hex')
}
To answer your question and make a SHA1 hash:
const INSECURE_ALGORITHM = 'sha1'
var getInsecureSHA1ofJSON = function(input){
return crypto.createHash(INSECURE_ALGORITHM).update(JSON.stringify(input)).digest('hex')
}
Then:
getSHA256ofJSON('whatever')
or
getSHA256ofJSON(['whatever'])
or
getSHA256ofJSON({'this':'too'})
Official node docs on crypto.createHash()
Tips to prevent issue (bad hash) :
I experienced that NodeJS is hashing the UTF-8 representation of the string. Other languages (like Python, PHP or PERL...) are hashing the byte string.
We can add binary argument to use the byte string.
const crypto = require("crypto");
function sha1(data) {
return crypto.createHash("sha1").update(data, "binary").digest("hex");
}
sha1("Your text ;)");
You can try with : "\xac", "\xd1", "\xb9", "\xe2", "\xbb", "\x93", etc...
Other languages (Python, PHP, ...):
sha1("\xac") //39527c59247a39d18ad48b9947ea738396a3bc47
Nodejs:
sha1 = crypto.createHash("sha1").update("\xac", "binary").digest("hex") //39527c59247a39d18ad48b9947ea738396a3bc47
//without:
sha1 = crypto.createHash("sha1").update("\xac").digest("hex") //f50eb35d94f1d75480496e54f4b4a472a9148752
You can use:
const sha1 = require('sha1');
const crypt = sha1('Text');
console.log(crypt);
For install:
sudo npm install -g sha1
npm install sha1 --save
Please read and strongly consider my advice in the comments of your post. That being said, if you still have a good reason to do this, check out this list of crypto modules for Node. It has modules for dealing with both sha1 and base64.
Answer using the new browser compatible, zero dependency SubtleCrypto API added in Node v15
const crypto = this.crypto || require('crypto').webcrypto;
const sha1sum = async (message) => {
const encoder = new TextEncoder()
const data = encoder.encode(message)
const hashBuffer = await crypto.subtle.digest('SHA-1', data)
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
return hashHex;
}
sha1sum('foo')
.then(digestHex => console.log(digestHex))
// "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
Node Sandbox: https://runkit.com/hesygolu/61564dbee2ec8600082a884d
Sources:
https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#converting_a_digest_to_a_hex_string
https://nodejs.org/api/webcrypto.html#webcrypto_class_subtlecrypto

Categories