Skip to content

Commit

Permalink
Some formatting applied
Browse files Browse the repository at this point in the history
  • Loading branch information
lhft committed Feb 11, 2013
1 parent 68c48b1 commit 1f353dd
Showing 1 changed file with 126 additions and 82 deletions.
208 changes: 126 additions & 82 deletions src/mochiweb_session.erl
Original file line number Diff line number Diff line change
@@ -1,71 +1,97 @@
%% @author Asier Azkuenaga Batiz <asier@zebixe.com>


%% @doc HTTP Cookie session. Note that the expiration time travels unencrypted as far as this module concerns.
%% @doc HTTP Cookie session. Note that the expiration time travels unencrypted
%% as far as this module concerns.
%% In order to achieve more security, it is adviced to use https

-module(mochiweb_session).
-export([generate_session_data/4,generate_session_cookie/4,check_session_cookie/4]).


%% @spec generate_session_data(ExpirationTime,Data :: string(),FSessionKey : function(A),ServerKey) -> binary()
%% @doc generates a secure encrypted binary convining all the parameters.
%% The expiration time is a number that must be able to be represented on 32 bit.
generate_session_data(ExpirationTime,Data,FSessionKey,ServerKey) when is_integer(ExpirationTime),is_list(Data), is_function(FSessionKey)->
BData=ensure_binary(Data),
ExpTime= integer_to_list(ExpirationTime),
Key=gen_key(ExpTime,ServerKey),
Hmac=gen_hmac(ExpTime,BData,FSessionKey(ExpTime),Key),
EData=encrypt_data(BData,Key),
base64:encode(<< ExpirationTime:32/integer,Hmac/binary, EData/binary>>).

%% @spec generate_session_data(UserName,ExpirationTime,SessionExtraData,FSessionKey : function(A),ServerKey) -> mochiweb_cookie()
-export([generate_session_data/4, generate_session_cookie/4,
check_session_cookie/4]).

%% @spec generate_session_data(ExpirationTime, Data, FSessionKey, ServerKey)
%% -> binary()
%% ExpirationTime = integer(),
%% Data = string(),
%% FSessionKey = function(A),
%% ServerKey = string(),
%% @doc generates a secure encrypted binary convining all the parameters. The
%% expiration time is a number represented on 32 bit.
generate_session_data(ExpirationTime, Data, FSessionKey, ServerKey)
when is_integer(ExpirationTime), is_function(FSessionKey)->
BData = ensure_binary(Data),
ExpTime = integer_to_list(ExpirationTime),
Key = gen_key(ExpTime,ServerKey),
Hmac = gen_hmac(ExpTime,BData,FSessionKey(ExpTime),Key),
EData = encrypt_data(BData,Key),
base64:encode(<< ExpirationTime:32/integer, Hmac/binary, EData/binary >>).

%% @spec generate_session_data(UserName, ExpirationTime, SessionExtraData,
%% FSessionKey,ServerKey) -> mochiweb_cookie()
%% ExpirationTime = integer(),
%% Data = string(),
%% FSessionKey = function(A),
%% ServerKey = string(),
%% @doc generates a secure encrypted binary convining all the parameters.
%% The expiration time is a number that must be able to be represented on 32 bit.
%% This function conveniently generates a mochiweb cookie using the "id" as key and current local time as local time
generate_session_cookie(ExpirationTime,Data,FSessionKey,ServerKey) when is_integer(ExpirationTime),is_list(Data), is_function(FSessionKey)->
CookieData=generate_session_data(ExpirationTime,Data,FSessionKey,ServerKey),
mochiweb_cookies:cookie("id",CookieData,[{max_age,20000},{local_time,calendar:universal_time_to_local_time(calendar:universal_time())}]).

