[erlang] initial checkin of Erlang client
This commit is contained in:
parent
00669b2be0
commit
88cff80690
16
erlang/LICENSE
Normal file
16
erlang/LICENSE
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
22
erlang/README.md
Normal file
22
erlang/README.md
Normal file
@ -0,0 +1,22 @@
|
||||
fwknop
|
||||
=====
|
||||
|
||||
Experimental native erlang fwknop client with Rijndael support.
|
||||
|
||||
Build
|
||||
-----
|
||||
|
||||
$ rebar3 compile
|
||||
$ rebar3 eunit
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
$ rebar3 shell
|
||||
1> fwknop:knock("spaserver.domain.com", 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>
|
||||
5
erlang/rebar.config
Normal file
5
erlang/rebar.config
Normal file
@ -0,0 +1,5 @@
|
||||
{erl_opts, [debug_info]}.
|
||||
{deps, [
|
||||
{pkcs7, {git, "https://github.com/camshaft/pkcs7.erl"}}
|
||||
]
|
||||
}.
|
||||
4
erlang/rebar.lock
Normal file
4
erlang/rebar.lock
Normal file
@ -0,0 +1,4 @@
|
||||
[{<<"pkcs7">>,
|
||||
{git,"https://github.com/camshaft/pkcs7.erl",
|
||||
{ref,"00218999ce9b60848aa1f36612248d1079c7ef06"}},
|
||||
0}].
|
||||
16
erlang/src/fwknop.app.src
Normal file
16
erlang/src/fwknop.app.src
Normal file
@ -0,0 +1,16 @@
|
||||
{application, fwknop,
|
||||
[{description, "An OTP application"},
|
||||
{vsn, "0.1.0"},
|
||||
{registered, []},
|
||||
{mod, { fwknop_app, []}},
|
||||
{applications,
|
||||
[kernel,
|
||||
stdlib
|
||||
]},
|
||||
{env,[]},
|
||||
{modules, []},
|
||||
|
||||
{maintainers, []},
|
||||
{licenses, []},
|
||||
{links, []}
|
||||
]}.
|
||||
85
erlang/src/fwknop.erl
Normal file
85
erlang/src/fwknop.erl
Normal file
@ -0,0 +1,85 @@
|
||||
%% 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( fwknop ).
|
||||
-compile( export_all ).
|
||||
|
||||
-define( FwknopVersion, "2.0.2" ).
|
||||
-define( CommandMode, 0 ).
|
||||
-define( AccessMode, 1 ).
|
||||
|
||||
-import( lists, [ dropwhile/2, nth/2, reverse/1, seq/2, split/2 ] ).
|
||||
-import( pkcs7, [ pad/1 ] ).
|
||||
|
||||
packet( Proto, Ip, Port, RijndaelKeyB64, HmacKeyB64 ) ->
|
||||
E64 = fun( Bin ) -> base64:encode( Bin ) end,
|
||||
|
||||
RijndaelKey = base64:decode( RijndaelKeyB64 ),
|
||||
HmacKey = base64:decode( HmacKeyB64 ),
|
||||
|
||||
Rand = random_digits( 16 ),
|
||||
User = E64( os:getenv( "USER" ) ),
|
||||
Version = ?FwknopVersion,
|
||||
MsgType = integer_to_list( ?AccessMode ),
|
||||
Request = strip_base64(E64( list_to_binary( io_lib:format( "~s,~s/~b", [ Ip, Proto, Port ] ) ) ) ),
|
||||
Time = timestamp(),
|
||||
|
||||
Message = list_to_binary( io_lib:format( "~s:~s:~p:~s:~s:~s", [ Rand, User, Time, Version, MsgType, Request ] ) ),
|
||||
error_logger:info_msg( "Message: ~s~n", [ Message ] ),
|
||||
Digest = strip_base64( E64( crypto:hash( sha256, Message ) ) ),
|
||||
|
||||
Plaintext = pkcs7:pad( <<Message/binary, <<":">>/binary, Digest/binary>> ),
|
||||
Salt = crypto:strong_rand_bytes( 8 ),
|
||||
|
||||
{Key, IV} = pbkdf1( Salt, RijndaelKey ),
|
||||
Magic = <<"Salted__">>,
|
||||
|
||||
Ciphertext = crypto:block_encrypt( aes_cbc256, Key, IV, Plaintext ),
|
||||
SpaData = strip_base64( E64( <<Magic/binary, Salt/binary, Ciphertext/binary>> ) ),
|
||||
|
||||
Hmac = strip_base64( E64( crypto:hmac( sha256, HmacKey, SpaData ) ) ),
|
||||
error_logger:info_msg( "HMAC: ~s~n", [ Hmac ] ),
|
||||
|
||||
% strip encoded magic word
|
||||
SpaData2 = binary:part(SpaData, {10, size(SpaData) - 10} ),
|
||||
|
||||
<< SpaData2/binary, Hmac/binary>>.
|
||||
|
||||
%
|
||||
% miscellaneous utilities
|
||||
%
|
||||
pbkdf1( Salt, Key ) ->
|
||||
Round1 = erlang:md5( <<Key/binary, Salt/binary>> ),
|
||||
Round2 = erlang:md5( <<Round1/binary, Key/binary, Salt/binary>> ),
|
||||
Round3 = erlang:md5( <<Round2/binary, Key/binary, Salt/binary>> ),
|
||||
{ <<Round1/binary, Round2/binary>>, <<Round3/binary>> }.
|
||||
|
||||
strip_base64( Bin ) ->
|
||||
F = fun( C ) -> C == $= end,
|
||||
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 ) ] ).
|
||||
|
||||
timestamp() ->
|
||||
{ A, B, _ } = os:timestamp(),
|
||||
list_to_integer( integer_to_list( (A * 1000000) + B ) ).
|
||||
|
||||
knock( Host, Port, RijndaelKeyB64, HmacKeyB64, { Proto, SrcIp, DstPort } ) ->
|
||||
Packet = packet( Proto, SrcIp, DstPort, RijndaelKeyB64, HmacKeyB64 ),
|
||||
{ ok, Socket } = gen_udp:open( 0, [binary] ),
|
||||
gen_udp:send( Socket, Host, Port, Packet ).
|
||||
38
erlang/src/fwknop_tests.erl
Normal file
38
erlang/src/fwknop_tests.erl
Normal file
@ -0,0 +1,38 @@
|
||||
%% 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( fwknop_tests ).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
pbkdf1_test() ->
|
||||
Salt = <<0,0,0,0,0,0,0,0>>,
|
||||
RijndaelKey = base64:decode("Sz80RjpXOlhH2olGuKBUamHKcqyMBsS9BTgLaMugUsg="),
|
||||
|
||||
ExpectedKey = <<80,137,195,6,117,8,63,199,226,93,78,205,231,238,241,80,217,161,149,164,60,102,129,175,81,53,82,23,137,50,236,37>>,
|
||||
ExpectedIV = <<56,251,47,154,60,96,84,106,192,163,161,216,59,202,166,203>>,
|
||||
|
||||
{ExpectedKey, ExpectedIV} = fwknop:pbkdf1(Salt, RijndaelKey).
|
||||
|
||||
strip_base64_test() ->
|
||||
Encoded = base64:encode( "Salted" ),
|
||||
Encoded2 = base64:encode( "Salted_" ),
|
||||
Encoded3 = base64:encode( "Salted__" ),
|
||||
|
||||
<< "U2FsdGVk" >> = fwknop:strip_base64(Encoded),
|
||||
<< "U2FsdGVkXw" >> = fwknop:strip_base64(Encoded2),
|
||||
<< "U2FsdGVkX18" >> = fwknop:strip_base64(Encoded3).
|
||||
Loading…
x
Reference in New Issue
Block a user