Merge pull request #260 from mstair/erlang_server
Adding Erlang server component
This commit is contained in:
commit
576d09afbf
@ -13,10 +13,13 @@ Usage
|
||||
-----
|
||||
|
||||
$ rebar3 shell
|
||||
1> fwknop:knock("spaserver.domain.com", 62201, "Sz80RjpXOlhH2olGuKBUamHKcqyMBsS9BTgLaMugUsg=", "c0TOaMJ2aVPdYTh4Aa25Dwxni7PrLo2zLAtBoVwSepkvH6nLcW45Cjb9zaEC2SQd03kaaV+Ckx3FhCh5ohNM5Q==", { tcp, "1.1.1.1", 22 } ).
|
||||
1> server:start("Sz80RjpXOlhH2olGuKBUamHKcqyMBsS9BTgLaMugUsg=", "c0TOaMJ2aVPdYTh4Aa25Dwxni7PrLo2zLAtBoVwSepkvH6nLcW45Cjb9zaEC2SQd03kaaV+Ckx3FhCh5ohNM5Q==").
|
||||
2> fwknop:knock("localhost", 62201, "Sz80RjpXOlhH2olGuKBUamHKcqyMBsS9BTgLaMugUsg=", "c0TOaMJ2aVPdYTh4Aa25Dwxni7PrLo2zLAtBoVwSepkvH6nLcW45Cjb9zaEC2SQd03kaaV+Ckx3FhCh5ohNM5Q==", { tcp, "1.1.1.1", 22 } ).
|
||||
=INFO REPORT==== 15-Nov-2016::15:49:16 ===
|
||||
Message: 0428888364523312:bXMxNzg0:1479224956:2.0.2:1:MTI3LjAuMC4xLHRjcC84NDQz
|
||||
=INFO REPORT==== 15-Nov-2016::15:49:16 ===
|
||||
HMAC: KDZrTwZ+gFpqgzk8+BCXvYhRCxCzk084UyNzhihiWLU
|
||||
ok
|
||||
2>
|
||||
Client request:{127,0,0,1}
|
||||
Got message: <<"1.1.1.1,tcp/22">>
|
||||
3>
|
||||
|
||||
@ -73,7 +73,7 @@ strip_base64( Bin ) ->
|
||||
list_to_binary( reverse( dropwhile( F, reverse( binary_to_list( Bin ) ) ) ) ).
|
||||
|
||||
random_digits( N ) ->
|
||||
list_to_binary( [ nth( crypto:rand_uniform( 1, 10 ), "0123456789" ) || _ <- seq( 1, N ) ] ).
|
||||
list_to_binary( [ nth( rand:uniform( 10 ), "0123456789" ) || _ <- seq( 1, N ) ] ).
|
||||
|
||||
timestamp() ->
|
||||
{ A, B, _ } = os:timestamp(),
|
||||
|
||||
102
erlang/src/server.erl
Normal file
102
erlang/src/server.erl
Normal file
@ -0,0 +1,102 @@
|
||||
%% License (GNU General Public License):
|
||||
%%
|
||||
%% This program is free software; you can redistribute it and/or
|
||||
%% modify it under the terms of the GNU General Public License
|
||||
%% as published by the Free Software Foundation; either version 2
|
||||
%% of the License, or (at your option) any later version.
|
||||
%%
|
||||
%% This program is distributed in the hope that it will be useful,
|
||||
%% but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
%% GNU General Public License for more details.
|
||||
%%
|
||||
%% You should have received a copy of the GNU General Public License
|
||||
%% along with this program; if not, write to the Free Software
|
||||
%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||
%% USA
|
||||
|
||||
-module( server ).
|
||||
-compile( export_all ).
|
||||
|
||||
-import( fwknop, [ strip_base64/1, pbkdf1/2 ] ).
|
||||
-export( [ start/2 ] ).
|
||||
|
||||
start(RijndaelKeyB64, HmacKeyB64) ->
|
||||
spawn(fun() -> server(62201, RijndaelKeyB64, HmacKeyB64) end).
|
||||
|
||||
server(Port, RijndaelKeyB64, HmacKeyB64) ->
|
||||
{ok, Socket} = gen_udp:open(Port, [binary, {active, false}]),
|
||||
accept(Socket, { RijndaelKeyB64, HmacKeyB64 } ).
|
||||
|
||||
accept(Socket, { Key, Hmac } = Creds) ->
|
||||
inet:setopts(Socket, [{active, once}]),
|
||||
receive
|
||||
{udp, Socket, Host, _, Bin} ->
|
||||
io:format("Client request:~p~n",[Host]),
|
||||
{ok, Ciphertext} = verify( Bin, Hmac ),
|
||||
{ok, Plaintext} = decrypt( Ciphertext, Key ),
|
||||
{ok, Message} = decode( Plaintext ),
|
||||
error_logger:info_msg( "Got message: ~p~n", [ Message ] ),
|
||||
|
||||
accept( Socket, Creds )
|
||||
end.
|
||||
|
||||
verify( SpaData, HmacKeyB64 ) ->
|
||||
Magic = <<"U2FsdGVkX1">>,
|
||||
HmacKey = base64:decode( HmacKeyB64 ),
|
||||
|
||||
% sha2.h:#define SHA256_B64_LEN 43
|
||||
HmacLen = 43,
|
||||
|
||||
% write something to just split this on size vs two parts
|
||||
DataHmac = binary:part(SpaData, {byte_size(SpaData), -HmacLen}),
|
||||
EncodedMsg = binary:part(SpaData, {0, byte_size(SpaData) - HmacLen}),
|
||||
|
||||
ExpectedHmac = fwknop:strip_base64( base64:encode( crypto:hmac( sha256, HmacKey, << Magic/binary, EncodedMsg/binary >> ) ) ),
|
||||
|
||||
%io:format("DataHmac: ~s~n",[DataHmac]),
|
||||
%io:format("ExpectedHmac: ~s~n",[ExpectedHmac]),
|
||||
|
||||
ExpectedHmac = DataHmac,
|
||||
|
||||
% handle replay-attack (just compare hmacs or decode to digests?)
|
||||
% verify len is a blocksize?
|
||||
|
||||
DecodedData = base64:decode( unstrip_base64( <<Magic/binary, EncodedMsg/binary>> ) ),
|
||||
% skip "Salted__"
|
||||
DecodedMsg = binary:part( DecodedData, {byte_size(DecodedData), -(byte_size(DecodedData) - 8) } ),
|
||||
|
||||
{ ok, DecodedMsg }.
|
||||
|
||||
decrypt( Ciphertext, RijndaelKeyB64 ) ->
|
||||
RijndaelKey = base64:decode( RijndaelKeyB64 ),
|
||||
|
||||
% split again on size
|
||||
Salt = binary:part( Ciphertext, {0, 8} ),
|
||||
EncryptedMsg = binary:part( Ciphertext, {byte_size(Ciphertext), -(byte_size(Ciphertext) - 8) } ),
|
||||
|
||||
{Key, IV} = fwknop:pbkdf1( Salt, RijndaelKey ),
|
||||
|
||||
{ok, crypto:block_decrypt( aes_cbc256, Key, IV, EncryptedMsg ) }.
|
||||
|
||||
decode( Plaintext ) ->
|
||||
Unpadded = pkcs7:unpad( Plaintext ),
|
||||
|
||||
%[Rand, User, Time, Version, MsgType, Request, Digest] = re:split( Unpadded, ":", [{return,binary}]),
|
||||
%io:format( "~s:~s:~s:~s:~s:~s:~s~n", [ Rand, User, Time, Version, MsgType, Request, Digest] ),
|
||||
[_, _, _, _, _, Request, _] = re:split( Unpadded, ":", [{return,binary}]),
|
||||
|
||||
[Ip, Proto, Port] = re:split( base64:decode( unstrip_base64( Request) ), "[,/]", [{return,binary}]),
|
||||
Message = list_to_binary( io_lib:format( "~s,~s/~s", [ Ip, Proto, Port ] ) ),
|
||||
|
||||
{ok, Message}.
|
||||
|
||||
%
|
||||
% miscellaneous utilities
|
||||
%
|
||||
unstrip_base64( Bin ) ->
|
||||
case ( size( Bin ) rem 4 ) of
|
||||
0 -> Bin;
|
||||
N -> Pad = list_to_binary(string:copies("=", (4-N) )),
|
||||
<< Bin/binary, Pad/binary >>
|
||||
end.
|
||||
Loading…
x
Reference in New Issue
Block a user