aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2015-10-08 16:03:47 +0200
committerHans Nilsson <[email protected]>2015-10-08 16:03:47 +0200
commit0bc373ca42a1e880a97e34f664ab968144c2a4f4 (patch)
treeecfc42f4247520e27524a22dc72a9da9b5d9d7fd /lib/ssh
parent2c7e387961251af59f14bca39cbf8fbbe880383e (diff)
parent4a356cf070e197cec375b562891af8fe9634c306 (diff)
downloadotp-0bc373ca42a1e880a97e34f664ab968144c2a4f4.tar.gz
otp-0bc373ca42a1e880a97e34f664ab968144c2a4f4.tar.bz2
otp-0bc373ca42a1e880a97e34f664ab968144c2a4f4.zip
Merge branch 'hans/ssh/ecdsa/OTP-12936' into maint
Diffstat (limited to 'lib/ssh')
-rw-r--r--lib/ssh/doc/src/ssh.xml2
-rw-r--r--lib/ssh/src/ssh_auth.erl84
-rw-r--r--lib/ssh/src/ssh_auth.hrl2
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl12
-rw-r--r--lib/ssh/src/ssh_file.erl63
-rw-r--r--lib/ssh/src/ssh_message.erl32
-rw-r--r--lib/ssh/src/ssh_transport.erl64
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl39
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa2565
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa3846
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa5217
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key2565
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key3846
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub1
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key5217
-rw-r--r--lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub1
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl2
-rw-r--r--lib/ssh/test/ssh_test_lib.erl32
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl2
23 files changed, 289 insertions, 87 deletions
diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml
index 93bea09e4e..c7a09d65a8 100644
--- a/lib/ssh/doc/src/ssh.xml
+++ b/lib/ssh/doc/src/ssh.xml
@@ -40,7 +40,7 @@
<list type="bulleted">
<item>For application dependencies see <seealso marker="SSH_app"> ssh(6)</seealso> </item>
<item>Supported SSH version is 2.0.</item>
- <item>Supported public key algorithms: ssh-rsa and ssh-dss.</item>
+ <item>Supported public key algorithms:ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa and ssh-dss.</item>
<item>Supported MAC algorithms: hmac-sha2-512, hmac-sha2-256 and hmac-sha1.</item>
<item>Supported MAC algorithms: hmac-sha2-256 and hmac-sha1.</item>
<item>Supported encryption algorithms: aes256-ctr, aes192-ctr, aes128-ctr, aes128-cb and 3des-cbc.</item>
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 726f52132f..0c16e19701 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -31,8 +31,7 @@
-export([publickey_msg/1, password_msg/1, keyboard_interactive_msg/1,
service_request_msg/1, init_userauth_request_msg/1,
userauth_request_msg/1, handle_userauth_request/3,
- handle_userauth_info_request/3, handle_userauth_info_response/2,
- default_public_key_algorithms/0
+ handle_userauth_info_request/3, handle_userauth_info_response/2
]).
%%--------------------------------------------------------------------
@@ -42,27 +41,29 @@ publickey_msg([Alg, #ssh{user = User,
session_id = SessionId,
service = Service,
opts = Opts} = Ssh]) ->
-
Hash = sha, %% Maybe option?!
KeyCb = proplists:get_value(key_cb, Opts, ssh_file),
-
case KeyCb:user_key(Alg, Opts) of
{ok, Key} ->
StrAlgo = algorithm_string(Alg),
- PubKeyBlob = encode_public_key(Key),
- SigData = build_sig_data(SessionId,
- User, Service, PubKeyBlob, StrAlgo),
- Sig = ssh_transport:sign(SigData, Hash, Key),
- SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]),
- ssh_transport:ssh_packet(
- #ssh_msg_userauth_request{user = User,
- service = Service,
- method = "publickey",
- data = [?TRUE,
- ?string(StrAlgo),
- ?binary(PubKeyBlob),
- ?binary(SigBlob)]},
- Ssh);
+ case encode_public_key(StrAlgo, Key) of
+ not_ok ->
+ not_ok;
+ PubKeyBlob ->
+ SigData = build_sig_data(SessionId,
+ User, Service, PubKeyBlob, StrAlgo),
+ Sig = ssh_transport:sign(SigData, Hash, Key),
+ SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]),
+ ssh_transport:ssh_packet(
+ #ssh_msg_userauth_request{user = User,
+ service = Service,
+ method = "publickey",
+ data = [?TRUE,
+ ?string(StrAlgo),
+ ?binary(PubKeyBlob),
+ ?binary(SigBlob)]},
+ Ssh)
+ end;
_Error ->
not_ok
end.
@@ -121,7 +122,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) ->
Algs = proplists:get_value(public_key,
proplists:get_value(preferred_algorithms, Opts, []),
- default_public_key_algorithms()),
+ ssh_transport:default_algorithms(public_key)),
Prefs = method_preference(Algs),
ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User,
userauth_preference = Prefs,
@@ -355,8 +356,6 @@ handle_userauth_info_response(#ssh_msg_userauth_info_response{},
language = "en"}).
-default_public_key_algorithms() -> ?PREFERRED_PK_ALGS.
-
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -431,10 +430,13 @@ build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
?binary(KeyBlob)],
list_to_binary(Sig).
-algorithm_string('ssh-rsa') ->
- "ssh-rsa";
-algorithm_string('ssh-dss') ->
- "ssh-dss".
+algorithm_string('ssh-rsa') -> "ssh-rsa";
+algorithm_string('ssh-dss') -> "ssh-dss";
+algorithm_string('ecdsa-sha2-nistp256') -> "ecdsa-sha2-nistp256";
+algorithm_string('ecdsa-sha2-nistp384') -> "ecdsa-sha2-nistp384";
+algorithm_string('ecdsa-sha2-nistp521') -> "ecdsa-sha2-nistp521".
+
+
decode_keyboard_interactive_prompts(_NumPrompts, Data) ->
ssh_message:decode_keyboard_interactive_prompts(Data, []).
@@ -497,11 +499,35 @@ decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
?UINT32(Len4), Y:Len4/big-signed-integer-unit:8>>
, "ssh-dss") ->
{ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}};
-
+decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
+ ?UINT32(Len1), Id:Len1/binary, %% Id = <<"nistp256">> for example
+ ?UINT32(Len2), Blob:Len2/binary>>,
+ Curve) ->
+ Id =
+ case Curve of
+ "ecdsa-sha2-nistp256" -> <<"nistp256">>;
+ "ecdsa-sha2-nistp384" -> <<"nistp384">>;
+ "ecdsa-sha2-nistp521" -> <<"nistp521">>
+ end,
+ {ok, {#'ECPoint'{point=Blob}, Id}};
decode_public_key_v2(_, _) ->
{error, bad_format}.
-encode_public_key(#'RSAPrivateKey'{publicExponent = E, modulus = N}) ->
+encode_public_key("ssh-rsa", #'RSAPrivateKey'{publicExponent = E, modulus = N}) ->
ssh_bits:encode(["ssh-rsa",E,N], [string,mpint,mpint]);
-encode_public_key(#'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) ->
- ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]).
+encode_public_key("ssh-dss", #'DSAPrivateKey'{p = P, q = Q, g = G, y = Y}) ->
+ ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint]);
+encode_public_key("ecdsa-sha2-"++Curve, #'ECPrivateKey'{parameters = Params,
+ publicKey = Pub}) ->
+ Id = ecdsa_id(Params),
+ if
+ Id =/= Curve ->
+ not_ok;
+ true ->
+ ssh_bits:encode(["ecdsa-sha2-"++Id, Id, Pub],
+ [string, string, binary])
+ end.
+
+ecdsa_id({namedCurve,?'secp256r1'}) -> "nistp256";
+ecdsa_id({namedCurve,?'secp384r1'}) -> "nistp384";
+ecdsa_id({namedCurve,?'secp521r1'}) -> "nistp521".
diff --git a/lib/ssh/src/ssh_auth.hrl b/lib/ssh/src/ssh_auth.hrl
index 71f222f6d7..5197a42fa4 100644
--- a/lib/ssh/src/ssh_auth.hrl
+++ b/lib/ssh/src/ssh_auth.hrl
@@ -24,8 +24,6 @@
-define(SUPPORTED_AUTH_METHODS, "publickey,keyboard-interactive,password").
--define(PREFERRED_PK_ALGS, ['ssh-rsa','ssh-dss']).
-
-define(SSH_MSG_USERAUTH_REQUEST, 50).
-define(SSH_MSG_USERAUTH_FAILURE, 51).
-define(SSH_MSG_USERAUTH_SUCCESS, 52).
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index ee1dd5c9ce..7fb86c1108 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -1266,9 +1266,9 @@ supported_host_keys(client, _, Options) ->
proplists:get_value(preferred_algorithms,Options,[])
) of
undefined ->
- ssh_auth:default_public_key_algorithms();
+ ssh_transport:default_algorithms(public_key);
L ->
- L -- (L--ssh_auth:default_public_key_algorithms())
+ L -- (L--ssh_transport:default_algorithms(public_key))
end
of
[] ->
@@ -1280,21 +1280,17 @@ supported_host_keys(client, _, Options) ->
{stop, {shutdown, Reason}}
end;
supported_host_keys(server, KeyCb, Options) ->
- Algs=
[atom_to_list(A) || A <- proplists:get_value(public_key,
proplists:get_value(preferred_algorithms,Options,[]),
- ssh_auth:default_public_key_algorithms()
+ ssh_transport:default_algorithms(public_key)
),
available_host_key(KeyCb, A, Options)
- ],
- Algs.
-
+ ].
%% Alg :: atom()
available_host_key(KeyCb, Alg, Opts) ->
element(1, catch KeyCb:host_key(Alg, Opts)) == ok.
-
send_msg(Msg, #state{socket = Socket, transport_cb = Transport}) ->
Transport:send(Socket, Msg).
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index b98a8a8410..4e6d58cbff 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -52,8 +52,20 @@ host_key(Algorithm, Opts) ->
%% so probably we could hardcod Password = ignore, but
%% we keep it as an undocumented option for now.
Password = proplists:get_value(identity_pass_phrase(Algorithm), Opts, ignore),
- decode(File, Password).
-
+ case decode(File, Password) of
+ {ok,Key} ->
+ case {Key,Algorithm} of
+ {#'RSAPrivateKey'{}, 'ssh-rsa'} -> {ok,Key};
+ {#'DSAPrivateKey'{}, 'ssh-dss'} -> {ok,Key};
+ {#'ECPrivateKey'{parameters = {namedCurve, ?'secp256r1'}}, 'ecdsa-sha2-nistp256'} -> {ok,Key};
+ {#'ECPrivateKey'{parameters = {namedCurve, ?'secp384r1'}}, 'ecdsa-sha2-nistp384'} -> {ok,Key};
+ {#'ECPrivateKey'{parameters = {namedCurve, ?'secp521r1'}}, 'ecdsa-sha2-nistp521'} -> {ok,Key};
+ _ ->
+ {error,bad_keytype_in_file}
+ end;
+ Other ->
+ Other
+ end.
is_auth_key(Key, User,Opts) ->
case lookup_user_key(Key, User, Opts) of
@@ -81,16 +93,15 @@ user_key(Algorithm, Opts) ->
%% Internal functions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-file_base_name('ssh-rsa') ->
- "ssh_host_rsa_key";
-file_base_name('ssh-dss') ->
- "ssh_host_dsa_key";
-file_base_name(_) ->
- "ssh_host_key".
+file_base_name('ssh-rsa' ) -> "ssh_host_rsa_key";
+file_base_name('ssh-dss' ) -> "ssh_host_dsa_key";
+file_base_name('ecdsa-sha2-nistp256') -> "ssh_host_ecdsa_key";
+file_base_name('ecdsa-sha2-nistp384') -> "ssh_host_ecdsa_key";
+file_base_name('ecdsa-sha2-nistp521') -> "ssh_host_ecdsa_key";
+file_base_name(_ ) -> "ssh_host_key".
decode(File, Password) ->
- try
- {ok, decode_ssh_file(read_ssh_file(File), Password)}
+ try {ok, decode_ssh_file(read_ssh_file(File), Password)}
catch
throw:Reason ->
{error, Reason};
@@ -215,20 +226,18 @@ do_lookup_host_key(KeyToMatch, Host, Alg, Opts) ->
Error -> Error
end.
-identity_key_filename('ssh-dss') ->
- "id_dsa";
-identity_key_filename('ssh-rsa') ->
- "id_rsa".
-
-identity_pass_phrase("ssh-dss") ->
- dsa_pass_phrase;
-identity_pass_phrase('ssh-dss') ->
- dsa_pass_phrase;
-identity_pass_phrase('ssh-rsa') ->
- rsa_pass_phrase;
-identity_pass_phrase("ssh-rsa") ->
- rsa_pass_phrase.
-
+identity_key_filename('ssh-dss' ) -> "id_dsa";
+identity_key_filename('ssh-rsa' ) -> "id_rsa";
+identity_key_filename('ecdsa-sha2-nistp256') -> "id_ecdsa";
+identity_key_filename('ecdsa-sha2-nistp384') -> "id_ecdsa";
+identity_key_filename('ecdsa-sha2-nistp521') -> "id_ecdsa".
+
+identity_pass_phrase("ssh-dss" ) -> dsa_pass_phrase;
+identity_pass_phrase("ssh-rsa" ) -> rsa_pass_phrase;
+identity_pass_phrase("ecdsa-sha2-"++_) -> ecdsa_pass_phrase;
+identity_pass_phrase(P) when is_atom(P) ->
+ identity_pass_phrase(atom_to_list(P)).
+
lookup_host_key_fd(Fd, KeyToMatch, Host, KeyType) ->
case io:get_line(Fd, '') of
eof ->
@@ -267,6 +276,12 @@ key_match(#'RSAPublicKey'{}, 'ssh-rsa') ->
true;
key_match({_, #'Dss-Parms'{}}, 'ssh-dss') ->
true;
+key_match({#'ECPoint'{},<<"nistp256">>}, 'ecdsa-sha2-nistp256') ->
+ true;
+key_match({#'ECPoint'{},<<"nistp384">>}, 'ecdsa-sha2-nistp384') ->
+ true;
+key_match({#'ECPoint'{},<<"nistp521">>}, 'ecdsa-sha2-nistp521') ->
+ true;
key_match(_, _) ->
false.
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index cb1dcb67c5..cfa11903fb 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -564,9 +564,11 @@ decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) ->
decode_sign(<<?UINT32(Len), _Alg:Len/binary, ?UINT32(_), Signature/binary>>) ->
Signature.
+
decode_host_key(<<?UINT32(Len), Alg:Len/binary, Rest/binary>>) ->
decode_host_key(Alg, Rest).
+
decode_host_key(<<"ssh-rsa">>, <<?UINT32(Len0), E:Len0/big-signed-integer-unit:8,
?UINT32(Len1), N:Len1/big-signed-integer-unit:8>>) ->
#'RSAPublicKey'{publicExponent = E,
@@ -579,19 +581,43 @@ decode_host_key(<<"ssh-dss">>,
?UINT32(Len3), Y:Len3/big-signed-integer-unit:8>>) ->
{Y, #'Dss-Parms'{p = P,
q = Q,
- g = G}}.
+ g = G}};
+
+decode_host_key(<<"ecdsa-sha2-",Id/binary>>,
+ <<?UINT32(Len0), Id:Len0/binary, %% Id = <<"nistp256">> for example
+ ?UINT32(Len1), Blob:Len1/binary>>) ->
+ {#'ECPoint'{point=Blob}, Id}.
+
encode_host_key(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
ssh_bits:encode(["ssh-rsa", E, N], [string, mpint, mpint]);
encode_host_key({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
ssh_bits:encode(["ssh-dss", P, Q, G, Y],
[string, mpint, mpint, mpint, mpint]);
+encode_host_key({#'ECPoint'{point = Q}, Id}) ->
+ ssh_bits:encode([<<"ecdsa-sha2-",Id/binary>>,Id,Q], [binary,binary,binary]);
+
encode_host_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
ssh_bits:encode(["ssh-rsa", E, N], [string, mpint, mpint]);
encode_host_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
ssh_bits:encode(["ssh-dss", P, Q, G, Y],
- [string, mpint, mpint, mpint, mpint]).
+ [string, mpint, mpint, mpint, mpint]);
+encode_host_key(#'ECPrivateKey'{parameters = Params, %{namedCurve,{1,2,840,10045,3,1,7}},
+ publicKey = Pub}) ->
+ Id = ecdsa_id(Params),
+ ssh_bits:encode(["ecdsa-sha2-"++Id, Id, Pub],
+ [string, string, binary]).
+
+
encode_sign(#'RSAPrivateKey'{}, Signature) ->
ssh_bits:encode(["ssh-rsa", Signature],[string, binary]);
encode_sign(#'DSAPrivateKey'{}, Signature) ->
- ssh_bits:encode(["ssh-dss", Signature],[string, binary]).
+ ssh_bits:encode(["ssh-dss", Signature],[string, binary]);
+encode_sign(#'ECPrivateKey'{parameters = Params}, Signature) ->
+ Id = "ecdsa-sha2-" ++ ecdsa_id(Params),
+ ssh_bits:encode([Id, Signature],[string, binary]).
+
+
+ecdsa_id({namedCurve,?'secp256r1'}) -> "nistp256";
+ecdsa_id({namedCurve,?'secp384r1'}) -> "nistp384";
+ecdsa_id({namedCurve,?'secp521r1'}) -> "nistp521".
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 840564e246..8030c1dc25 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -65,7 +65,8 @@ default_algorithms() -> [{K,default_algorithms(K)} || K <- algo_classes()].
algo_classes() -> [kex, public_key, cipher, mac, compression].
-default_algorithms(kex) -> supported_algorithms(kex, []); %% Just to have a call...
+default_algorithms(kex) ->
+ supported_algorithms(kex, []); %% Just to have a call to supported_algorithms/2
default_algorithms(Alg) ->
supported_algorithms(Alg).
@@ -84,7 +85,14 @@ supported_algorithms(kex) ->
{'diffie-hellman-group1-sha1', [{public_keys,dh}, {hashs,sha}]}
]);
supported_algorithms(public_key) ->
- ssh_auth:default_public_key_algorithms();
+ select_crypto_supported(
+ [{'ecdsa-sha2-nistp256', [{public_keys,ecdsa}, {hashs,sha256}, {ec_curve,secp256r1}]},
+ {'ecdsa-sha2-nistp384', [{public_keys,ecdsa}, {hashs,sha384}, {ec_curve,secp384r1}]},
+ {'ecdsa-sha2-nistp521', [{public_keys,ecdsa}, {hashs,sha512}, {ec_curve,secp521r1}]},
+ {'ssh-rsa', [{public_keys,rsa}, {hashs,sha} ]},
+ {'ssh-dss', [{public_keys,dss}, {hashs,sha} ]}
+ ]);
+
supported_algorithms(cipher) ->
same(
select_crypto_supported(
@@ -640,33 +648,40 @@ get_host_key(SSH) ->
#ssh{key_cb = Mod, opts = Opts, algorithms = ALG} = SSH,
case Mod:host_key(ALG#alg.hkey, Opts) of
- {ok, #'RSAPrivateKey'{} = Key} ->
- Key;
- {ok, #'DSAPrivateKey'{} = Key} ->
- Key;
+ {ok, #'RSAPrivateKey'{} = Key} -> Key;
+ {ok, #'DSAPrivateKey'{} = Key} -> Key;
+ {ok, #'ECPrivateKey'{} = Key} -> Key;
Result ->
exit({error, {Result, unsupported_key_type}})
end.
-sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) ->
- Hash = sha,
- _Signature = sign(H, Hash, Private);
-sign_host_key(_Ssh, #'DSAPrivateKey'{} = Private, H) ->
- Hash = sha,
- _RawSignature = sign(H, Hash, Private).
+sign_host_key(_Ssh, PrivateKey, H) ->
+ sign(H, sign_host_key_sha(PrivateKey), PrivateKey).
+
+sign_host_key_sha(#'ECPrivateKey'{parameters = {namedCurve, ?'secp256r1'}}) -> sha256;
+sign_host_key_sha(#'ECPrivateKey'{parameters = {namedCurve, ?'secp384r1'}}) -> sha384;
+sign_host_key_sha(#'ECPrivateKey'{parameters = {namedCurve, ?'secp521r1'}}) -> sha512;
+sign_host_key_sha(#'RSAPrivateKey'{}) -> sha;
+sign_host_key_sha(#'DSAPrivateKey'{}) -> sha.
+
verify_host_key(SSH, PublicKey, Digest, Signature) ->
- case verify(Digest, sha, Signature, PublicKey) of
+ case verify(Digest, host_key_sha(PublicKey), Signature, PublicKey) of
false ->
{error, bad_signature};
true ->
known_host_key(SSH, PublicKey, public_algo(PublicKey))
end.
-public_algo(#'RSAPublicKey'{}) ->
- 'ssh-rsa';
-public_algo({_, #'Dss-Parms'{}}) ->
- 'ssh-dss'.
+host_key_sha(#'RSAPublicKey'{}) -> sha;
+host_key_sha({_, #'Dss-Parms'{}}) -> sha;
+host_key_sha({#'ECPoint'{},Id}) -> sha(list_to_atom(binary_to_list(Id))).
+
+
+public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa';
+public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
+public_algo({#'ECPoint'{},Id}) -> list_to_atom("ecdsa-sha2-" ++ binary_to_list(Id)).
+
accepted_host(Ssh, PeerName, Opts) ->
case proplists:get_value(silently_accept_hosts, Opts, false) of
@@ -906,6 +921,10 @@ sign(SigData, Hash, #'DSAPrivateKey'{} = Key) ->
DerSignature = public_key:sign(SigData, Hash, Key),
#'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature),
<<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>;
+sign(SigData, Hash, Key = #'ECPrivateKey'{}) ->
+ DerEncodedSign = public_key:sign(SigData, Hash, Key),
+ #'ECDSA-Sig-Value'{r=R, s=S} = public_key:der_decode('ECDSA-Sig-Value', DerEncodedSign),
+ ssh_bits:encode([R,S], [mpint,mpint]);
sign(SigData, Hash, Key) ->
public_key:sign(SigData, Hash, Key).
@@ -913,6 +932,17 @@ verify(PlainText, Hash, Sig, {_, #'Dss-Parms'{}} = Key) ->
<<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> = Sig,
Signature = public_key:der_encode('Dss-Sig-Value', #'Dss-Sig-Value'{r = R, s = S}),
public_key:verify(PlainText, Hash, Signature, Key);
+verify(PlainText, Hash, Sig, {ECPoint=#'ECPoint'{}, Param}) ->
+ C = case Param of
+ <<"nistp256">> -> {namedCurve, ?'secp256r1'};
+ <<"nistp384">> -> {namedCurve, ?'secp384r1'};
+ <<"nistp521">> -> {namedCurve, ?'secp521r1'}
+ end,
+ <<?UINT32(Rlen),R:Rlen/big-signed-integer-unit:8,
+ ?UINT32(Slen),S:Slen/big-signed-integer-unit:8>> = Sig,
+ Sval = #'ECDSA-Sig-Value'{r=R, s=S},
+ DerEncodedSig = public_key:der_encode('ECDSA-Sig-Value',Sval),
+ public_key:verify(PlainText, Hash, DerEncodedSig, {ECPoint,C});
verify(PlainText, Hash, Sig, Key) ->
public_key:verify(PlainText, Hash, Sig, Key).
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 2ff7198bf8..7f1a64f094 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -77,6 +77,9 @@ all() ->
appup_test,
{group, dsa_key},
{group, rsa_key},
+ {group, ecdsa_sha2_nistp256_key},
+ {group, ecdsa_sha2_nistp384_key},
+ {group, ecdsa_sha2_nistp521_key},
{group, dsa_pass_key},
{group, rsa_pass_key},
{group, internal_error},
@@ -89,6 +92,9 @@ all() ->
groups() ->
[{dsa_key, [], basic_tests()},
{rsa_key, [], basic_tests()},
+ {ecdsa_sha2_nistp256_key, [], basic_tests()},
+ {ecdsa_sha2_nistp384_key, [], basic_tests()},
+ {ecdsa_sha2_nistp521_key, [], basic_tests()},
{dsa_pass_key, [], [pass_phrase]},
{rsa_pass_key, [], [pass_phrase]},
{internal_error, [], [internal_error]}
@@ -129,6 +135,39 @@ init_per_group(rsa_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:setup_rsa(DataDir, PrivDir),
Config;
+init_per_group(ecdsa_sha2_nistp256_key, Config) ->
+ case lists:member('ecdsa-sha2-nistp256',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_ecdsa("256", DataDir, PrivDir),
+ Config;
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(ecdsa_sha2_nistp384_key, Config) ->
+ case lists:member('ecdsa-sha2-nistp384',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_ecdsa("384", DataDir, PrivDir),
+ Config;
+ false ->
+ {skip, unsupported_pub_key}
+ end;
+init_per_group(ecdsa_sha2_nistp521_key, Config) ->
+ case lists:member('ecdsa-sha2-nistp521',
+ ssh_transport:default_algorithms(public_key)) of
+ true ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_ecdsa("521", DataDir, PrivDir),
+ Config;
+ false ->
+ {skip, unsupported_pub_key}
+ end;
init_per_group(rsa_pass_key, Config) ->
DataDir = ?config(data_dir, Config),
PrivDir = ?config(priv_dir, Config),
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256
new file mode 100644
index 0000000000..4b1eb12eaa
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIJfCaBKIIKhjbJl5F8BedqlXOQYDX5ba9Skypllmx/w+oAoGCCqGSM49
+AwEHoUQDQgAE49RbK2xQ/19ji3uDPM7uT4692LbwWF1TiaA9vUuebMGazoW/98br
+N9xZu0L1AWwtEjs3kmJDTB7eJEGXnjUAcQ==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub
new file mode 100644
index 0000000000..a0147e60fa
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBOPUWytsUP9fY4t7gzzO7k+Ovdi28FhdU4mgPb1LnmzBms6Fv/fG6zfcWbtC9QFsLRI7N5JiQ0we3iRBl541AHE= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384
new file mode 100644
index 0000000000..4e8aa40959
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCYXb6OSAZyXRfLXOtMo43za197Hdc/T0YKjgQQjwDt6rlRwqTh7v7S
+PV2kXwNGdWigBwYFK4EEACKhZANiAARN2khlJUOOIiwsWHEALwDieeZR96qL4pUd
+ci7aeGaczdUK5jOA9D9zmBZtSYTfO8Cr7ekVghDlcWAIJ/BXcswgQwSEQ6wyfaTF
+8FYfyr4l3u9IirsnyaFzeIgeoNis8Gw=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub
new file mode 100644
index 0000000000..41e722e545
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBE3aSGUlQ44iLCxYcQAvAOJ55lH3qovilR1yLtp4ZpzN1QrmM4D0P3OYFm1JhN87wKvt6RWCEOVxYAgn8FdyzCBDBIRDrDJ9pMXwVh/KviXe70iKuyfJoXN4iB6g2KzwbA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521 b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521
new file mode 100644
index 0000000000..7196f46e97
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHbAgEBBEFMadoz4ckEcClfqXa2tiUuYkJdDfwq+/iFQcpt8ESuEd26IY/vm47Q
+9UzbPkO4ou8xkNsQ3WvCRQBBWtn5O2kUU6AHBgUrgQQAI6GBiQOBhgAEAde5BRu5
+01/jS0jRk212xsb2DxPrxNpgp6IMCV8TA4Eps+8bSqHB091nLiBcP422HXYfuCd7
+XDjSs8ihcmhp0hCRASLqZR9EzW9W/SOt876May1Huj5X+WSO6RLe7vPn9vmf7kHf
+pip6m7M7qp2qGgQ3q2vRwS2K/O6156ohiOlmuuFs
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub
new file mode 100644
index 0000000000..8f059120bc
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/id_ecdsa521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHXuQUbudNf40tI0ZNtdsbG9g8T68TaYKeiDAlfEwOBKbPvG0qhwdPdZy4gXD+Nth12H7gne1w40rPIoXJoadIQkQEi6mUfRM1vVv0jrfO+jGstR7o+V/lkjukS3u7z5/b5n+5B36YqepuzO6qdqhoEN6tr0cEtivzuteeqIYjpZrrhbA== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256
new file mode 100644
index 0000000000..2979ea88ed
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256
@@ -0,0 +1,5 @@
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIMe4MDoit0t8RzSVPwkCBemQ9fhXL+xnTSAWISw8HNCioAoGCCqGSM49
+AwEHoUQDQgAEo2q7U3P6r0W5WGOLtM78UQtofM9UalEhiZeDdiyylsR/RR17Op0s
+VPGSADLmzzgcucLEKy17j2S+oz42VUJy5A==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub
new file mode 100644
index 0000000000..85dc419345
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key256.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBKNqu1Nz+q9FuVhji7TO/FELaHzPVGpRIYmXg3YsspbEf0UdezqdLFTxkgAy5s84HLnCxCste49kvqM+NlVCcuQ= uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384
new file mode 100644
index 0000000000..fb1a862ded
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384
@@ -0,0 +1,6 @@
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDArxbDfh3p1okrD9wQw6jJ4d4DdlBPD5GqXE8bIeRJiK41Sh40LgvPw
+mkqEDSXK++CgBwYFK4EEACKhZANiAAScl43Ih2lWTDKrSox5ve5uiTXil4smsup3
+CfS1XPjKxgBAmlfBim8izbdrT0BFdQzz2joduNMtpt61wO4rGs6jm0UP7Kim9PC7
+Hneb/99fIYopdMH5NMnk60zGO1uZ2vc=
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub
new file mode 100644
index 0000000000..428d5fb7d7
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key384.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBJyXjciHaVZMMqtKjHm97m6JNeKXiyay6ncJ9LVc+MrGAECaV8GKbyLNt2tPQEV1DPPaOh240y2m3rXA7isazqObRQ/sqKb08Lsed5v/318hiil0wfk0yeTrTMY7W5na9w== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521 b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521
new file mode 100644
index 0000000000..3e51ec2ecd
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521
@@ -0,0 +1,7 @@
+-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIB8O1BFkl2HQjQLRLonEZ97da/h39DMa9/0/hvPZWAI8gUPEQcHxRx
+U7b09p3Zh+EBbMFq8+1ae9ds+ZTxE4WFSvKgBwYFK4EEACOhgYkDgYYABAAlWVjq
+Bzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/
+vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5
+ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg==
+-----END EC PRIVATE KEY-----
diff --git a/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub
new file mode 100644
index 0000000000..017a29f4da
--- /dev/null
+++ b/lib/ssh/test/ssh_basic_SUITE_data/ssh_host_ecdsa_key521.pub
@@ -0,0 +1 @@
+ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAAlWVjqBzg7Wt4gE6UNb1lRE2cnlmH2L/A5uo6qZRx5lPnSKOxEhxSb/Oay1+9d6KRdrh6/vlhd9SHDBhLcAPDvWgBnJIEj92Q3pXX4JtoitL0yl+SvvU+vUh966mzHShHzj8p5ccOgPkPNoA70yrpGzkIhPezpZOQdCaOXj/jFqNCTDg== uabhnil@elxadlj3q32
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index d8e99799e2..406f8e5960 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -91,7 +91,7 @@ init_per_testcase(TC, Config) when TC == gex_client_init_default_noexact ;
[]
end,
start_std_daemon(Config,
- [{preferred_algorithms, ssh_transport:supported_algorithms()}
+ [{preferred_algorithms, ssh:default_algorithms()}
| Opts]);
init_per_testcase(_TestCase, Config) ->
check_std_daemon_works(Config, ?LINE).
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index e16df6f959..6fa579d78d 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -286,6 +286,7 @@ setup_dsa(DataDir, UserDir) ->
file:make_dir(System),
file:copy(filename:join(DataDir, "ssh_host_dsa_key"), filename:join(System, "ssh_host_dsa_key")),
file:copy(filename:join(DataDir, "ssh_host_dsa_key.pub"), filename:join(System, "ssh_host_dsa_key.pub")),
+ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_dsa_known_host(DataDir, UserDir),
setup_dsa_auth_keys(DataDir, UserDir).
@@ -294,10 +295,21 @@ setup_rsa(DataDir, UserDir) ->
System = filename:join(UserDir, "system"),
file:make_dir(System),
file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key")),
- file:copy(filename:join(DataDir, "ssh_host_rsa_key"), filename:join(System, "ssh_host_rsa_key.pub")),
+ file:copy(filename:join(DataDir, "ssh_host_rsa_key.pub"), filename:join(System, "ssh_host_rsa_key.pub")),
+ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
setup_rsa_known_host(DataDir, UserDir),
setup_rsa_auth_keys(DataDir, UserDir).
+setup_ecdsa(Size, DataDir, UserDir) ->
+ file:copy(filename:join(DataDir, "id_ecdsa"++Size), filename:join(UserDir, "id_ecdsa")),
+ System = filename:join(UserDir, "system"),
+ file:make_dir(System),
+ file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size), filename:join(System, "ssh_host_ecdsa_key")),
+ file:copy(filename:join(DataDir, "ssh_host_ecdsa_key"++Size++".pub"), filename:join(System, "ssh_host_ecdsa_key.pub")),
+ct:pal("DataDir ~p:~n ~p~n~nSystDir ~p:~n ~p~n~nUserDir ~p:~n ~p",[DataDir, file:list_dir(DataDir), System, file:list_dir(System), UserDir, file:list_dir(UserDir)]),
+ setup_ecdsa_known_host(Size, System, UserDir),
+ setup_ecdsa_auth_keys(Size, UserDir, UserDir).
+
clean_dsa(UserDir) ->
del_dirs(filename:join(UserDir, "system")),
file:delete(filename:join(UserDir,"id_dsa")),
@@ -349,6 +361,11 @@ setup_rsa_known_host(SystemDir, UserDir) ->
[{Key, _}] = public_key:ssh_decode(SshBin, public_key),
setup_known_hosts(Key, UserDir).
+setup_ecdsa_known_host(_Size, SystemDir, UserDir) ->
+ {ok, SshBin} = file:read_file(filename:join(SystemDir, "ssh_host_ecdsa_key.pub")),
+ [{Key, _}] = public_key:ssh_decode(SshBin, public_key),
+ setup_known_hosts(Key, UserDir).
+
setup_known_hosts(Key, UserDir) ->
{ok, Hostname} = inet:gethostname(),
{ok, {A, B, C, D}} = inet:getaddr(Hostname, inet),
@@ -376,6 +393,19 @@ setup_rsa_auth_keys(Dir, UserDir) ->
PKey = #'RSAPublicKey'{publicExponent = E, modulus = N},
setup_auth_keys([{ PKey, [{comment, "Test"}]}], UserDir).
+setup_ecdsa_auth_keys(Size, Dir, UserDir) ->
+ {ok, Pem} = file:read_file(filename:join(Dir, "id_ecdsa")),
+ ECDSA = public_key:pem_entry_decode(hd(public_key:pem_decode(Pem))),
+ #'ECPrivateKey'{publicKey = Q,
+ parameters = {namedCurve,Id0}} = ECDSA,
+ PKey = #'ECPoint'{point = Q},
+ Id = case pubkey_cert_records:namedCurves(Id0) of
+ secp256r1 when Size=="256" -> <<"nistp256">>;
+ secp384r1 when Size=="384" -> <<"nistp384">>;
+ secp521r1 when Size=="521" -> <<"nistp521">>
+ end,
+ setup_auth_keys([{ {PKey,Id}, [{comment, "Test"}]}], UserDir).
+
setup_auth_keys(Keys, Dir) ->
AuthKeys = public_key:ssh_encode(Keys, auth_keys),
AuthKeysFile = filename:join(Dir, "authorized_keys"),
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index bc51ae0724..c0dee6a1d3 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -389,7 +389,7 @@ erlang_server_openssh_client_kexs(Config) when is_list(Config) ->
{Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
{failfun, fun ssh_test_lib:failfun/2},
{preferred_algorithms,
- [{kex,ssh_transport:supported_algorithms(kex)}]}
+ [{kex,ssh_transport:default_algorithms(kex)}]}
]),
ct:sleep(500),