diff --git a/include/inventory.hrl b/include/inventory.hrl index f74d170..37a7a0f 100644 --- a/include/inventory.hrl +++ b/include/inventory.hrl @@ -48,6 +48,7 @@ cert = <<>> :: binary(), decrypt = <<>> :: binary(), csr = <<>> :: binary(), + cacert = <<>> :: binary(), attributes = #{} :: attribute_list() }). @@ -66,6 +67,7 @@ cert = <<>> :: binary(), decrypt = <<>> :: binary(), csr = <<>> :: binary(), + cacert = <<>> :: binary(), attributes = #{} :: attribute_list() }). diff --git a/include/mqtt_definitions.hrl b/include/mqtt_definitions.hrl index 9cc89e5..d6a4ba6 100644 --- a/include/mqtt_definitions.hrl +++ b/include/mqtt_definitions.hrl @@ -165,6 +165,7 @@ keep_alive = 0 :: integer(), client_identifier = <<>> :: binary(), will_topic = <<>> :: binary(), + will_message = <<>> :: binary(), will_properties = [] :: list(), will_payload = <<>> :: binary(), username = <<>> :: binary(), @@ -271,7 +272,7 @@ packet_type = 0 :: integer(), flags = 0 :: integer(), remaining_length = 0 :: integer(), - variable_header }). + variable_header = none :: none | mqtt_msg_any() }). -record(mqtt_connection_stats,{ client_identifier = <<>> :: binary(), diff --git a/src/inventory.erl b/src/inventory.erl index 7e93338..962a030 100644 --- a/src/inventory.erl +++ b/src/inventory.erl @@ -433,18 +433,22 @@ create_ca(CaName,Password,State,_Pid)-> CaKeyCertFileName = filename:join([CaDir,binary_to_list(CaName) ++ "_cert.pem"]), CaConfigFileName = filename:join([CaDir,binary_to_list(CaName)++".cnf"]), - Cmd0 = io_lib:format("openssl genrsa -passout pass:~s -aes256 -out ~s 4096",[Password,CaKeyFileName]), + Cmd0 = case Password == <<>> of + false -> io_lib:format("openssl genrsa -passout pass:~s -aes256 -out ~s 4096",[Password,CaKeyFileName]); + true -> io_lib:format("openssl genrsa -out ~s 4096",[CaKeyFileName]) + end, _CommandResult0 = os:cmd(Cmd0), _ = file:change_mode(CaKeyFileName,8#0400), -%% io:format("~p> CMD0: ~s, RESULT: ~s~n~n",[Pid,Cmd0,CommandResult0]), - Cmd1 = io_lib:format("openssl req -config ~s -batch -new -x509 -days 3000 -sha256 -extensions v3_ca -passin pass:~s -key ~s -out ~s", - [ CaConfigFileName, - Password, - CaKeyFileName, - CaKeyCertFileName ]), + %% io:format("> CMD0: ~s, RESULT: ~s~n~n",[Cmd0,CommandResult0]), + Cmd1 = case Password == <<>> of + false -> io_lib:format("openssl req -config ~s -batch -new -x509 -days 3000 -sha256 -extensions v3_ca -passin pass:~s -key ~s -out ~s", + [ CaConfigFileName, Password, CaKeyFileName, CaKeyCertFileName ]); + true -> io_lib:format("openssl req -config ~s -batch -new -x509 -days 3000 -sha256 -extensions v3_ca -key ~s -out ~s", + [ CaConfigFileName, CaKeyFileName, CaKeyCertFileName ]) + end, _CommandResult1 = os:cmd(Cmd1), _ = file:change_mode(CaKeyCertFileName,8#0444), -%% io:format("~p> CMD1: ~s, RESULT: ~s~n~n",[Pid,Cmd1,CommandResult1]), + %% io:format("> CMD1: ~s, RESULT: ~s~n~n",[Cmd1,CommandResult1]), ok = file:write_file( filename:join([CaDir, "index.txt"]),<<>>), ok = file:write_file( filename:join([CaDir, "serial.txt"]),<<$0,$1>>), @@ -550,29 +554,49 @@ create_server(CAInfo,Name,Type,State,_Pid)-> ServerCertCsr = filename:join([BaseDir,"server-" ++ binary_to_list(Name) ++ "-cert.csr"]), ServerCertPem = filename:join([BaseDir,"server-" ++ binary_to_list(Name) ++ "-cert.pem"]), Subject = "\"/C=CA/ST=BC/L=Vancouver/O=Arilia Wireless Inc./OU=Servers/CN=" ++ binary_to_list(Name) ++ "\"", - Cmd1 = io_lib:format("openssl req -config ~s -batch -passout pass:~s -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM", - [ binary_to_list(CAInfo#ca_info.config_file_name), - binary_to_list(CAInfo#ca_info.password), - ServerKeyPem, - ServerCertCsr]), + Cmd1 = case CAInfo#ca_info.password == <<>> of + false -> + io_lib:format("openssl req -config ~s -batch -passout pass:~s -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes", + [ binary_to_list(CAInfo#ca_info.config_file_name), + binary_to_list(CAInfo#ca_info.password), + ServerKeyPem, + ServerCertCsr]); + true -> + io_lib:format("openssl req -config ~s -batch -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes", + [ binary_to_list(CAInfo#ca_info.config_file_name), + ServerKeyPem, + ServerCertCsr]) +%% "openssl req -config ~s -batch -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes" + end, _CommandResult1 = os:cmd(Cmd1), - %% io:format("~p> CMD1: ~s, RESULT: ~s~n~n",[Pid,Cmd1,CommandResult1]), - Cmd2 = io_lib:format("openssl ca -batch -passin pass:~s -config ~s -subj ~s -keyfile ~s -cert ~s -extensions server_cert -policy policy_loose -out ~s -infiles ~s", - [ binary_to_list(CAInfo#ca_info.password), - binary_to_list(CAInfo#ca_info.config_file_name), - Subject, - binary_to_list(CAInfo#ca_info.key_file_name), - binary_to_list(CAInfo#ca_info.cert_file_name), - ServerCertPem, - ServerCertCsr]), + %% io:format("> CMD1: ~s, RESULT: ~s~n~n",[Cmd1,CommandResult1]), + Cmd2 = case CAInfo#ca_info.password == <<>> of + false -> io_lib:format("openssl ca -batch -passin pass:~s -config ~s -subj ~s -keyfile ~s -cert ~s -extensions server_cert -policy policy_loose -out ~s -infiles ~s", + [ binary_to_list(CAInfo#ca_info.password), + binary_to_list(CAInfo#ca_info.config_file_name), + Subject, + binary_to_list(CAInfo#ca_info.key_file_name), + binary_to_list(CAInfo#ca_info.cert_file_name), + ServerCertPem, + ServerCertCsr]); + true -> io_lib:format("openssl ca -batch -config ~s -subj ~s -keyfile ~s -cert ~s -extensions server_cert -policy policy_loose -out ~s -infiles ~s", + [ binary_to_list(CAInfo#ca_info.config_file_name), + Subject, + binary_to_list(CAInfo#ca_info.key_file_name), + binary_to_list(CAInfo#ca_info.cert_file_name), + ServerCertPem, + ServerCertCsr]) + end, _CommandResult2 = os:cmd(Cmd2), - %% io:format("~p> CMD2: ~s, RESULT: ~s~n~n",[Pid,Cmd2,CommandResult2]), - Cmd3 = io_lib:format("openssl rsa -passin pass:~s -in ~s -out ~s", - [ binary_to_list(CAInfo#ca_info.password), - ServerKeyPem, - ServerKeyDec]), + %% io:format("> CMD2: ~s, RESULT: ~s~n~n",[Cmd2,CommandResult2]), + Cmd3 = case CAInfo#ca_info.password == <<>> of + false ->io_lib:format("openssl rsa -passin pass:~s -in ~s -out ~s", + [ binary_to_list(CAInfo#ca_info.password), ServerKeyPem, ServerKeyDec]); + true -> io_lib:format("openssl rsa -in ~s -out ~s", + [ ServerKeyPem, ServerKeyDec]) + end, _CommandResult3 = os:cmd(Cmd3), - %% io:format("~p> CMD3: ~s, RESULT: ~s~n~n",[Pid,Cmd3,CommandResult3]), + %% io:format("> CMD3: ~s, RESULT: ~s~n~n",[Cmd3,CommandResult3]), {ok,KeyPemData} = file:read_file(ServerKeyPem), {ok,ServerKeyDecPemData} = file:read_file(ServerKeyDec), @@ -586,7 +610,8 @@ create_server(CAInfo,Name,Type,State,_Pid)-> key = KeyPemData, cert = ServerCertPemData, decrypt = ServerKeyDecPemData, - csr = ServerCertCsrPemData + csr = ServerCertCsrPemData, + cacert = CAInfo#ca_info.cert_data }, _ = add_record(NewServerInfo), @@ -612,23 +637,35 @@ create_client(CAInfo,Attributes,State)-> ClientCertCsr = filename:join([BaseDir,"client-" ++ binary_to_list(Name) ++ "-cert.csr"]), ClientCertPem = filename:join([BaseDir,"client-" ++ binary_to_list(Name) ++ "-cert.pem"]), Subject = "\"/C=CA/ST=BC/L=Vancouver/O=Arilia Wireless Inc./OU=Clients/CN=" ++ binary_to_list(Name) ++ "\"", - Cmd1 = io_lib:format("openssl req -config ~s -batch -passout pass:~s -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes", - [ CAInfo#ca_info.config_file_name, CAInfo#ca_info.password, ClientKeyPem,ClientCertCsr]), + + Cmd1 = case CAInfo#ca_info.password == <<>> of + false -> io_lib:format("openssl req -config ~s -batch -passout pass:~s -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes", + [ binary_to_list(CAInfo#ca_info.config_file_name), binary_to_list(CAInfo#ca_info.password), ClientKeyPem,ClientCertCsr]); + true -> io_lib:format("openssl req -config ~s -batch -newkey rsa:2048 -sha256 -keyout ~s -out ~s -outform PEM -nodes", + [ binary_to_list(CAInfo#ca_info.config_file_name), ClientKeyPem, ClientCertCsr]) + end, _CommandResult1 = os:cmd(Cmd1), %% io:format("CMD1: ~s, RESULT: ~s~n~n",[Cmd1,CommandResult1]), - Cmd2 = io_lib:format("openssl ca -batch -passin pass:~s -config ~s -subj ~s -keyfile ~s -cert ~s -extensions usr_cert -policy policy_loose -out ~s -infiles ~s", - [ binary_to_list(CAInfo#ca_info.password), - binary_to_list(CAInfo#ca_info.config_file_name), - Subject, - binary_to_list(CAInfo#ca_info.key_file_name), - binary_to_list(CAInfo#ca_info.cert_file_name), - ClientCertPem , - ClientCertCsr - ]), + Cmd2 = case CAInfo#ca_info.password == <<>> of + false -> io_lib:format("openssl ca -batch -passin pass:~s -config ~s -subj ~s -keyfile ~s -cert ~s -extensions usr_cert -policy policy_loose -out ~s -infiles ~s", + [ binary_to_list(CAInfo#ca_info.password), + binary_to_list(CAInfo#ca_info.config_file_name), + Subject, + binary_to_list(CAInfo#ca_info.key_file_name), + binary_to_list(CAInfo#ca_info.cert_file_name), + ClientCertPem , + ClientCertCsr + ]); + true -> io_lib:format("openssl ca -batch -config ~s -subj ~s -keyfile ~s -cert ~s -extensions usr_cert -policy policy_loose -out ~s -infiles ~s", + [ binary_to_list(CAInfo#ca_info.config_file_name), Subject, binary_to_list(CAInfo#ca_info.key_file_name), binary_to_list(CAInfo#ca_info.cert_file_name), + ClientCertPem , ClientCertCsr ]) + end, _CommandResult2 = os:cmd(Cmd2), %% io:format("CMD2: ~s, RESULT: ~s~n~n",[Cmd2,CommandResult2]), - Cmd3 = io_lib:format("openssl rsa -passin pass:~s -in ~s -out ~s", - [ binary_to_list(CAInfo#ca_info.password), ClientKeyPem, ClientKeyDec]), + Cmd3 = case CAInfo#ca_info.password == <<>> of + false -> io_lib:format("openssl rsa -passin pass:~s -in ~s -out ~s", [ binary_to_list(CAInfo#ca_info.password), ClientKeyPem, ClientKeyDec]); + true -> io_lib:format("openssl rsa -in ~s -out ~s", [ ClientKeyPem, ClientKeyDec]) + end, _CommandResult3 = os:cmd(Cmd3), %% io:format("CMD3: ~s, RESULT: ~s~n~n",[Cmd3,CommandResult3]), @@ -647,6 +684,7 @@ create_client(CAInfo,Attributes,State)-> key = ClientKeyPemData, cert = ClientCertPemData, decrypt = ClientKeyDecData, + cacert = CAInfo#ca_info.cert_data, csr = ClientCertCsrData }, @@ -704,6 +742,8 @@ valid_ca_name([H|T],Pos) when (((H >= $0) and (H =< $9)) or ((H >= $a) and (H =< valid_ca_name(_,_Pos)-> false. +valid_password("")-> + true; valid_password(Password)-> valid_password(Password,1). valid_password([],Pos) when Pos >= 8 -> diff --git a/src/mqtt_client.erl b/src/mqtt_client.erl index 64f667c..0bd46c8 100644 --- a/src/mqtt_client.erl +++ b/src/mqtt_client.erl @@ -11,19 +11,40 @@ -include("../include/common.hrl"). -include("../include/inventory.hrl"). +-include("../include/mqtt_definitions.hrl"). %% API -export([start/4]). --record(client_state,{manager_pid, topics, compress, configuration, details}). +-record(client_state,{id, manager_pid, topics, compress, configuration, details}). start(CAName,Id,Configuration,ManagerPid)-> #{ <<"broker">> := Broker, <<"compress">> := Compress, <<"port">> := Port, <<"topics">> := Topics } = Configuration, {ok,DeviceConfiguration} = inventory:get_client(CAName,Id), {ok,SSL}=ssl:connect(binary_to_list(Broker),list_to_integer(binary_to_list(Port)), - [ {cert,DeviceConfiguration#client_info.cert}, {key,DeviceConfiguration#client_info.key}]), - CS = #client_state{ manager_pid = ManagerPid, topics = Topics, compress = Compress , configuration = Configuration, details = DeviceConfiguration }, + [ + {session_tickets,auto}, + {versions, ['tlsv1.2','tlsv1.3']}, + {cert,DeviceConfiguration#client_info.cert}, + {key,DeviceConfiguration#client_info.key}, + {active,false }, + binary]), + CS = #client_state{ id=Id, manager_pid = ManagerPid, topics = Topics, compress = Compress , configuration = Configuration, details = DeviceConfiguration }, run_client(SSL,CS). run_client(SSL,CS)-> + C = #mqtt_connect_variable_header{ + protocol_version = ?MQTT_PROTOCOL_VERSION_3_11, + username_flag = 0, + password_flag = 0, + will_retain_flag = 0, + will_qos_flag = 0, + will_flag = 0 , + clean_start_flag = 1, + client_identifier = CS#client_state.id, + keep_alive = 60 }, + M = #mqtt_msg{ variable_header = C}, + ConnectMessage = mqtt_message:encode(M), + + _ = ssl:send(SSL,ConnectMessage), ok. diff --git a/src/mqtt_message.erl b/src/mqtt_message.erl index 9f2d803..263552b 100644 --- a/src/mqtt_message.erl +++ b/src/mqtt_message.erl @@ -324,12 +324,26 @@ inner_encode( #mqtt_connect_variable_header{} = Header )-> true -> mqttlib:enc_binary(Header#mqtt_connect_variable_header.password); false -> <<>> end, - Payload = << (mqttlib:enc_string(Header#mqtt_connect_variable_header.client_identifier))/binary, - (set_properties_section((Header#mqtt_connect_variable_header.will_properties)))/binary, - (mqttlib:enc_string(Header#mqtt_connect_variable_header.will_topic))/binary, - (mqttlib:enc_binary(Header#mqtt_connect_variable_header.will_payload))/binary, - UserNamePayload/binary, - UserPasswordPayload/binary>>, + WillTopic = case Header#mqtt_connect_variable_header.will_flag == 1 of + true -> <<(mqttlib:enc_string(Header#mqtt_connect_variable_header.will_topic))/binary, + (mqttlib:enc_binary(Header#mqtt_connect_variable_header.will_message))/binary>>; + false -> <<>> + end, + Payload = case Header#mqtt_connect_variable_header.protocol_version of + ?MQTT_PROTOCOL_VERSION_5 -> + << (mqttlib:enc_string(Header#mqtt_connect_variable_header.client_identifier))/binary, + (set_properties_section((Header#mqtt_connect_variable_header.will_properties)))/binary, + WillTopic/binary, + (mqttlib:enc_binary(Header#mqtt_connect_variable_header.will_payload))/binary, + UserNamePayload/binary, + UserPasswordPayload/binary>>; + ?MQTT_PROTOCOL_VERSION_3_11 -> + << (mqttlib:enc_string(Header#mqtt_connect_variable_header.client_identifier))/binary, + WillTopic/binary, + (mqttlib:enc_binary(Header#mqtt_connect_variable_header.will_payload))/binary, + UserNamePayload/binary, + UserPasswordPayload/binary>> + end, Blob = <<0:8,4:8,$M,$Q,$T,$T,(Header#mqtt_connect_variable_header.protocol_version):8,Flags/binary,(Header#mqtt_connect_variable_header.keep_alive):16,Payload/binary>>, {?MQTT_CONNECT, 0, Blob}; diff --git a/src/mqtt_tests.erl b/src/mqtt_tests.erl index cb3dc0e..f6e41cc 100644 --- a/src/mqtt_tests.erl +++ b/src/mqtt_tests.erl @@ -19,6 +19,7 @@ simple_test() -> connection_packet_encoding_decoding_test() -> ?DBGTRC("Starting"), PacketVariableHeader = #mqtt_connect_variable_header{ + protocol_version = ?MQTT_PROTOCOL_VERSION_5, username_flag = 1 , password_flag = 1, username = <<"Stephb">>, @@ -28,6 +29,7 @@ connection_packet_encoding_decoding_test() -> keep_alive = 60, client_identifier = <<"test_device_1">>, will_topic = <<"topics/a">>, + will_message = <<>>, clean_start_flag = 1, will_properties = [{will_delay_interval,1000}, {payload_format_indicator,1}, diff --git a/src/simengine.erl b/src/simengine.erl index 6b1ec2f..d27ee10 100644 --- a/src/simengine.erl +++ b/src/simengine.erl @@ -214,7 +214,7 @@ progress(BatchNumber,Id,SimName)-> io:format("~nSimulation ~s preparation progress: ID=~s Batch=~p~n",[binary_to_list(SimName),binary_to_list(Id),BatchNumber]). %% Create the servers - only if they are pon automatic mode -split_build_servers(SimInfo,Notification)-> +split_build_servers(SimInfo,_Notification)-> _ = generate_server(SimInfo,mqtt_server,SimInfo#simulation.mqtt_servers), _ = generate_server(SimInfo,ovsdb_server,SimInfo#simulation.ovsdb_servers), ok. diff --git a/src/ssl_test.erl b/src/ssl_test.erl new file mode 100644 index 0000000..9cb159f --- /dev/null +++ b/src/ssl_test.erl @@ -0,0 +1,100 @@ +%%%------------------------------------------------------------------- +%%% @author stephb +%%% @copyright (C) 2020, Arilia Wireless Inc. +%%% @doc +%%% +%%% @end +%%% Created : 03. Dec 2020 11:50 a.m. +%%%------------------------------------------------------------------- +-module(ssl_test). +-author("stephb"). + +%% API +-export([start/0,server/0,client/0]). + +-define(S_CERT,"certs/test_certs/server-mqtt-1--cert.pem"). +-define(S_KEY ,"certs/test_certs/server-mqtt-1--key.pem"). +-define(S_DKEY,"certs/test_certs/server-mqtt-1--key_dec.pem"). + +-define(C_CERT,"certs/test_certs/client-sim1-1-000032-cert.pem"). +-define(C_KEY ,"certs/test_certs/client-sim1-1-000032-key.pem"). +-define(C_DKEY,"certs/test_certs/client-sim1-1-000032-key_dec.pem"). +-define(C_CA ,"certs/test_certs/sim1_cert.pem"). + +-define(PORT,11000). + +start()-> + _=ssl:start(), + spawn(?MODULE,server,[]), + timer:sleep(2000), + spawn(?MODULE,client,[]). + +server()-> + { ok , ListenSocket } = ssl:listen(?PORT,[ +%% {log_level,debug}, + {session_tickets,stateless}, + {mode,binary}, + {versions,['tlsv1.2','tlsv1.3']}, + {active,false}, + {reuseaddr,true}, + {certfile,?S_CERT}, + {keyfile,?S_KEY} +]), + accept(ListenSocket). + +accept(ListenSocket) -> + _=case ssl:transport_accept(ListenSocket) of + {ok,Socket} -> + case ssl:handshake(Socket) of + { ok, SslSocket } -> + _=ssl:setopts(SslSocket,[{active,true}]), + server_processor_active(SslSocket), + io:format("Client is done...~n"); + Error -> + io:format("Client error is done: ~p...~n",[Error]), + ssl:close(Socket) + end; + Error -> + io:format("Client error done: ~p...~n",[Error]) + end, + accept(ListenSocket). + +%% server_processor_passive(Socket)-> +%% case ssl:recv(Socket,10) of +%% {ok,Data} -> +%% io:format("Received ~p bytes: ~p.~n",[size(Data),Data]); +%% Error -> +%% io:format("Error ~p.~n",[Error]) +%% end, +%% server_processor_passive(Socket). + +server_processor_active(Socket)-> + receive + {ssl,Socket,Data} -> + io:format("Received ~p bytes: ~p.~n",[size(Data),Data]), + server_processor_active(Socket); + {ssl_closed,Socket} -> + io:format("Closing socket.~n"); + Anything -> + io:format("Unknown message: ~p.~n",[Anything]), + server_processor_active(Socket) + end. + +client()-> +%% {ok,Cacerts}=file:read_file(?C_CA), + {ok,SSL}=ssl:connect("renegademac.arilia.com",?PORT, + [ + %% {log_level,debug}, + {session_tickets,auto}, + {versions, ['tlsv1.2','tlsv1.3']}, + {certfile,?C_CERT}, + {keyfile,?C_KEY}, + {active,false }, + binary]), + send_data(SSL). + +send_data(SSL)-> + _ = ssl:send(SSL,<<"foo">>), + %% io:format("Sending: ~p~n",[E]), + timer:sleep(1000), + send_data(SSL). \ No newline at end of file diff --git a/src/user_default.erl b/src/user_default.erl index ee45549..430e674 100644 --- a/src/user_default.erl +++ b/src/user_default.erl @@ -137,7 +137,7 @@ show_plan(_SimName)-> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -spec create_ca(CAName::string())-> generic_result(). create_ca(CAName) when is_list(CAName)-> - create_ca(CAName,"password"). + create_ca(CAName,""). -spec create_ca(CAName::string(),Password::string())-> generic_result(). create_ca(CAName,Password) when is_list(CAName),is_list(Password)->