aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/test')
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl176
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl14
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl9
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl3
-rw-r--r--lib/ssh/test/ssh_test_lib.erl497
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl108
6 files changed, 629 insertions, 178 deletions
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 2c0fd882a0..e801664ff2 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -76,7 +76,7 @@ end_per_suite(Config) ->
%% Description: Initialization before each test case
%%--------------------------------------------------------------------
init_per_testcase(_TestCase, Config) ->
- rename_known_hosts(backup),
+ ssh_test_lib:known_hosts(backup),
ssh:start(),
Config.
@@ -90,7 +90,7 @@ init_per_testcase(_TestCase, Config) ->
%%--------------------------------------------------------------------
end_per_testcase(_TestCase, _Config) ->
ssh:stop(),
- rename_known_hosts(restore),
+ ssh_test_lib:known_hosts(restore),
ok.
%%--------------------------------------------------------------------
@@ -117,6 +117,16 @@ end_per_group(_GroupName, Config) ->
%% Test cases starts here.
%%--------------------------------------------------------------------
+sign_and_verify_rsa(doc) ->
+ ["Test api function ssh:sign_data and ssh:verify_data"];
+
+sign_and_verify_rsa(suite) ->
+ [];
+sign_and_verify_rsa(Config) when is_list(Config) ->
+ Data = ssh:sign_data(<<"correct data">>, "ssh-rsa"),
+ ok = ssh:verify_data(<<"correct data">>, Data, "ssh-rsa"),
+ {error,invalid_signature} = ssh:verify_data(<<"incorrect data">>, Data,"ssh-rsa").
+
exec(doc) ->
["Test api function ssh_connection:exec"];
@@ -127,13 +137,11 @@ exec(suite) ->
exec(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SystemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
- {ok, ConnectionRef} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user_interaction, false}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"1+1.", infinity),
@@ -171,15 +179,13 @@ exec_compressed(suite) ->
exec_compressed(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SystemDir},
- {compression, zlib},
- {failfun, fun ssh_test_lib:failfun/2}]),
-
- {ok, ConnectionRef} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user_interaction, false}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {compression, zlib},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId,
"1+1.", infinity),
@@ -204,9 +210,8 @@ shell(suite) ->
shell(Config) when is_list(Config) ->
process_flag(trap_exit, true),
SystemDir = ?config(data_dir, Config),
- Port = ssh_test_lib:inet_port(),
- {ok, _Pid} = ssh:daemon(Port, [{system_dir, SystemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
+ {_Pid, _Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
test_server:sleep(500),
IO = ssh_test_lib:start_io_server(),
@@ -265,21 +270,20 @@ shell(Config) when is_list(Config) ->
end.
%%--------------------------------------------------------------------
-daemon_allready_started(doc) ->
+daemon_already_started(doc) ->
["Test that get correct error message if you try to start a daemon",
- "on an adress that allready runs a daemon see also seq10667" ];
+ "on an adress that already runs a daemon see also seq10667" ];
-daemon_allready_started(suite) ->
+daemon_already_started(suite) ->
[];
-daemon_allready_started(Config) when is_list(Config) ->
+daemon_already_started(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SystemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
- {error, eaddrinuse} = ssh:daemon(Port, [{system_dir, SystemDir},
- {failfun,
- fun ssh_test_lib:failfun/2}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ {error, eaddrinuse} = ssh_test_lib:daemon(Host, Port, [{system_dir, SystemDir},
+ {failfun,
+ fun ssh_test_lib:failfun/2}]),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -290,26 +294,23 @@ server_password_option(suite) ->
server_password_option(Config) when is_list(Config) ->
UserDir = ?config(data_dir, Config), % to make sure we don't use
SysDir = ?config(data_dir, Config), % public-key-auth
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} =
- ssh:daemon(Port, [{system_dir, SysDir},
- {password, "morot"}]),
- Host = ssh_test_lib:hostname(),
-
- {ok, ConnectionRef} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {password, "morot"}]),
+
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
{error, Reason} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "vego"},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- test_server:format("Test of wrong pasword: Error msg: ~p ~n", [Reason]),
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
+ test_server:format("Test of wrong password: Error msg: ~p ~n", [Reason]),
ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
@@ -323,39 +324,36 @@ server_userpassword_option(suite) ->
server_userpassword_option(Config) when is_list(Config) ->
UserDir = ?config(data_dir, Config), % to make sure we don't use
SysDir = ?config(data_dir, Config), % public-key-auth
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} =
- ssh:daemon(Port, [{system_dir, SysDir},
- {user_passwords, [{"vego", "morot"}]}]),
- Host = ssh_test_lib:hostname(),
-
- {ok, ConnectionRef} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "vego"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir},
+ {user_passwords, [{"vego", "morot"}]}]),
+
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
ssh:close(ConnectionRef),
{error, Reason0} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
test_server:format("Test of user foo that does not exist. "
"Error msg: ~p ~n", [Reason0]),
{error, Reason1} =
- ssh:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "vego"},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- test_server:format("Test of wrong Pasword. "
+ ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ test_server:format("Test of wrong Password. "
"Error msg: ~p ~n", [Reason1]),
-
+
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -366,41 +364,27 @@ known_hosts(suite) ->
known_hosts(Config) when is_list(Config) ->
SystemDir = ?config(data_dir, Config),
UserDir = ?config(priv_dir, Config),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SystemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
KnownHosts = filename:join(UserDir, "known_hosts"),
file:delete(KnownHosts),
{error, enoent} = file:read_file(KnownHosts),
- Host = ssh_test_lib:hostname(),
- {ok, ConnectionRef} =
- ssh:connect(Host, Port, [{user_dir, UserDir},
- {user_interaction, false},
- silently_accept_hosts]),
+ ConnectionRef =
+ ssh_test_lib:connect(Host, Port, [{user_dir, UserDir},
+ {user_interaction, false},
+ silently_accept_hosts]),
{ok, _Channel} = ssh_connection:session_channel(ConnectionRef, infinity),
ok = ssh:close(ConnectionRef),
{ok, Binary} = file:read_file(KnownHosts),
Lines = string:tokens(binary_to_list(Binary), "\n"),
[Line] = Lines,
- {ok, Hostname} = inet:gethostname(),
[HostAndIp, Alg, _KeyData] = string:tokens(Line, " "),
- [Hostname, _Ip] = string:tokens(HostAndIp, ","),
+ [Host, _Ip] = string:tokens(HostAndIp, ","),
"ssh-" ++ _ = Alg,
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
-
-rename_known_hosts(BR) ->
- KnownHosts = ssh_file:file_name(user, "known_hosts", []),
- B = KnownHosts ++ "xxx",
- case BR of
- backup ->
- file:rename(KnownHosts, B);
- restore ->
- file:delete(KnownHosts),
- file:rename(B, KnownHosts)
- end.
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 0c4a7f3b3f..123a12773b 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -103,13 +103,13 @@ init_per_testcase(_Case, Config) ->
{ok, ChannelPid, Connection} ->
{ChannelPid, Connection};
_Error ->
- {ok, _Sftpd} =
- ssh:daemon(?SFPD_PORT,
- [{system_dir, SysDir},
- {user_passwords,
- [{?USER, ?PASSWD}]},
- {failfun,
- fun ssh_test_lib:failfun/2}]),
+ {_Sftpd, _Host, _Port} =
+ ssh_test_lib:daemon(Host, ?SFPD_PORT,
+ [{system_dir, SysDir},
+ {user_passwords,
+ [{?USER, ?PASSWD}]},
+ {failfun,
+ fun ssh_test_lib:failfun/2}]),
Result = (catch ssh_sftp:start_channel(Host, ?SFPD_PORT,
[{user, ?USER},
{password, ?PASSWD},
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 1afc206148..f5ed668fa6 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -93,16 +93,15 @@ init_per_testcase(TestCase, Config) ->
SysDir = ?config(data_dir, Config),
{ok, Sftpd} =
ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SysDir},
- {user_passwords,[{?USER, ?PASSWD}]}]),
-
- Host = ssh_test_lib:hostname(),
- {ok, Cm} = ssh:connect(Host, ?SFPD_PORT,
+ {user_passwords,[{?USER, ?PASSWD}]}]),
+
+ Cm = ssh_test_lib:connect(?SFPD_PORT,
[{silently_accept_hosts, true},
{user, ?USER}, {password, ?PASSWD}]),
{ok, Channel} =
ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE,
?XFER_PACKET_SIZE, ?TIMEOUT),
-
+
success = ssh_connection:subsystem(Cm, Channel, "sftp", ?TIMEOUT),
ProtocolVer = case atom_to_list(TestCase) of
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index c7107635c1..db23a98225 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -115,9 +115,8 @@ init_per_testcase(TestCase, Config) ->
{system_dir, DataDir}]
end,
- {ok, Sftpd} = ssh:daemon(any, ?SSHD_PORT, Options),
+ {Sftpd, Host, _Port} = ssh_test_lib:daemon(any, ?SSHD_PORT, Options),
- Host = ssh_test_lib:hostname(),
{ok, ChannelPid, Connection} =
ssh_sftp:start_channel(Host, ?SSHD_PORT,
[{silently_accept_hosts, true},
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 2eb19cec22..c237e1ba5d 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2004-2011. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -24,12 +24,54 @@
%% Note: This directive should only be used in test suites.
-compile(export_all).
+-include_lib("public_key/include/public_key.hrl").
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+
+-define(TIMEOUT, 50000).
+-define(SSH_DEFAULT_PORT, 22).
+
+connect(Options) ->
+ connect(hostname(), inet_port(), Options).
+
+connect(Port, Options) when is_integer(Port) ->
+ connect(hostname(), Port, Options);
+connect(Host, Options) ->
+ connect(Host, inet_port(), Options).
+
+connect(Host, Port, Options) ->
+ case ssh:connect(Host, Port, Options) of
+ {ok, ConnectionRef} ->
+ ConnectionRef;
+ Error ->
+ Error
+ end.
+
+daemon(Options) ->
+ daemon(any, inet_port(), Options).
+
+daemon(Port, Options) when is_integer(Port) ->
+ daemon(any, Port, Options);
+daemon(Host, Options) ->
+ daemon(Host, inet_port(), Options).
+
+daemon(Host, Port, Options) ->
+ case ssh:daemon(Host, Port, Options) of
+ {ok, Pid} ->
+ {Pid, Host, Port};
+ Error ->
+ Error
+ end.
+
+
+
+
start_shell(Port, IOServer) ->
spawn_link(?MODULE, init_shell, [Port, IOServer]).
init_shell(Port, IOServer) ->
- Host = ssh_test_lib:hostname(),
- UserDir = ssh_test_lib:get_user_dir(),
+ Host = hostname(),
+ UserDir = get_user_dir(),
Options = [{user_interaction, false}, {silently_accept_hosts,
true}] ++ UserDir,
group_leader(IOServer, self()),
@@ -123,9 +165,9 @@ receive_exec_end(ConnectionRef, ChannelId) ->
receive_exec_result(Data, ConnectionRef, ChannelId) ->
Eof = {ssh_cm, ConnectionRef, {eof, ChannelId}},
Closed = {ssh_cm, ConnectionRef,{closed, ChannelId}},
- expected = ssh_test_lib:receive_exec_result(Data),
- expected = ssh_test_lib:receive_exec_result(Eof),
- expected = ssh_test_lib:receive_exec_result(Closed).
+ expected = receive_exec_result(Data),
+ expected = receive_exec_result(Eof),
+ expected = receive_exec_result(Closed).
inet_port()->
@@ -164,6 +206,18 @@ hostname() ->
{ok,Host} = inet:gethostname(),
Host.
+known_hosts(BR) ->
+ KnownHosts = ssh_file:file_name(user, "known_hosts", []),
+ B = KnownHosts ++ "xxx",
+ case BR of
+ backup ->
+ file:rename(KnownHosts, B);
+ restore ->
+ file:delete(KnownHosts),
+ file:rename(B, KnownHosts)
+ end.
+
+
save_known_hosts(PrivDir) ->
Src = ssh_file:file_name(user, "known_hosts", []),
Dst = filename:join(PrivDir, "kh_save"),
@@ -187,3 +241,434 @@ get_user_dir() ->
_ ->
[]
end.
+
+
+%% Create certificates.
+make_dsa_cert(Config) ->
+
+ {ServerCaCertFile, ServerCertFile, ServerKeyFile} = make_dsa_cert_files("server", Config),
+ {ClientCaCertFile, ClientCertFile, ClientKeyFile} = make_dsa_cert_files("client", Config),
+ [{server_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {server_dsa_verify_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile},
+ {verify, verify_peer}]},
+ {client_dsa_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}
+ | Config].
+
+
+
+make_dsa_cert_files(RoleStr, Config) ->
+ CaInfo = {CaCert, _} = make_cert([{key, dsa}]),
+ {Cert, CertKey} = make_cert([{key, dsa}, {issuer, CaInfo}]),
+ CaCertFile = filename:join(["/home/nick/trash/ssh/",
+ RoleStr, "dsa_cacerts.pem"]),
+ CertFile = filename:join(["/home/nick/trash/ssh/",
+ RoleStr, "dsa_cert.pem"]),
+ KeyFile = filename:join(["/home/nick/trash/ssh/",
+ RoleStr, "dsa_key.pem"]),
+%% CaCertFile = filename:join([?config(priv_dir, Config),
+%% RoleStr, "dsa_cacerts.pem"]),
+%% CertFile = filename:join([?config(priv_dir, Config),
+%% RoleStr, "dsa_cert.pem"]),
+%% KeyFile = filename:join([?config(priv_dir, Config),
+%% RoleStr, "dsa_key.pem"]),
+
+ der_to_pem(CaCertFile, [{'Certificate', CaCert, not_encrypted}]),
+ der_to_pem(CertFile, [{'Certificate', Cert, not_encrypted}]),
+ der_to_pem(KeyFile, [CertKey]),
+ {CaCertFile, CertFile, KeyFile}.
+
+
+%%--------------------------------------------------------------------
+%% Create and return a der encoded certificate
+%% Option Default
+%% -------------------------------------------------------
+%% digest sha1
+%% validity {date(), date() + week()}
+%% version 3
+%% subject [] list of the following content
+%% {name, Name}
+%% {email, Email}
+%% {city, City}
+%% {state, State}
+%% {org, Org}
+%% {org_unit, OrgUnit}
+%% {country, Country}
+%% {serial, Serial}
+%% {title, Title}
+%% {dnQualifer, DnQ}
+%% issuer = {Issuer, IssuerKey} true (i.e. a ca cert is created)
+%% (obs IssuerKey migth be {Key, Password}
+%% key = KeyFile|KeyBin|rsa|dsa Subject PublicKey rsa or dsa generates key
+%%
+%%
+%% (OBS: The generated keys are for testing only)
+%% make_cert([{::atom(), ::term()}]) -> {Cert::binary(), Key::binary()}
+%%--------------------------------------------------------------------
+make_cert(Opts) ->
+ SubjectPrivateKey = get_key(Opts),
+ {TBSCert, IssuerKey} = make_tbs(SubjectPrivateKey, Opts),
+ Cert = public_key:pkix_sign(TBSCert, IssuerKey),
+ true = verify_signature(Cert, IssuerKey, undef), %% verify that the keys where ok
+ {Cert, encode_key(SubjectPrivateKey)}.
+
+%%--------------------------------------------------------------------
+%% Writes pem files in Dir with FileName ++ ".pem" and FileName ++ "_key.pem"
+%% write_pem(::string(), ::string(), {Cert,Key}) -> ok
+%%--------------------------------------------------------------------
+write_pem(Dir, FileName, {Cert, Key = {_,_,not_encrypted}}) when is_binary(Cert) ->
+ ok = der_to_pem(filename:join(Dir, FileName ++ ".pem"),
+ [{'Certificate', Cert, not_encrypted}]),
+ ok = der_to_pem(filename:join(Dir, FileName ++ "_key.pem"), [Key]).
+
+%%--------------------------------------------------------------------
+%% Creates a rsa key (OBS: for testing only)
+%% the size are in bytes
+%% gen_rsa(::integer()) -> {::atom(), ::binary(), ::opaque()}
+%%--------------------------------------------------------------------
+gen_rsa(Size) when is_integer(Size) ->
+ Key = gen_rsa2(Size),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% Creates a dsa key (OBS: for testing only)
+%% the sizes are in bytes
+%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()}
+%%--------------------------------------------------------------------
+gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) ->
+ Key = gen_dsa2(LSize, NSize),
+ {Key, encode_key(Key)}.
+
+%%--------------------------------------------------------------------
+%% Verifies cert signatures
+%% verify_signature(::binary(), ::tuple()) -> ::boolean()
+%%--------------------------------------------------------------------
+verify_signature(DerEncodedCert, DerKey, _KeyParams) ->
+ Key = decode_key(DerKey),
+ case Key of
+ #'RSAPrivateKey'{modulus=Mod, publicExponent=Exp} ->
+ public_key:pkix_verify(DerEncodedCert,
+ #'RSAPublicKey'{modulus=Mod, publicExponent=Exp});
+ #'DSAPrivateKey'{p=P, q=Q, g=G, y=Y} ->
+ public_key:pkix_verify(DerEncodedCert, {Y, #'Dss-Parms'{p=P, q=Q, g=G}})
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%% Implementation %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+get_key(Opts) ->
+ case proplists:get_value(key, Opts) of
+ undefined -> make_key(rsa, Opts);
+ rsa -> make_key(rsa, Opts);
+ dsa -> make_key(dsa, Opts);
+ Key ->
+ Password = proplists:get_value(password, Opts, no_passwd),
+ decode_key(Key, Password)
+ end.
+
+decode_key({Key, Pw}) ->
+ decode_key(Key, Pw);
+decode_key(Key) ->
+ decode_key(Key, no_passwd).
+
+
+decode_key(#'RSAPublicKey'{} = Key,_) ->
+ Key;
+decode_key(#'RSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(#'DSAPrivateKey'{} = Key,_) ->
+ Key;
+decode_key(PemEntry = {_,_,_}, Pw) ->
+ public_key:pem_entry_decode(PemEntry, Pw);
+decode_key(PemBin, Pw) ->
+ [KeyInfo] = public_key:pem_decode(PemBin),
+ decode_key(KeyInfo, Pw).
+
+encode_key(Key = #'RSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key),
+ {'RSAPrivateKey', list_to_binary(Der), not_encrypted};
+encode_key(Key = #'DSAPrivateKey'{}) ->
+ {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key),
+ {'DSAPrivateKey', list_to_binary(Der), not_encrypted}.
+
+make_tbs(SubjectKey, Opts) ->
+ Version = list_to_atom("v"++integer_to_list(proplists:get_value(version, Opts, 3))),
+
+ IssuerProp = proplists:get_value(issuer, Opts, true),
+ {Issuer, IssuerKey} = issuer(IssuerProp, Opts, SubjectKey),
+
+ {Algo, Parameters} = sign_algorithm(IssuerKey, Opts),
+
+ SignAlgo = #'SignatureAlgorithm'{algorithm = Algo,
+ parameters = Parameters},
+ Subject = case IssuerProp of
+ true -> %% Is a Root Ca
+ Issuer;
+ _ ->
+ subject(proplists:get_value(subject, Opts),false)
+ end,
+
+ {#'OTPTBSCertificate'{serialNumber = trunc(random:uniform()*100000000)*10000 + 1,
+ signature = SignAlgo,
+ issuer = Issuer,
+ validity = validity(Opts),
+ subject = Subject,
+ subjectPublicKeyInfo = publickey(SubjectKey),
+ version = Version,
+ extensions = extensions(Opts)
+ }, IssuerKey}.
+
+issuer(true, Opts, SubjectKey) ->
+ %% Self signed
+ {subject(proplists:get_value(subject, Opts), true), SubjectKey};
+issuer({Issuer, IssuerKey}, _Opts, _SubjectKey) when is_binary(Issuer) ->
+ {issuer_der(Issuer), decode_key(IssuerKey)};
+issuer({File, IssuerKey}, _Opts, _SubjectKey) when is_list(File) ->
+ {ok, [{cert, Cert, _}|_]} = pem_to_der(File),
+ {issuer_der(Cert), decode_key(IssuerKey)}.
+
+issuer_der(Issuer) ->
+ Decoded = public_key:pkix_decode_cert(Issuer, otp),
+ #'OTPCertificate'{tbsCertificate=Tbs} = Decoded,
+ #'OTPTBSCertificate'{subject=Subject} = Tbs,
+ Subject.
+
+subject(undefined, IsRootCA) ->
+ User = if IsRootCA -> "RootCA"; true -> os:getenv("USER") end,
+ Opts = [{email, User ++ "@erlang.org"},
+ {name, User},
+ {city, "Stockholm"},
+ {country, "SE"},
+ {org, "erlang"},
+ {org_unit, "testing dep"}],
+ subject(Opts);
+subject(Opts, _) ->
+ subject(Opts).
+
+subject(SubjectOpts) when is_list(SubjectOpts) ->
+ Encode = fun(Opt) ->
+ {Type,Value} = subject_enc(Opt),
+ [#'AttributeTypeAndValue'{type=Type, value=Value}]
+ end,
+ {rdnSequence, [Encode(Opt) || Opt <- SubjectOpts]}.
+
+%% Fill in the blanks
+subject_enc({name, Name}) -> {?'id-at-commonName', {printableString, Name}};
+subject_enc({email, Email}) -> {?'id-emailAddress', Email};
+subject_enc({city, City}) -> {?'id-at-localityName', {printableString, City}};
+subject_enc({state, State}) -> {?'id-at-stateOrProvinceName', {printableString, State}};
+subject_enc({org, Org}) -> {?'id-at-organizationName', {printableString, Org}};
+subject_enc({org_unit, OrgUnit}) -> {?'id-at-organizationalUnitName', {printableString, OrgUnit}};
+subject_enc({country, Country}) -> {?'id-at-countryName', Country};
+subject_enc({serial, Serial}) -> {?'id-at-serialNumber', Serial};
+subject_enc({title, Title}) -> {?'id-at-title', {printableString, Title}};
+subject_enc({dnQualifer, DnQ}) -> {?'id-at-dnQualifier', DnQ};
+subject_enc(Other) -> Other.
+
+
+extensions(Opts) ->
+ case proplists:get_value(extensions, Opts, []) of
+ false ->
+ asn1_NOVALUE;
+ Exts ->
+ lists:flatten([extension(Ext) || Ext <- default_extensions(Exts)])
+ end.
+
+default_extensions(Exts) ->
+ Def = [{key_usage,undefined},
+ {subject_altname, undefined},
+ {issuer_altname, undefined},
+ {basic_constraints, default},
+ {name_constraints, undefined},
+ {policy_constraints, undefined},
+ {ext_key_usage, undefined},
+ {inhibit_any, undefined},
+ {auth_key_id, undefined},
+ {subject_key_id, undefined},
+ {policy_mapping, undefined}],
+ Filter = fun({Key, _}, D) -> lists:keydelete(Key, 1, D) end,
+ Exts ++ lists:foldl(Filter, Def, Exts).
+
+extension({_, undefined}) -> [];
+extension({basic_constraints, Data}) ->
+ case Data of
+ default ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true},
+ critical=true};
+ false ->
+ [];
+ Len when is_integer(Len) ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = #'BasicConstraints'{cA=true, pathLenConstraint=Len},
+ critical=true};
+ _ ->
+ #'Extension'{extnID = ?'id-ce-basicConstraints',
+ extnValue = Data}
+ end;
+extension({Id, Data, Critical}) ->
+ #'Extension'{extnID = Id, extnValue = Data, critical = Critical}.
+
+
+publickey(#'RSAPrivateKey'{modulus=N, publicExponent=E}) ->
+ Public = #'RSAPublicKey'{modulus=N, publicExponent=E},
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?rsaEncryption, parameters='NULL'},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo,
+ subjectPublicKey = Public};
+publickey(#'DSAPrivateKey'{p=P, q=Q, g=G, y=Y}) ->
+ Algo = #'PublicKeyAlgorithm'{algorithm= ?'id-dsa',
+ parameters={params, #'Dss-Parms'{p=P, q=Q, g=G}}},
+ #'OTPSubjectPublicKeyInfo'{algorithm = Algo, subjectPublicKey = Y}.
+
+validity(Opts) ->
+ DefFrom0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())-1),
+ DefTo0 = calendar:gregorian_days_to_date(calendar:date_to_gregorian_days(date())+7),
+ {DefFrom, DefTo} = proplists:get_value(validity, Opts, {DefFrom0, DefTo0}),
+ Format = fun({Y,M,D}) -> lists:flatten(io_lib:format("~w~2..0w~2..0w000000Z",[Y,M,D])) end,
+ #'Validity'{notBefore={generalTime, Format(DefFrom)},
+ notAfter ={generalTime, Format(DefTo)}}.
+
+sign_algorithm(#'RSAPrivateKey'{}, Opts) ->
+ Type = case proplists:get_value(digest, Opts, sha1) of
+ sha1 -> ?'sha1WithRSAEncryption';
+ sha512 -> ?'sha512WithRSAEncryption';
+ sha384 -> ?'sha384WithRSAEncryption';
+ sha256 -> ?'sha256WithRSAEncryption';
+ md5 -> ?'md5WithRSAEncryption';
+ md2 -> ?'md2WithRSAEncryption'
+ end,
+ {Type, 'NULL'};
+sign_algorithm(#'DSAPrivateKey'{p=P, q=Q, g=G}, _Opts) ->
+ {?'id-dsa-with-sha1', {params,#'Dss-Parms'{p=P, q=Q, g=G}}}.
+
+make_key(rsa, _Opts) ->
+ %% (OBS: for testing only)
+ gen_rsa2(64);
+make_key(dsa, _Opts) ->
+ gen_dsa2(128, 20). %% Bytes i.e. {1024, 160}
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% RSA key generation (OBS: for testing only)
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-define(SMALL_PRIMES, [65537,97,89,83,79,73,71,67,61,59,53,
+ 47,43,41,37,31,29,23,19,17,13,11,7,5,3]).
+
+gen_rsa2(Size) ->
+ P = prime(Size),
+ Q = prime(Size),
+ N = P*Q,
+ Tot = (P - 1) * (Q - 1),
+ [E|_] = lists:dropwhile(fun(Candidate) -> (Tot rem Candidate) == 0 end, ?SMALL_PRIMES),
+ {D1,D2} = extended_gcd(E, Tot),
+ D = erlang:max(D1,D2),
+ case D < E of
+ true ->
+ gen_rsa2(Size);
+ false ->
+ {Co1,Co2} = extended_gcd(Q, P),
+ Co = erlang:max(Co1,Co2),
+ #'RSAPrivateKey'{version = 'two-prime',
+ modulus = N,
+ publicExponent = E,
+ privateExponent = D,
+ prime1 = P,
+ prime2 = Q,
+ exponent1 = D rem (P-1),
+ exponent2 = D rem (Q-1),
+ coefficient = Co
+ }
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% DSA key generation (OBS: for testing only)
+%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm
+%% and the fips_186-3.pdf
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+gen_dsa2(LSize, NSize) ->
+ Q = prime(NSize), %% Choose N-bit prime Q
+ X0 = prime(LSize),
+ P0 = prime((LSize div 2) +1),
+
+ %% Choose L-bit prime modulus P such that p-1 is a multiple of q.
+ case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of
+ error ->
+ gen_dsa2(LSize, NSize);
+ P ->
+ G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q.
+ %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used.
+
+ X = prime(20), %% Choose x by some random method, where 0 < x < q.
+ Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p.
+
+ #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X}
+ end.
+
+%% See fips_186-3.pdf
+dsa_search(T, P0, Q, Iter) when Iter > 0 ->
+ P = 2*T*Q*P0 + 1,
+ case is_prime(crypto:mpint(P), 50) of
+ true -> P;
+ false -> dsa_search(T+1, P0, Q, Iter-1)
+ end;
+dsa_search(_,_,_,_) ->
+ error.
+
+
+%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+prime(ByteSize) ->
+ Rand = odd_rand(ByteSize),
+ crypto:erlint(prime_odd(Rand, 0)).
+
+prime_odd(Rand, N) ->
+ case is_prime(Rand, 50) of
+ true ->
+ Rand;
+ false ->
+ NotPrime = crypto:erlint(Rand),
+ prime_odd(crypto:mpint(NotPrime+2), N+1)
+ end.
+
+%% see http://en.wikipedia.org/wiki/Fermat_primality_test
+is_prime(_, 0) -> true;
+is_prime(Candidate, Test) ->
+ CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate),
+ case crypto:mod_exp(CoPrime, Candidate, Candidate) of
+ CoPrime -> is_prime(Candidate, Test-1);
+ _ -> false
+ end.
+
+odd_rand(Size) ->
+ Min = 1 bsl (Size*8-1),
+ Max = (1 bsl (Size*8))-1,
+ odd_rand(crypto:mpint(Min), crypto:mpint(Max)).
+
+odd_rand(Min,Max) ->
+ Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max),
+ BitSkip = (Sz+4)*8-1,
+ case Rand of
+ Odd = <<_:BitSkip, 1:1>> -> Odd;
+ Even = <<_:BitSkip, 0:1>> ->
+ crypto:mpint(crypto:erlint(Even)+1)
+ end.
+
+extended_gcd(A, B) ->
+ case A rem B of
+ 0 ->
+ {0, 1};
+ N ->
+ {X, Y} = extended_gcd(B, N),
+ {Y, X-Y*(A div B)}
+ end.
+
+pem_to_der(File) ->
+ {ok, PemBin} = file:read_file(File),
+ public_key:pem_decode(PemBin).
+
+der_to_pem(File, Entries) ->
+ PemBin = public_key:pem_encode(Entries),
+ file:write_file(File, PemBin).
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 0c15c067a8..cf3279df75 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -159,10 +159,8 @@ erlang_client_openssh_server_exec(suite) ->
[];
erlang_client_openssh_server_exec(Config) when is_list(Config) ->
- Host = ssh_test_lib:hostname(),
- {ok, ConnectionRef} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
{ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId0,
"echo testing", infinity),
@@ -203,11 +201,9 @@ erlang_client_openssh_server_exec_compressed(suite) ->
[];
erlang_client_openssh_server_exec_compressed(Config) when is_list(Config) ->
- Host = ssh_test_lib:hostname(),
- {ok, ConnectionRef} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false},
- {compression, zlib}]),
+ ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false},
+ {compression, zlib}]),
{ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity),
success = ssh_connection:exec(ConnectionRef, ChannelId,
"echo testing", infinity),
@@ -231,13 +227,11 @@ erlang_server_openssh_client_exec(suite) ->
[];
erlang_server_openssh_client_exec(Config) when is_list(Config) ->
- SytemDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
- Port = ssh_test_lib:inet_port(),
-
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SytemDir},
- {failfun, fun ssh_test_lib:failfun/2}]),
-
+ SystemDir = ?config(data_dir, Config),
+
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
test_server:sleep(500),
@@ -262,12 +256,10 @@ erlang_server_openssh_client_exec_compressed(suite) ->
[];
erlang_server_openssh_client_exec_compressed(Config) when is_list(Config) ->
- SytemDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SytemDir},
- {compression, zlib},
- {failfun, fun ssh_test_lib:failfun/2}]),
+ SystemDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {compression, zlib},
+ {failfun, fun ssh_test_lib:failfun/2}]),
test_server:sleep(500),
@@ -292,10 +284,9 @@ erlang_client_openssh_server_setenv(suite) ->
[];
erlang_client_openssh_server_setenv(Config) when is_list(Config) ->
- Host = ssh_test_lib:hostname(),
- {ok, ConnectionRef} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user_interaction, false}]),
+ ConnectionRef =
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user_interaction, false}]),
{ok, ChannelId} =
ssh_connection:session_channel(ConnectionRef, infinity),
Env = case ssh_connection:setenv(ConnectionRef, ChannelId,
@@ -340,16 +331,14 @@ erlang_client_openssh_server_publickey_rsa(Config) when is_list(Config) ->
{ok,[[Home]]} = init:get_argument(home),
SrcDir = filename:join(Home, ".ssh"),
UserDir = ?config(priv_dir, Config),
- Host = ssh_test_lib:hostname(),
-
case ssh_test_lib:copyfile(SrcDir, UserDir, "id_rsa") of
{ok, _} ->
- {ok, ConnectionRef} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT,
- [{user_dir, UserDir},
- {public_key_alg, ssh_rsa},
- {user_interaction, false},
- silently_accept_hosts]),
+ ConnectionRef =
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{user_dir, UserDir},
+ {public_key_alg, ssh_rsa},
+ {user_interaction, false},
+ silently_accept_hosts]),
{ok, Channel} =
ssh_connection:session_channel(ConnectionRef, infinity),
ok = ssh_connection:close(ConnectionRef, Channel),
@@ -368,15 +357,14 @@ erlang_client_openssh_server_publickey_dsa(Config) when is_list(Config) ->
{ok,[[Home]]} = init:get_argument(home),
SrcDir = filename:join(Home, ".ssh"),
UserDir = ?config(priv_dir, Config),
- Host = ssh_test_lib:hostname(),
case ssh_test_lib:copyfile(SrcDir, UserDir, "id_dsa") of
{ok, _} ->
- {ok, ConnectionRef} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT,
- [{user_dir, UserDir},
- {public_key_alg, ssh_dsa},
- {user_interaction, false},
- silently_accept_hosts]),
+ ConnectionRef =
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{user_dir, UserDir},
+ {public_key_alg, ssh_dsa},
+ {user_interaction, false},
+ silently_accept_hosts]),
{ok, Channel} =
ssh_connection:session_channel(ConnectionRef, infinity),
ok = ssh_connection:close(ConnectionRef, Channel),
@@ -394,13 +382,11 @@ erlang_server_openssh_client_pulic_key_dsa(suite) ->
[];
erlang_server_openssh_client_pulic_key_dsa(Config) when is_list(Config) ->
- SytemDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
- Port = ssh_test_lib:inet_port(),
- {ok, Pid} = ssh:daemon(Port, [{system_dir, SytemDir},
- {public_key_alg, ssh_dsa},
- {failfun, fun ssh_test_lib:failfun/2}]),
-
+ SystemDir = ?config(data_dir, Config),
+ {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {public_key_alg, ssh_dsa},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+
test_server:sleep(500),
Cmd = "ssh -p " ++ integer_to_list(Port) ++
@@ -426,15 +412,13 @@ erlang_client_openssh_server_password(suite) ->
erlang_client_openssh_server_password(Config) when is_list(Config) ->
%% to make sure we don't public-key-auth
UserDir = ?config(data_dir, Config),
- Host = ssh_test_lib:hostname(),
-
{error, Reason0} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+
test_server:format("Test of user foo that does not exist. "
"Error msg: ~p~n", [Reason0]),
@@ -443,12 +427,12 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
case length(string:tokens(User, " ")) of
1 ->
{error, Reason1} =
- ssh:connect(Host, ?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ ssh_test_lib:connect(?SSH_DEFAULT_PORT,
+ [{silently_accept_hosts, true},
+ {user, User},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
test_server:format("Test of wrong Pasword. "
"Error msg: ~p~n", [Reason1]);
_ ->