From 3507ea008839ad68dc16060a2696e3efde551684 Mon Sep 17 00:00:00 2001
From: Hans Nilsson <hans@erlang.org>
Date: Thu, 18 May 2017 20:33:14 +0200
Subject: ssh: fix the rsa-sha2-* hostkey verify error

---
 lib/public_key/src/pubkey_ssh.erl | 18 ++++++++----------
 lib/ssh/src/ssh_message.erl       |  4 ++--
 lib/ssh/src/ssh_transport.erl     | 18 +++++++++++-------
 3 files changed, 21 insertions(+), 19 deletions(-)

diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index 6974afa992..9bda76d670 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -408,10 +408,11 @@ comma_list_encode([Option | Rest], Acc) ->
     comma_list_encode(Rest, Acc ++ "," ++ Option).
 
 
-%% An experimental fix adding the signature algorithm name as the last element in a tuple...
-
 ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
     ssh2_pubkey_encode({#'RSAPublicKey'{modulus = N, publicExponent = E}, 'ssh-rsa'});
+
+ssh2_pubkey_encode({Key, 'rsa-sha2-256'}) -> ssh2_pubkey_encode({Key, 'ssh-rsa'});
+ssh2_pubkey_encode({Key, 'rsa-sha2-512'}) -> ssh2_pubkey_encode({Key, 'ssh-rsa'});
 ssh2_pubkey_encode({#'RSAPublicKey'{modulus = N, publicExponent = E}, SignAlg}) ->
     SignAlgName = list_to_binary(atom_to_list(SignAlg)),
     StrLen = size(SignAlgName),
@@ -448,16 +449,12 @@ ssh2_pubkey_encode(Key={#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
 ssh2_pubkey_decode(Bin = <<?UINT32(Len), Type:Len/binary, _/binary>>) ->
     ssh2_pubkey_decode(Type, Bin).
 
-%% An experimental fix with the Signature Algorithm Name
-ssh2_pubkey_decode(SignAlgName,
+ssh2_pubkey_decode(<<"rsa-sha2-256">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
+ssh2_pubkey_decode(<<"rsa-sha2-512">>, Bin) -> ssh2_pubkey_decode(<<"ssh-rsa">>, Bin);
+ssh2_pubkey_decode(<<"ssh-rsa">>,
 		   <<?UINT32(Len),   _:Len/binary,
 		     ?UINT32(SizeE), E:SizeE/binary,
-		     ?UINT32(SizeN), N:SizeN/binary>>)
-  when SignAlgName == <<"ssh-rsa">> ;
-       SignAlgName == <<"rsa-sha2-256">> ;
-       SignAlgName == <<"rsa-sha2-384">> ;
-       SignAlgName == <<"rsa-sha2-512">> 
-       ->
+		     ?UINT32(SizeN), N:SizeN/binary>>) ->
     #'RSAPublicKey'{modulus = erlint(SizeN, N),
 		    publicExponent = erlint(SizeE, E)};
 
@@ -471,6 +468,7 @@ ssh2_pubkey_decode(<<"ssh-dss">>,
      #'Dss-Parms'{p = erlint(SizeP, P),
 		  q = erlint(SizeQ, Q),
 		  g = erlint(SizeG, G)}};
+
 ssh2_pubkey_decode(<<"ecdsa-sha2-",Id/binary>>,
 		   <<?UINT32(Len), ECDSA_SHA2_etc:Len/binary,
 		     ?UINT32(SizeId), Id:SizeId/binary,
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index 609040826f..4f2eeca026 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -598,8 +598,8 @@ decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
 %%% Signature decode/encode
 %%%
 
-decode_signature(<<?DEC_BIN(_Alg,__0), ?UINT32(_), Signature/binary>>) ->
-    Signature.
+decode_signature(<<?DEC_BIN(Alg,__0), ?UINT32(_), Signature/binary>>) ->
+    {binary_to_list(Alg), Signature}.
 
 
 encode_signature({#'RSAPublicKey'{},Sign}, Signature) ->
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 1a15798080..412f5de9de 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -776,16 +776,20 @@ extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
     {#'ECPoint'{point=Q}, {namedCurve,OID}}.
 
 
-verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, Signature) ->
-    case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey) of
-	false ->
-	    {error, bad_signature};
-	true ->
-	    known_host_key(SSH, PublicKey, public_algo(PublicKey))
+verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) ->
+    case atom_to_list(Alg#alg.hkey) of
+        AlgStr ->
+            case verify(Digest, sha(Alg#alg.hkey), Signature, PublicKey) of
+                false ->
+                    {error, bad_signature};
+                true ->
+                    known_host_key(SSH, PublicKey, public_algo(PublicKey))
+            end;
+        _ ->
+            {error, bad_signature_name}
     end.
 
 
-
 accepted_host(Ssh, PeerName, Public, Opts) ->
     case ?GET_OPT(silently_accept_hosts, Opts) of
 
-- 
cgit v1.2.3