1

I'm trying to obtain in Javascript the same value returned by the following generate_hash erlang function

-define(b2l(V), binary_to_list(V)).
-define(l2b(V), list_to_binary(V)).

generate_hash(User, Secret, TimeStamp) ->
    SessionData = User ++ ":" ++ erlang:integer_to_list(TimeStamp, 16),
    Hash = crypto:sha_mac(Secret, SessionData),
    base64:encode(SessionData ++ ":" ++ ?b2l(Hash)).

make_time() ->
    {NowMS, NowS, _} = erlang:now(),
    NowMS * 1000000 + NowS.

This function is being called in erlang in this way:

Username = "username" 
Secret = ?l2b("secret"),
UserSalt = "usersalt",
CurrentTime = make_time(),
Hash = generate_hash( ?b2l(UserName), <<Secret/binary, UserSalt/binary>>, CurrentTime).

I managed to use the google CryptoJS library to calculate the hash, but the base64 returned value does not match the one returned in erlang.

<script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/hmac-sha1.js"></script>

function generate_hash(User, Secret, TimeStamp) {
    var SessionData = User + ":" + parseInt(TimeStamp,16);
    var hash = CryptoJS.HmacSHA1(Secret,SessionData);
    return atob(SessionData + ":" + hash.toString())
}

var Hash = generate_hash( "username", "secret"+"usersalt", new Date().getTime())

alert(Hash);
4
  • maybe stupid, but are you sure to use the same timestamp Commented Feb 18, 2015 at 12:47
  • i tried by using seconds, milliseconds and microseconds bu the result is always not the same as in erlang.. Commented Feb 18, 2015 at 12:49
  • I'm afraid the problem relies in what the "<<Secret/binary, UserSalt/binary>>" piece means in Erlang.. I'm afraid it isn't equal to the simple javascript string concatenation Commented Feb 18, 2015 at 12:50
  • @mlorini: This "<<Secret/binary, UserSalt/binary>>" code is exactly, what you need, this is concatenation. Commented Feb 18, 2015 at 13:18

3 Answers 3

2

There are three problems in your code.

Firstly: CryptoJS.HmacSHA1(Secret,SessionData); has its arguments reversed. It should be CryptoJS.HmacSHA1(SessionData, Secret);.

You can check it out in JS console:

var hash = CryptoJS.HmacSHA1("b", "a");
0: 1717011798
1: -2038285946
2: -931908057
3: 823367506
4: 21804555

Now, go to Erlang console and type this:

crypto:sha_mac(<<"a">>, <<"b">>).
<<102,87,133,86,134,130,57,134,200,116,54,39,49,19,151,82,1,76,182,11>>
binary:encode_unsigned(1717011798).
<<102,87,133,86>>
binary:encode_unsigned(21804555).
<<1,76,182,11>>

I don't know equivalent method for signed integers, but this proves, that changing the order of arguments gives the same binary value.

Second problem is with hash.toString(), which following my example gives something like:

hash = CryptoJS.HmacSHA1("b", "a");
hash.toString();
"6657855686823986c874362731139752014cb60b"

while Erlang binary to list will result in:

Str = binary_to_list(Hash).
[102,87,133,86,134,130,57,134,200,116,54,39,49,19,151,82,1,76,182,11]
io:format("~s", [Str]).
fWV9Èt6'1^SR^AL¶^K

I am not sure, what toString does with word array, but this messes up the final result.

Third problem is, that new Date().getTime() will return time in milliseconds, while in Erlang, you have microseconds. This shouldn't matter, when you test it with static integer, though.

Sign up to request clarification or add additional context in comments.

5 Comments

@mlorini hash.toString() creates hexadecimal string representation. You can get it in erlang with lists:flatten([io_lib:format("~2.16.0b", [Byte]) || <<Byte:8>> <= Hash]).
I think that @mlorini wants the opposite - to create the Erlang representation with JS. @Łukasz: do you know, how to convert JS list of words to something like Erlang string after binary_to_list?
exactly. I want the opposite!
@mlorini Sorry, found answer for your problem here: stackoverflow.com/a/3195961/4169859 but I'm not sure how it is going to handle non ascii values...
@tkowal yeah, thanks to your suggestions, this fiddle produces in javascript the same byte sequence of the erlang counterpart: jsfiddle.net/ou0wknwv/2
0

Two things:

  1. The make_time function in the Erlang code above returns the number of seconds since the Unix epoch, while the Javascript method getTime returns the number of milliseconds.

    Besides that, since you're probably not running the two functions in the same second, you'll get different timestamps and therefore different hashes anyway.

  2. The Javascript function parseInt parses a string and returns an integer, while the Erlang function integer_to_list takes an integer and converts it to a string (in Erlang, strings are represented as lists, thus the name). You probably want to use the toString method instead.

1 Comment

yes, I mean by running this function at the same time.. For test of course, i'm using a fixed value for timestamp..
0

This algorithm can generate the same sequence of bytes generated by erlang counterpart:

var ret = [];

var hash = CryptoJS.HmacSHA1("b","a").words;
angular.forEach(hash,function(v){
    var pos = v>=0, last=ret.length;
    for(v=pos?v:v>>>0; v>0; v=Math.floor(v/256)) {
        ret.splice(last, 0, v%256);
    }
});
console.info(ret);
console.info(String.fromCharCode.apply(String,ret));

The above outputs:

[102, 87, 133, 86, 134, 130, 57, 134, 200, 116, 54, 39, 49, 19, 151, 82, 1, 76, 182, 11]
fWV9Èt6'1RL¶

Comments

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.