MD5 in Erlang

September 10, 2008

An MD5 hash for any given message is 16 bytes (128 bits) in size and is represented by a unique 32 digit Hexadecimal number.

Erlang has a built-in-function to calculate the MD5, which returns the hash in the form of a binary data-structure.

$ erlang:md5("hello").
<93,65,64,42,188,75,42,118,185,113,157,145,16,23,197,146>

But what we usually need is a string representation of the Hexadecimal value. Hence, we need to convert the Erlang Binary to a Hex-string, for which I didn’t find any BIF.

So first of all, lets convert the Binary to a list (of integers)- so that we can process it easily. Erlang has a function for converting binary to list:

$ binary_to_list(<93,65,64,42,188,75,42,118,185,113,157,145,16,23,197,146>).
[93,65,64,42,188,75,42,118,185,113,157,145,16,23,197,146]

Now, we have to convert each of the integers in the list into its hex equivalent. How do you convert an integer in Decimal system to a Hexadecimal system?

Eg. Take an integer 230. Divide it by 16.
230 div 16 = 14 In Hex, 14 is E
230 rem 16 = 6 In Hex, 6 is 6
So 230 in Hex is E6.

Now we will have to do the same for every integer in the list. I did it using the lists:map function and the applying the int_to_hex conversion to every integer:

$ lists:map(fun(X) ->
int_to_hex(X) end, L).

This will actually return a list of integers representing the (hex) string, which is how a string is represented in Erlang – a list of Integers.

The complete code is below:

-module(md5).
-export([md5_hex/1]).

md5_hex(S) ->
       Md5_bin =  erlang:md5(S),
       Md5_list = binary_to_list(Md5_bin),
       lists:flatten(list_to_hex(Md5_list)).

list_to_hex(L) ->
       lists:map(fun(X) -> int_to_hex(X) end, L).

int_to_hex(N) when N < 256 ->
       [hex(N div 16), hex(N rem 16)].

hex(N) when N < 10 ->
       $0+N;
hex(N) when N >= 10, N < 16 ->
       $a + (N-10).

Output:

$ md5:md5_hex("hello").
"5d41402abc4b2a76b9719d911017c592"
16 Comments on MD5 in Erlang

Respond | Trackback

  1. masklinn says:

    Line 10, why write `fun(X) -> int_to_hex(X) end` instead of the simpler (and maybe faster?) `fun int_to_hex/1`?

    The whole “int_to_hex” code could also be replaced with `fun(X) -> io_lib:format(“~.16b”, [X]) end`, but I’m not sure it would be much better (if better at all).

    And using binary comprehension, we can do away with the rest of the code and use:

    md5_hex(S) ->
    lists:flatten(
    [io_lib:format("~.16b",[N]) || <> <= erlang:md5(S)]).

  2. Eduard says:

    wathout: oneliner

    >> io:format(lists:flatten( array:to_list(array:new([{size,16},{default,"~.16b"}])) “~n” ), binary_to_list(erlang:md5(“hello”))).
    5d41402abc4b2a76b9719d911017c592

    I know this looks awful. But look at io:format documentation, it can
    print hex numbers. ;)

  3. Per Gustafsson says:

    Or a much shorter and slightly more obscure version:

    -module(md5).
    -export([md5_hex/1]).

    md5_hex(S) -> [hex(N) || <> <= erlang:md5(S)].

    hex(N) when N $0 N;
    hex(N) when N >= 10, N $a (N-10).

  4. Per Gustafsson says:

    Whoops my comment does not look right because of angle brackets I’ll try once more:

    module(md5).
    -export([md5_hex/1]).

    md5_hex(S) -&gt [hex(N) || &lt&ltN:4&gt&gt <= erlang:md5(S)].

    hex(N) when N &lt 10 -&gt $0 N;
    hex(N) when N &gt= 10, N &lt 16 -&gt $a (N-10).

  5. David Mercer says:

    You can also use list comprehensions to make it simpler:

    14> MD5 = fun(S) -> [ HexChar || <> <= erlang:md5(S), [HexChar] <- io_lib:format(“~.16b”, [Nibble]) ] end.
    #Fun
    15> MD5(“hello”).
    “5d41402abc4b2a76b9719d911017c592″

  6. David Mercer says:

    Looks like your website eats up characters. Please advise as to how to format my reply appropriately. Thank-you.

  7. admin says:

    Thanks guys. All of your solutions are very much valid and appreciated. I am learning my way through, as I go.

  8. [...] %% Crypto doesn’t have a hexdigest method. I found the code below %% here %% Convert Integer from the SHA to Hex list_to_hex(L)-> lists:map(fun(X) -> int_to_hex(X) end, L). [...]

  9. Sushant says:

    I am also learning Erlang and was having a problem doing this. Thank you for the post and also thanks to other commenters.

  10. David Ireland says:

    1> lists:flatten([io_lib:format("~2.16.0b",[N])||N<-binary_to_list(erlang:md5(“abc”))]).
    “900150983cd24fb0d6963f7d28e17f72″

  11. Emil says:

    Or use a little bit of magic bit syntax:

    1> <> = erlang:md5(“abc”), lists:flatten(io_lib:format(“~32.16.0b”, [M])).
    “900150983cd24fb0d6963f7d28e17f72″

    Read more about bit syntax here: http://erlang.org/documentation/doc-5.4.12/doc/programming_examples/bit_syntax.html

  12. Emil says:

    Nice form that strips out random characters within *lesser than* and *greater *than* :P

    the left hand side of the = should be: lesser_than M colon 128 greater_than

    Thanks

  13. Jon Watte says:

    Erlang has great bignum support. All the mapping/comprehension solutions miss the point.

    Here’s the canonical conversion function:

    hexstring(<>) ->
    lists:flatten(io_lib:format(“~32.16.0b”, [X])).

    As you can see, it takes a 128-bit binary value, and formats it as a big-endian unsigned 0-padded base-16 lowercase number. Which is exactly what you want.

    hexstring(erlang:md5(“foobar”)).
    “3858f62230ac3c915f300c664312c63f”

  14. Jon Watte says:

    Blech. Crappy tag stripping…
    The argument doing the match is:

    < < X:128/big-unsigned-integer > >

  15. admin says:

    @JonWatte, Interesting. Would have saved some trouble! Thanks for your post on the topic. I am linking it here: http://www.enchantedage.com/node/199

Respond

Comments

Comments