%% @spec cookie_check_session(RawData,ExpirationTime,FSessionKey, ServerKey)->{false,[ExpirationTime,Data]} |
%% {false,[]} |
%% {true,[ExpirationTime,Data]},
%% RawData = binary() ,
%% ExpirationTime = integer(),
%% FSessionKey = function(A) ,
%% ServerKey = string()
%% The expiration time is a number represented on 32 bit.
%% This function conveniently generates a mochiweb cookie using the "id"
%% as key and current local time as local time
generate_session_cookie(ExpirationTime, Data, FSessionKey, ServerKey)
when is_integer(ExpirationTime), is_function(FSessionKey)->
CookieData = generate_session_data(ExpirationTime, Data,
FSessionKey, ServerKey),
mochiweb_cookies:cookie("id", CookieData,
[{max_age, 20000},
{local_time,
calendar:universal_time_to_local_time(
calendar:universal_time())}]).

%% @spec cookie_check_session(RawData,ExpirationTime,FSessionKey, ServerKey)->
%% {false,[ExpirationTime,Data]} |
%% {false,[]} |
%% {true,[ExpirationTime,Data]},
%% RawData = binary() ,
%% ExpirationTime = integer(),
%% FSessionKey = function(A) ,
%% ServerKey = string()
check_session_cookie(undefined,_,_,_) ->
{false,[]};
{false, []};
check_session_cookie([],_,_,_) ->
{false,[]};
check_session_cookie(ECookie,ExpirationTime,FSessionKey,ServerKey) when is_binary(ECookie), is_integer(ExpirationTime), is_function(FSessionKey)->
<<ExpirationTime1:32/integer,BHmac:20/binary,EData/binary>> = base64:decode(ECookie),
Key=gen_key(integer_to_list(ExpirationTime1),ServerKey),
Data=decrypt_data(EData,Key),
Hmac2=gen_hmac(integer_to_list(ExpirationTime1),Data,FSessionKey(integer_to_list(ExpirationTime1)),Key),
if ExpirationTime1<ExpirationTime -> {false,[ExpirationTime1,binary_to_list(Data)]};
{false, []};
check_session_cookie(ECookie, ExpirationTime, FSessionKey, ServerKey)
when is_binary(ECookie), is_integer(ExpirationTime),
is_function(FSessionKey)->
<< ExpirationTime1:32/integer, BHmac:20/binary, EData/binary >> =
base64:decode(ECookie),
Key = gen_key(integer_to_list(ExpirationTime1), ServerKey),
Data = decrypt_data(EData, Key),
Hmac2 = gen_hmac(integer_to_list(ExpirationTime1), Data,
FSessionKey(integer_to_list(ExpirationTime1)), Key),
if ExpirationTime1 < ExpirationTime ->
{false, [ExpirationTime1, binary_to_list(Data)]};
true ->
if Hmac2==BHmac -> {true,[ExpirationTime1,binary_to_list(Data)]};
true -> {false,[ExpirationTime1,binary_to_list(Data)]}
if Hmac2 == BHmac ->
{true, [ExpirationTime1, binary_to_list(Data)]};
true ->
{false, [ExpirationTime1, binary_to_list(Data)]}
end
end.

ensure_binary(B) when is_binary(B) ->
B;
ensure_binary(L) when is_list(L) ->
iolist_to_binary(L). %note, iolist_to_binary is better, since we might use something like mochijson2 here
iolist_to_binary(L).

encrypt_data(Data,Key) ->
encrypt_data(Data, Key) ->
IV = crypto:rand_bytes(16),
Crypt=crypto:aes_cfb_128_encrypt(Key, IV, Data),
<<IV/binary,Crypt/binary>>.
decrypt_data(<<IV:16/binary,Crypt/binary>>,Key) ->
crypto:aes_cfb_128_decrypt(Key, IV,Crypt).
Crypt = crypto:aes_cfb_128_encrypt(Key, IV, Data),
<< IV/binary, Crypt/binary >>.
decrypt_data(<< IV:16/binary, Crypt/binary >>, Key) ->
crypto:aes_cfb_128_decrypt(Key, IV, Crypt).

