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);
giowild
  • 479
  • 5
  • 17
  • maybe stupid, but are you sure to use the same timestamp – Pascal Feb 18 '15 at 12:47
  • i tried by using seconds, milliseconds and microseconds bu the result is always not the same as in erlang.. – giowild Feb 18 '15 at 12:49
  • I'm afraid the problem relies in what the "<>" piece means in Erlang.. I'm afraid it isn't equal to the simple javascript string concatenation – giowild Feb 18 '15 at 12:50
  • @mlorini: This "<>" code is exactly, what you need, this is concatenation. – tkowal Feb 18 '15 at 13:18

3 Answers3

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.

tkowal
  • 9,129
  • 1
  • 27
  • 51
  • @mlorini hash.toString() creates hexadecimal string representation. You can get it in erlang with `lists:flatten([io_lib:format("~2.16.0b", [Byte]) || <> <= Hash]).` – Łukasz Ptaszyński Feb 18 '15 at 13:33
  • 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`? – tkowal Feb 18 '15 at 13:38
  • exactly. I want the opposite! – giowild Feb 18 '15 at 13:42
  • @mlorini Sorry, found answer for your problem here: http://stackoverflow.com/a/3195961/4169859 but I'm not sure how it is going to handle non ascii values... – Łukasz Ptaszyński Feb 18 '15 at 13:52
  • @tkowal yeah, thanks to your suggestions, this fiddle produces in javascript the same byte sequence of the erlang counterpart: https://jsfiddle.net/ou0wknwv/2/ – giowild Feb 18 '15 at 16:46
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.

legoscia
  • 39,593
  • 22
  • 116
  • 167
  • yes, I mean by running this function at the same time.. For test of course, i'm using a fixed value for timestamp.. – giowild Feb 18 '15 at 13:23
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¶
kenorb
  • 155,785
  • 88
  • 678
  • 743