-module(epop_md5).
-author('').

%%% --------------------------------------------------------------------
%%% File    : md5.erl
  Author   :   <  >
  Purpose : Implementation of MD5 in erlang
  Created : 30 Oct 1997 by   <  >
%%% ====================================================================
%%% License to copy and use this software is granted provided that it
  is identified as the " RSA Data Security , Inc. MD5 Message - Digest
%%% Algorithm" in all material mentioning or referencing this software
%%% or this function.
%%%  
%%% License is also granted to make and use derivative works provided
  that such works are identified as " derived from the RSA Data
  Security , Inc. MD5 Message - Digest Algorithm " in all material
%%% mentioning or referencing the derived work.
%%% ====================================================================
  Adopted : 17 Aug 1998 by 
%%%           Made it into an epop module and cleaned it up a bit.
%%% --------------------------------------------------------------------

-export([string/1]).

-import(lists, [reverse/1]).

-record(md5_ctx, 
	{
	 state = { 16#67452301, 16#efcdab89, 16#98badcfe, 16#10325476 },
  number of bits ( 64 bit )
  input buffer ( 16 bytes )
	}).

-define(S11, 7).
-define(S12, 12).
-define(S13, 17).
-define(S14, 22).
-define(S21, 5).
-define(S22, 9).
-define(S23, 14).
-define(S24, 20).
-define(S31, 4).
-define(S32, 11).
-define(S33, 16).
-define(S34, 23).
-define(S41, 6).
-define(S42, 10).
-define(S43, 15).
-define(S44, 21).

  F , G , H and I are basic MD5 functions .

-define(F(X, Y, Z), (((X) band (Y)) bor ((bnot (X)) band (Z)))).
-define(G(X, Y, Z), (((X) band (Z)) bor ((Y) band (bnot (Z))))).
-define(H(X, Y, Z), ((X) bxor (Y) bxor (Z))).
-define(I(X, Y, Z), ((Y) bxor ((X) bor (bnot (Z))))).

-define(U32(X), ((X) band 16#ffffffff)).

-define(ROTATE_LEFT(X,N), rotate_left(X,N)).

  FF , GG , HH , and II transformations for rounds 1 , 2 , 3 , and 4 .
%% Rotation is separate from addition to prevent recomputation.
%%
-define(FF(A, B, C, D, X, S, AC),
	?ROTATE_LEFT(A + ?F((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define(GG(A, B, C, D, X, S, AC), 
	?ROTATE_LEFT(A + ?G((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define( HH(A, B, C, D, X, S, AC), 
	?ROTATE_LEFT(A + ?H((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define(II(A, B, C, D, X, S, AC),
	?ROTATE_LEFT(A +  ?I((B), (C), (D)) + (X) + (AC),(S)) + (B)).

%% ---------------------------------
%% Exported function: string/1
%% Do a message digest on a string
%% ---------------------------------

string(Str) ->
    format(final(update(init(), Str))).

format([X | Xs]) ->
    [hex(X bsr 4), hex(X) | format(Xs)];
format([]) -> [].

hex(X) ->
    X4 = (X band 16#f),
    if X4 < 10 -> X4 + $0;
       true -> (X4-10) + $a
    end.

init() ->
    #md5_ctx {}.

update(CTX, Input) ->
    Buffer = CTX#md5_ctx.buffer,
    LenI = length(Input),
    Len = LenI + length(Buffer),
    update(Buffer ++ Input, Len,CTX#md5_ctx.state,
	   CTX#md5_ctx.count+(LenI bsl 3)).

%%
%% update state, count reflects number of bytes 
%% including bytes in buffer
%%
update(Buf0, Len0, State0, Count) when Len0 >= 64 ->
    {Xs,Buf1} = decode(Buf0, 64),
    State1 = transform(State0, Xs),
    update(Buf1, Len0 - 64, State1, Count);
update(Buf0, Len0, State0, Count) ->
    #md5_ctx { state = State0, count = Count, buffer = Buf0 }.

%% produce a digest
final(CTX) ->
  pad out to a length 56 ( we later add a count that makes 64 )
    Count = CTX#md5_ctx.count,      %% number of bits
    Index =  (Count bsr 3) rem 64,  %% number of bytes
    PadLen = if Index < 56 ->
		     56 - Index;
		true -> 120 - Index
	     end,
    CTX1 = update(CTX, padding(PadLen,[])),
    CTX2 = update(CTX1, encode([?U32(Count), ?U32(Count bsr 32)])),
    encode(tuple_to_list(CTX2#md5_ctx.state)).

%% generate padding info to final    
padding(0,Acc) -> Acc;
padding(1,Acc) -> [16#80 | Acc];
padding(N,Acc) -> padding(N-1, [0 | Acc]).

  rotate X as 32 - bit unsigned left N bits
rotate_left(X, N) ->
    ?U32(X bsl N) bor (?U32(X) bsr (32 - N)).

%%
  decodes  number of bytes into 32 bit integers
  returns     }
%%
decode(Buf, Len) ->
    decode(Buf, Len, []).

decode(Buf, 0, Acc) -> 
    {reverse(Acc), Buf};
decode([A0,A1,A2,A3 | Buf], N, Acc) ->
    decode(Buf, N-4, [ A0 + (A1 bsl 8) + (A2 bsl 16) + (A3 bsl 24) | Acc]).

%%
  Encodes input 32 - bit ints into byte buffer output .
%%
encode(Xs) -> encode(Xs, []).

encode([X | Xs], Acc) ->
    encode(Xs, [(X bsr 24) band 16#ff,
		(X bsr 16) band 16#ff,
		(X bsr 8) band 16#ff,
		X  band 16#ff | Acc]);
encode([], Acc) -> reverse(Acc).

    
transform({A0,B0,C0,D0}, Xs) ->
    [X0,X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15] = Xs,

    %% Round 1 
    A1 = ?FF (A0, B0, C0, D0, X0, ?S11, 16#d76aa478),
    D1 = ?FF (D0, A1, B0, C0, X1, ?S12, 16#e8c7b756),
    C1 = ?FF (C0, D1, A1, B0, X2, ?S13, 16#242070db),
    B1 = ?FF (B0, C1, D1, A1, X3, ?S14, 16#c1bdceee),

    A2 = ?FF (A1, B1, C1, D1, X4, ?S11, 16#f57c0faf),
    D2 = ?FF (D1, A2, B1, C1, X5, ?S12, 16#4787c62a),
    C2 = ?FF (C1, D2, A2, B1, X6, ?S13, 16#a8304613),
    B2 = ?FF (B1, C2, D2, A2, X7, ?S14, 16#fd469501),

    A3 = ?FF (A2, B2, C2, D2, X8, ?S11, 16#698098d8),
    D3 = ?FF (D2, A3, B2, C2, X9, ?S12, 16#8b44f7af),
    C3 = ?FF (C2, D3, A3, B2, X10, ?S13, 16#ffff5bb1),
    B3 = ?FF (B2, C3, D3, A3, X11, ?S14, 16#895cd7be),

    A4 = ?FF (A3, B3, C3, D3, X12, ?S11, 16#6b901122),
    D4 = ?FF (D3, A4, B3, C3, X13, ?S12, 16#fd987193),
    C4 = ?FF (C3, D4, A4, B3, X14, ?S13, 16#a679438e),
    B4 = ?FF (B3, C4, D4, A4, X15, ?S14, 16#49b40821),

    %% Round 2 
    A5 = ?GG (A4, B4, C4, D4, X1, ?S21, 16#f61e2562),
    D5 = ?GG (D4, A5, B4, C4, X6, ?S22, 16#c040b340),
    C5 = ?GG (C4, D5, A5, B4, X11, ?S23, 16#265e5a51),
    B5 = ?GG (B4, C5, D5, A5, X0, ?S24, 16#e9b6c7aa),

    A6 = ?GG (A5, B5, C5, D5, X5, ?S21, 16#d62f105d),
    D6 = ?GG (D5, A6, B5, C5, X10, ?S22,  16#2441453),
    C6 = ?GG (C5, D6, A6, B5, X15, ?S23, 16#d8a1e681),
    B6 = ?GG (B5, C6, D6, A6, X4, ?S24, 16#e7d3fbc8),

    A7 = ?GG (A6, B6, C6, D6, X9, ?S21, 16#21e1cde6),
    D7 = ?GG (D6, A7, B6, C6, X14, ?S22, 16#c33707d6),
    C7 = ?GG (C6, D7, A7, B6, X3, ?S23, 16#f4d50d87),
    B7 = ?GG (B6, C7, D7, A7, X8, ?S24, 16#455a14ed),

    A8 = ?GG (A7, B7, C7, D7, X13, ?S21, 16#a9e3e905),
    D8 = ?GG (D7, A8, B7, C7, X2, ?S22, 16#fcefa3f8),
    C8 = ?GG (C7, D8, A8, B7, X7, ?S23, 16#676f02d9),
    B8 = ?GG (B7, C8, D8, A8, X12, ?S24, 16#8d2a4c8a),

 %% Round 3
    A9 = ?HH (A8, B8, C8, D8, X5, ?S31, 16#fffa3942),
    D9 = ?HH (D8, A9, B8, C8, X8, ?S32, 16#8771f681),
    C9 = ?HH (C8, D9, A9, B8, X11, ?S33, 16#6d9d6122),
    B9 = ?HH (B8, C9, D9, A9, X14, ?S34, 16#fde5380c),

    A10 = ?HH (A9, B9, C9, D9, X1, ?S31, 16#a4beea44),
    D10 = ?HH (D9, A10, B9, C9, X4, ?S32, 16#4bdecfa9),
    C10 = ?HH (C9, D10, A10, B9, X7, ?S33, 16#f6bb4b60),
    B10 = ?HH (B9, C10, D10, A10, X10, ?S34, 16#bebfbc70),

    A11 = ?HH (A10, B10, C10, D10, X13, ?S31, 16#289b7ec6),
    D11 = ?HH (D10, A11, B10, C10, X0, ?S32, 16#eaa127fa),
    C11 = ?HH (C10, D11, A11, B10, X3, ?S33, 16#d4ef3085),
    B11 = ?HH (B10, C11, D11, A11, X6, ?S34,  16#4881d05),

    A12 = ?HH (A11, B11, C11, D11, X9, ?S31, 16#d9d4d039),
    D12 = ?HH (D11, A12, B11, C11, X12, ?S32, 16#e6db99e5),
    C12 = ?HH (C11, D12, A12, B11, X15, ?S33, 16#1fa27cf8),
    B12 = ?HH (B11, C12, D12, A12, X2, ?S34, 16#c4ac5665),

 %% Round 4
    A13 = ?II (A12, B12, C12, D12, X0, ?S41, 16#f4292244),
    D13 = ?II (D12, A13, B12, C12, X7, ?S42, 16#432aff97),
    C13 = ?II (C12, D13, A13, B12, X14, ?S43, 16#ab9423a7),
    B13 = ?II (B12, C13, D13, A13, X5, ?S44, 16#fc93a039),

    A14 = ?II (A13, B13, C13, D13, X12, ?S41, 16#655b59c3),
    D14 = ?II (D13, A14, B13, C13, X3, ?S42, 16#8f0ccc92),
    C14 = ?II (C13, D14, A14, B13, X10, ?S43, 16#ffeff47d),
    B14 = ?II (B13, C14, D14, A14, X1, ?S44, 16#85845dd1),

    A15 = ?II (A14, B14, C14, D14, X8, ?S41, 16#6fa87e4f),
    D15 = ?II (D14, A15, B14, C14, X15, ?S42, 16#fe2ce6e0),
    C15 = ?II (C14, D15, A15, B14, X6, ?S43, 16#a3014314),
    B15 = ?II (B14, C15, D15, A15, X13, ?S44, 16#4e0811a1),

    A16 = ?II (A15, B15, C15, D15, X4, ?S41, 16#f7537e82),
    D16 = ?II (D15, A16, B15, C15, X11, ?S42, 16#bd3af235),
    C16 = ?II (C15, D16, A16, B15, X2, ?S43, 16#2ad7d2bb),
    B16 = ?II (B15, C16, D16, A16, X9, ?S44, 16#eb86d391),
    
    {?U32(A0+A16), ?U32(B0+B16), ?U32(C0+C16), ?U32(D0+D16)}.


 --------------------------------------------------------------------
 File    : md5.erl
 ====================================================================
 License to copy and use this software is granted provided that it
 Algorithm" in all material mentioning or referencing this software
 or this function.
  
 License is also granted to make and use derivative works provided
 mentioning or referencing the derived work.
 ====================================================================
           Made it into an epop module and cleaned it up a bit.
 --------------------------------------------------------------------
 Rotation is separate from addition to prevent recomputation.

 ---------------------------------
 Exported function: string/1
 Do a message digest on a string
 ---------------------------------

 update state, count reflects number of bytes 
 including bytes in buffer

 produce a digest
 number of bits
 number of bytes
 generate padding info to final    




 Round 1 
 Round 2 
 Round 3
 Round 4
-module(epop_md5).
-author('').

  Author   :   <  >
  Purpose : Implementation of MD5 in erlang
  Created : 30 Oct 1997 by   <  >
  is identified as the " RSA Data Security , Inc. MD5 Message - Digest
  that such works are identified as " derived from the RSA Data
  Security , Inc. MD5 Message - Digest Algorithm " in all material
  Adopted : 17 Aug 1998 by 

-export([string/1]).

-import(lists, [reverse/1]).

-record(md5_ctx, 
	{
	 state = { 16#67452301, 16#efcdab89, 16#98badcfe, 16#10325476 },
  number of bits ( 64 bit )
  input buffer ( 16 bytes )
	}).

-define(S11, 7).
-define(S12, 12).
-define(S13, 17).
-define(S14, 22).
-define(S21, 5).
-define(S22, 9).
-define(S23, 14).
-define(S24, 20).
-define(S31, 4).
-define(S32, 11).
-define(S33, 16).
-define(S34, 23).
-define(S41, 6).
-define(S42, 10).
-define(S43, 15).
-define(S44, 21).

  F , G , H and I are basic MD5 functions .

-define(F(X, Y, Z), (((X) band (Y)) bor ((bnot (X)) band (Z)))).
-define(G(X, Y, Z), (((X) band (Z)) bor ((Y) band (bnot (Z))))).
-define(H(X, Y, Z), ((X) bxor (Y) bxor (Z))).
-define(I(X, Y, Z), ((Y) bxor ((X) bor (bnot (Z))))).

-define(U32(X), ((X) band 16#ffffffff)).

-define(ROTATE_LEFT(X,N), rotate_left(X,N)).

  FF , GG , HH , and II transformations for rounds 1 , 2 , 3 , and 4 .
-define(FF(A, B, C, D, X, S, AC),
	?ROTATE_LEFT(A + ?F((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define(GG(A, B, C, D, X, S, AC), 
	?ROTATE_LEFT(A + ?G((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define( HH(A, B, C, D, X, S, AC), 
	?ROTATE_LEFT(A + ?H((B), (C), (D)) + (X) + (AC),(S)) + (B)).

-define(II(A, B, C, D, X, S, AC),
	?ROTATE_LEFT(A +  ?I((B), (C), (D)) + (X) + (AC),(S)) + (B)).


string(Str) ->
    format(final(update(init(), Str))).

format([X | Xs]) ->
    [hex(X bsr 4), hex(X) | format(Xs)];
format([]) -> [].

hex(X) ->
    X4 = (X band 16#f),
    if X4 < 10 -> X4 + $0;
       true -> (X4-10) + $a
    end.

init() ->
    #md5_ctx {}.

update(CTX, Input) ->
    Buffer = CTX#md5_ctx.buffer,
    LenI = length(Input),
    Len = LenI + length(Buffer),
    update(Buffer ++ Input, Len,CTX#md5_ctx.state,
	   CTX#md5_ctx.count+(LenI bsl 3)).

update(Buf0, Len0, State0, Count) when Len0 >= 64 ->
    {Xs,Buf1} = decode(Buf0, 64),
    State1 = transform(State0, Xs),
    update(Buf1, Len0 - 64, State1, Count);
update(Buf0, Len0, State0, Count) ->
    #md5_ctx { state = State0, count = Count, buffer = Buf0 }.

final(CTX) ->
  pad out to a length 56 ( we later add a count that makes 64 )
    PadLen = if Index < 56 ->
		     56 - Index;
		true -> 120 - Index
	     end,
    CTX1 = update(CTX, padding(PadLen,[])),
    CTX2 = update(CTX1, encode([?U32(Count), ?U32(Count bsr 32)])),
    encode(tuple_to_list(CTX2#md5_ctx.state)).

padding(0,Acc) -> Acc;
padding(1,Acc) -> [16#80 | Acc];
padding(N,Acc) -> padding(N-1, [0 | Acc]).

  rotate X as 32 - bit unsigned left N bits
rotate_left(X, N) ->
    ?U32(X bsl N) bor (?U32(X) bsr (32 - N)).

  decodes  number of bytes into 32 bit integers
  returns     }
decode(Buf, Len) ->
    decode(Buf, Len, []).

decode(Buf, 0, Acc) -> 
    {reverse(Acc), Buf};
decode([A0,A1,A2,A3 | Buf], N, Acc) ->
    decode(Buf, N-4, [ A0 + (A1 bsl 8) + (A2 bsl 16) + (A3 bsl 24) | Acc]).

  Encodes input 32 - bit ints into byte buffer output .
encode(Xs) -> encode(Xs, []).

encode([X | Xs], Acc) ->
    encode(Xs, [(X bsr 24) band 16#ff,
		(X bsr 16) band 16#ff,
		(X bsr 8) band 16#ff,
		X  band 16#ff | Acc]);
encode([], Acc) -> reverse(Acc).

    
transform({A0,B0,C0,D0}, Xs) ->
    [X0,X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11,X12,X13,X14,X15] = Xs,

    A1 = ?FF (A0, B0, C0, D0, X0, ?S11, 16#d76aa478),
    D1 = ?FF (D0, A1, B0, C0, X1, ?S12, 16#e8c7b756),
    C1 = ?FF (C0, D1, A1, B0, X2, ?S13, 16#242070db),
    B1 = ?FF (B0, C1, D1, A1, X3, ?S14, 16#c1bdceee),

    A2 = ?FF (A1, B1, C1, D1, X4, ?S11, 16#f57c0faf),
    D2 = ?FF (D1, A2, B1, C1, X5, ?S12, 16#4787c62a),
    C2 = ?FF (C1, D2, A2, B1, X6, ?S13, 16#a8304613),
    B2 = ?FF (B1, C2, D2, A2, X7, ?S14, 16#fd469501),

    A3 = ?FF (A2, B2, C2, D2, X8, ?S11, 16#698098d8),
    D3 = ?FF (D2, A3, B2, C2, X9, ?S12, 16#8b44f7af),
    C3 = ?FF (C2, D3, A3, B2, X10, ?S13, 16#ffff5bb1),
    B3 = ?FF (B2, C3, D3, A3, X11, ?S14, 16#895cd7be),

    A4 = ?FF (A3, B3, C3, D3, X12, ?S11, 16#6b901122),
    D4 = ?FF (D3, A4, B3, C3, X13, ?S12, 16#fd987193),
    C4 = ?FF (C3, D4, A4, B3, X14, ?S13, 16#a679438e),
    B4 = ?FF (B3, C4, D4, A4, X15, ?S14, 16#49b40821),

    A5 = ?GG (A4, B4, C4, D4, X1, ?S21, 16#f61e2562),
    D5 = ?GG (D4, A5, B4, C4, X6, ?S22, 16#c040b340),
    C5 = ?GG (C4, D5, A5, B4, X11, ?S23, 16#265e5a51),
    B5 = ?GG (B4, C5, D5, A5, X0, ?S24, 16#e9b6c7aa),

    A6 = ?GG (A5, B5, C5, D5, X5, ?S21, 16#d62f105d),
    D6 = ?GG (D5, A6, B5, C5, X10, ?S22,  16#2441453),
    C6 = ?GG (C5, D6, A6, B5, X15, ?S23, 16#d8a1e681),
    B6 = ?GG (B5, C6, D6, A6, X4, ?S24, 16#e7d3fbc8),

    A7 = ?GG (A6, B6, C6, D6, X9, ?S21, 16#21e1cde6),
    D7 = ?GG (D6, A7, B6, C6, X14, ?S22, 16#c33707d6),
    C7 = ?GG (C6, D7, A7, B6, X3, ?S23, 16#f4d50d87),
    B7 = ?GG (B6, C7, D7, A7, X8, ?S24, 16#455a14ed),

    A8 = ?GG (A7, B7, C7, D7, X13, ?S21, 16#a9e3e905),
    D8 = ?GG (D7, A8, B7, C7, X2, ?S22, 16#fcefa3f8),
    C8 = ?GG (C7, D8, A8, B7, X7, ?S23, 16#676f02d9),
    B8 = ?GG (B7, C8, D8, A8, X12, ?S24, 16#8d2a4c8a),

    A9 = ?HH (A8, B8, C8, D8, X5, ?S31, 16#fffa3942),
    D9 = ?HH (D8, A9, B8, C8, X8, ?S32, 16#8771f681),
    C9 = ?HH (C8, D9, A9, B8, X11, ?S33, 16#6d9d6122),
    B9 = ?HH (B8, C9, D9, A9, X14, ?S34, 16#fde5380c),

    A10 = ?HH (A9, B9, C9, D9, X1, ?S31, 16#a4beea44),
    D10 = ?HH (D9, A10, B9, C9, X4, ?S32, 16#4bdecfa9),
    C10 = ?HH (C9, D10, A10, B9, X7, ?S33, 16#f6bb4b60),
    B10 = ?HH (B9, C10, D10, A10, X10, ?S34, 16#bebfbc70),

    A11 = ?HH (A10, B10, C10, D10, X13, ?S31, 16#289b7ec6),
    D11 = ?HH (D10, A11, B10, C10, X0, ?S32, 16#eaa127fa),
    C11 = ?HH (C10, D11, A11, B10, X3, ?S33, 16#d4ef3085),
    B11 = ?HH (B10, C11, D11, A11, X6, ?S34,  16#4881d05),

    A12 = ?HH (A11, B11, C11, D11, X9, ?S31, 16#d9d4d039),
    D12 = ?HH (D11, A12, B11, C11, X12, ?S32, 16#e6db99e5),
    C12 = ?HH (C11, D12, A12, B11, X15, ?S33, 16#1fa27cf8),
    B12 = ?HH (B11, C12, D12, A12, X2, ?S34, 16#c4ac5665),

    A13 = ?II (A12, B12, C12, D12, X0, ?S41, 16#f4292244),
    D13 = ?II (D12, A13, B12, C12, X7, ?S42, 16#432aff97),
    C13 = ?II (C12, D13, A13, B12, X14, ?S43, 16#ab9423a7),
    B13 = ?II (B12, C13, D13, A13, X5, ?S44, 16#fc93a039),

    A14 = ?II (A13, B13, C13, D13, X12, ?S41, 16#655b59c3),
    D14 = ?II (D13, A14, B13, C13, X3, ?S42, 16#8f0ccc92),
    C14 = ?II (C13, D14, A14, B13, X10, ?S43, 16#ffeff47d),
    B14 = ?II (B13, C14, D14, A14, X1, ?S44, 16#85845dd1),

    A15 = ?II (A14, B14, C14, D14, X8, ?S41, 16#6fa87e4f),
    D15 = ?II (D14, A15, B14, C14, X15, ?S42, 16#fe2ce6e0),
    C15 = ?II (C14, D15, A15, B14, X6, ?S43, 16#a3014314),
    B15 = ?II (B14, C15, D15, A15, X13, ?S44, 16#4e0811a1),

    A16 = ?II (A15, B15, C15, D15, X4, ?S41, 16#f7537e82),
    D16 = ?II (D15, A16, B15, C15, X11, ?S42, 16#bd3af235),
    C16 = ?II (C15, D16, A16, B15, X2, ?S43, 16#2ad7d2bb),
    B16 = ?II (B15, C16, D16, A16, X9, ?S44, 16#eb86d391),
    
    {?U32(A0+A16), ?U32(B0+B16), ?U32(C0+C16), ?U32(D0+D16)}.