gen_key(ExpirationTime,ServerKey)->
gen_key(ExpirationTime, ServerKey)->
crypto:md5_mac(ServerKey, [ExpirationTime]).
gen_hmac(ExpirationTime,Data,SessionKey,Key)->
crypto:sha_mac(Key,[ExpirationTime,Data,SessionKey]).
gen_hmac(ExpirationTime, Data, SessionKey, Key)->
crypto:sha_mac(Key, [ExpirationTime, Data, SessionKey]).


-ifdef(TEST).
Expand All @@ -79,38 +105,56 @@ generate_check_session_cookie_test_()->
server_key()->%setup function
["adfasdfasfs",30000].

generate_check_session_cookie([ServerKey,TimeStamp]) ->
[?_assertEqual({true,[TimeStamp+1000,"alice"]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertEqual({true,[TimeStamp+1000,"alice and"]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice and",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),


?_assertEqual({true,[TimeStamp+1000,"alice and"]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice and",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),


?_assertEqual({true,[TimeStamp+1000,"alice and bob"]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice and bob",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertEqual({true,[TimeStamp+1000,"alice jlkjfkjsdfg sdkfjgldsjgl"]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice jlkjfkjsdfg sdkfjgldsjgl",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertEqual({true,[TimeStamp+1000,"alice .'¡'ç+-$%/(&\""]},
check_session_cookie(generate_session_data(TimeStamp+1000,"alice .'¡'ç+-$%/(&\"",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertEqual({true,[TimeStamp+1000,"alice456689875"]},
check_session_cookie(generate_session_data(TimeStamp+1000,["alice","456689875"],fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertError(function_clause,
check_session_cookie(generate_session_data(TimeStamp+1000,{tuple,one},fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey)),
?_assertEqual({false,[TimeStamp-1,"bob"]},
check_session_cookie(generate_session_data(TimeStamp-1,"bob",fun(A)-> A end,ServerKey),
TimeStamp,fun(A)-> A end,ServerKey))%current timestamp newer than cookie, it's expired
generate_check_session_cookie([ServerKey, TS]) ->
[?_assertEqual(
{true, [TS+1000, "alice"]},
check_session_cookie(
generate_session_data(TS+1000, "alice", fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertEqual(
{true, [TS+1000, "alice and"]},
check_session_cookie(
generate_session_data(TS+1000, "alice and", fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertEqual(
{true, [TS+1000, "alice and"]},
check_session_cookie(
generate_session_data(TS+1000, "alice and", fun(A)-> A end, ServerKey),
TS, fun(A)-> A end,ServerKey)),
?_assertEqual(
{true, [TS+1000, "alice and bob"]},
check_session_cookie(
generate_session_data(TS+1000, "alice and bob",
fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertEqual(
{true, [TS+1000, "alice jlkjfkjsdfg sdkfjgldsjgl"]},
check_session_cookie(
generate_session_data(TS+1000, "alice jlkjfkjsdfg sdkfjgldsjgl",
fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertEqual(
{true, [TS+1000, "alice .'¡'ç+-$%/(&\""]},
check_session_cookie(
generate_session_data(TS+1000, "alice .'¡'ç+-$%/(&\""
,fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertEqual(
{true,[TS+1000,"alice456689875"]},
check_session_cookie(
generate_session_data(TS+1000, ["alice","456689875"],
fun(A)-> A end, ServerKey),
TS, fun(A)-> A end, ServerKey)),
?_assertError(
function_clause,
check_session_cookie(
generate_session_data(TS+1000, {tuple,one},
fun(A)-> A end, ServerKey),
TS, fun(A)-> A end,ServerKey)),
?_assertEqual(
{false, [TS-1, "bob"]},
check_session_cookie(
generate_session_data(TS-1, "bob", fun(A)-> A end,ServerKey),
TS, fun(A)-> A end, ServerKey))
].

-endif.

0 comments on commit 1f353dd

Please sign in to comment.