aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssh/src/ssh_transport.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssh/src/ssh_transport.erl')
-rw-r--r--lib/ssh/src/ssh_transport.erl87
1 files changed, 53 insertions, 34 deletions
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 46154cf536..90a94a7e86 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -426,7 +426,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
K = compute_key(dh, E, Private, [P,G]),
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {E,Public,K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {E,Public,K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kexdh_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -451,13 +451,12 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
f = F,
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}},
- algorithms = #alg{kex=Kex,
- hkey=SignAlg}} = Ssh0) ->
+ algorithms = #alg{kex=Kex}} = Ssh0) ->
%% client
if
1=<F, F=<(P-1)->
K = compute_key(dh, F, Private, [P,G]),
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Public,F,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Public,F,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -590,7 +589,7 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
1<K, K<(P-1) ->
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Kex), {Min,NBits,Max,P,G,E,Public,K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh} =
ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -620,8 +619,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}},
keyex_info = {Min, Max, NBits},
- algorithms = #alg{kex=Kex,
- hkey=SignAlg}} =
+ algorithms = #alg{kex=Kex}} =
Ssh0) ->
%% client
if
@@ -629,7 +627,7 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostK
K = compute_key(dh, F, Private, [P,G]),
if
1<K, K<(P-1) ->
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Kex), {Min,NBits,Max,P,G,Public,F,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -676,7 +674,7 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
K ->
MyPrivHostKey = get_host_key(Ssh0, SignAlg),
MyPubHostKey = extract_public_key(MyPrivHostKey),
- H = kex_hash(Ssh0, MyPubHostKey, SignAlg, sha(Curve), {PeerPublic, MyPublic, K}),
+ H = kex_hash(Ssh0, MyPubHostKey, sha(Curve), {PeerPublic, MyPublic, K}),
H_SIG = sign(H, sha(SignAlg), MyPrivHostKey),
{SshPacket, Ssh1} =
ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = {MyPubHostKey,SignAlg},
@@ -699,15 +697,15 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = PeerPubHostKey,
q_s = PeerPublic,
h_sig = H_SIG},
- #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve},
- algorithms = #alg{hkey=SignAlg}} = Ssh0
+ #ssh{keyex_key = {{MyPublic,MyPrivate}, Curve}
+ } = Ssh0
) ->
%% at client
try
compute_key(ecdh, PeerPublic, MyPrivate, Curve)
of
K ->
- H = kex_hash(Ssh0, PeerPubHostKey, SignAlg, sha(Curve), {MyPublic,PeerPublic,K}),
+ H = kex_hash(Ssh0, PeerPubHostKey, sha(Curve), {MyPublic,PeerPublic,K}),
case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
@@ -797,8 +795,14 @@ get_host_key(SSH, SignAlg) ->
#ssh{key_cb = {KeyCb,KeyCbOpts}, opts = Opts} = SSH,
UserOpts = ?GET_OPT(user_options, Opts),
case KeyCb:host_key(SignAlg, [{key_cb_private,KeyCbOpts}|UserOpts]) of
- {ok, PrivHostKey} -> PrivHostKey;
- Result -> exit({error, {Result, unsupported_key_type}})
+ {ok, PrivHostKey} ->
+ %% Check the key - the KeyCb may be a buggy plugin
+ case valid_key_sha_alg(PrivHostKey, SignAlg) of
+ true -> PrivHostKey;
+ false -> exit({error, bad_hostkey})
+ end;
+ Result ->
+ exit({error, {Result, unsupported_key_type}})
end.
extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
@@ -807,7 +811,15 @@ extract_public_key(#'DSAPrivateKey'{y = Y, p = P, q = Q, g = G}) ->
{Y, #'Dss-Parms'{p=P, q=Q, g=G}};
extract_public_key(#'ECPrivateKey'{parameters = {namedCurve,OID},
publicKey = Q}) ->
- {#'ECPoint'{point=Q}, {namedCurve,OID}}.
+ {#'ECPoint'{point=Q}, {namedCurve,OID}};
+extract_public_key(#{engine:=_, key_id:=_, algorithm:=Alg} = M) ->
+ case {Alg, crypto:privkey_to_pubkey(Alg, M)} of
+ {rsa, [E,N]} ->
+ #'RSAPublicKey'{modulus = N, publicExponent = E};
+ {dss, [P,Q,G,Y]} ->
+ {Y, #'Dss-Parms'{p=P, q=Q, g=G}}
+ end.
+
verify_host_key(#ssh{algorithms=Alg}=SSH, PublicKey, Digest, {AlgStr,Signature}) ->
@@ -1257,10 +1269,12 @@ payload(<<PacketLen:32, PaddingLen:8, PayloadAndPadding/binary>>) ->
<<Payload:PayloadLen/binary, _/binary>> = PayloadAndPadding,
Payload.
+sign(SigData, HashAlg, #{algorithm:=dss} = Key) ->
+ mk_dss_sig(crypto:sign(dss, HashAlg, SigData, Key));
+sign(SigData, HashAlg, #{algorithm:=SigAlg} = Key) ->
+ crypto:sign(SigAlg, HashAlg, SigData, Key);
sign(SigData, HashAlg, #'DSAPrivateKey'{} = Key) ->
- DerSignature = public_key:sign(SigData, HashAlg, 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>>;
+ mk_dss_sig(public_key:sign(SigData, HashAlg, Key));
sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
DerEncodedSign = public_key:sign(SigData, HashAlg, Key),
#'ECDSA-Sig-Value'{r=R, s=S} = public_key:der_decode('ECDSA-Sig-Value', DerEncodedSign),
@@ -1268,6 +1282,12 @@ sign(SigData, HashAlg, Key = #'ECPrivateKey'{}) ->
sign(SigData, HashAlg, Key) ->
public_key:sign(SigData, HashAlg, Key).
+
+mk_dss_sig(DerSignature) ->
+ #'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>>.
+
+
verify(PlainText, HashAlg, Sig, {_, #'Dss-Parms'{}} = Key) ->
case Sig of
<<R:160/big-unsigned-integer, S:160/big-unsigned-integer>> ->
@@ -1794,11 +1814,11 @@ hash(K, H, Ki, N, HashAlg) ->
hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HashAlg).
%%%----------------------------------------------------------------
-kex_hash(SSH, Key, SignAlg, HashAlg, Args) ->
- crypto:hash(HashAlg, kex_plaintext(SSH,Key,SignAlg,Args)).
+kex_hash(SSH, Key, HashAlg, Args) ->
+ crypto:hash(HashAlg, kex_plaintext(SSH,Key,Args)).
-kex_plaintext(SSH, Key, SignAlg, Args) ->
- EncodedKey = public_key:ssh_encode({Key,SignAlg}, ssh2_pubkey),
+kex_plaintext(SSH, Key, Args) ->
+ EncodedKey = public_key:ssh_encode(Key, ssh2_pubkey),
<<?Estring(SSH#ssh.c_version), ?Estring(SSH#ssh.s_version),
?Ebinary(SSH#ssh.c_keyinit), ?Ebinary(SSH#ssh.s_keyinit),
?Ebinary(EncodedKey),
@@ -1819,6 +1839,8 @@ kex_alg_dependent({Min, NBits, Max, Prime, Gen, E, F, K}) ->
%%%----------------------------------------------------------------
+valid_key_sha_alg(#{engine:=_, key_id:=_}, _Alg) -> true; % Engine key
+
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-512') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-384') -> true;
valid_key_sha_alg(#'RSAPublicKey'{}, 'rsa-sha2-256') -> true;
@@ -1832,11 +1854,14 @@ valid_key_sha_alg(#'RSAPrivateKey'{}, 'ssh-rsa' ) -> true;
valid_key_sha_alg({_, #'Dss-Parms'{}}, 'ssh-dss') -> true;
valid_key_sha_alg(#'DSAPrivateKey'{}, 'ssh-dss') -> true;
-valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
-valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> sha(OID) == sha(Alg);
+valid_key_sha_alg({#'ECPoint'{},{namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
+valid_key_sha_alg(#'ECPrivateKey'{parameters = {namedCurve,OID}}, Alg) -> valid_key_sha_alg_ec(OID, Alg);
valid_key_sha_alg(_, _) -> false.
-
+valid_key_sha_alg_ec(OID, Alg) ->
+ Curve = public_key:oid2ssh_curvename(OID),
+ Alg == list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)).
+
public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa'; % FIXME: Not right with draft-curdle-rsa-sha2
public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
@@ -2002,12 +2027,6 @@ same(Algs) -> [{client2server,Algs}, {server2client,Algs}].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
trim_tail(Str) ->
- lists:reverse(trim_head(lists:reverse(Str))).
-
-trim_head([$\s|Cs]) -> trim_head(Cs);
-trim_head([$\t|Cs]) -> trim_head(Cs);
-trim_head([$\n|Cs]) -> trim_head(Cs);
-trim_head([$\r|Cs]) -> trim_head(Cs);
-trim_head(Cs) -> Cs.
-
-
+ lists:takewhile(fun(C) ->
+ C=/=$\r andalso C=/=$\n
+ end, Str).