aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorHans Nilsson <[email protected]>2015-10-19 12:43:47 +0200
committerHans Nilsson <[email protected]>2015-10-19 12:43:47 +0200
commit5ea9a240688f43f81eb607d79e4161f9085024e7 (patch)
tree15795977e2a9cff06913cc1c41ba6bf96b39fa7d /lib
parent5f73e439578c4356db26804f47e8b50901a8e45a (diff)
parentfdae664715f6b64e3f0c61729344bc372b67e1e2 (diff)
downloadotp-5ea9a240688f43f81eb607d79e4161f9085024e7.tar.gz
otp-5ea9a240688f43f81eb607d79e4161f9085024e7.tar.bz2
otp-5ea9a240688f43f81eb607d79e4161f9085024e7.zip
Merge branch 'maint'
* maint: ssh: Implemented ssh_transport:ecdh_validate_public_key (partly) ssh: dialyzer fix public_key: add/update -spec for ssh functions ssh, public_key: use pubkey encode/decode in app public_key ssh, public_key: Change EC Public Key representation to what was intended
Diffstat (limited to 'lib')
-rw-r--r--lib/public_key/src/pubkey_ssh.erl153
-rw-r--r--lib/public_key/src/public_key.erl36
-rw-r--r--lib/ssh/src/ssh.erl5
-rw-r--r--lib/ssh/src/ssh.hrl13
-rw-r--r--lib/ssh/src/ssh_auth.erl69
-rw-r--r--lib/ssh/src/ssh_file.erl13
-rw-r--r--lib/ssh/src/ssh_message.erl173
-rw-r--r--lib/ssh/src/ssh_transport.erl187
-rw-r--r--lib/ssh/test/ssh_test_lib.erl11
-rw-r--r--lib/ssh/test/ssh_trpt_test_lib.erl2
10 files changed, 336 insertions, 326 deletions
diff --git a/lib/public_key/src/pubkey_ssh.erl b/lib/public_key/src/pubkey_ssh.erl
index 26fbeb68ce..82042550a0 100644
--- a/lib/public_key/src/pubkey_ssh.erl
+++ b/lib/public_key/src/pubkey_ssh.erl
@@ -21,7 +21,8 @@
-include("public_key.hrl").
--export([decode/2, encode/2]).
+-export([decode/2, encode/2
+ ]).
-define(UINT32(X), X:32/unsigned-big-integer).
-define(STRING(X), ?UINT32((size(X))), (X)/binary).
@@ -33,13 +34,16 @@
%% are still compliant.)" So we choose to use 68 also.
-define(ENCODED_LINE_LENGTH, 68).
+
%%====================================================================
%% Internal application API
%%====================================================================
%%--------------------------------------------------------------------
-spec decode(binary(), public_key | public_key:ssh_file()) ->
- [{public_key:public_key(), Attributes::list()}].
+ [{public_key:public_key(), Attributes::list()}]
+ ; (binary(), ssh2_pubkey) -> public_key:public_key()
+ .
%%
%% Description: Decodes a ssh file-binary.
%%--------------------------------------------------------------------
@@ -52,15 +56,21 @@ decode(Bin, public_key)->
end;
decode(Bin, rfc4716_public_key) ->
rfc4716_decode(Bin);
+decode(Bin, ssh2_pubkey) ->
+ ssh2_pubkey_decode(Bin);
decode(Bin, Type) ->
openssh_decode(Bin, Type).
%%--------------------------------------------------------------------
-spec encode([{public_key:public_key(), Attributes::list()}], public_key:ssh_file()) ->
- binary().
+ binary()
+ ; (public_key:public_key(), ssh2_pubkey) -> binary()
+ .
%%
%% Description: Encodes a list of ssh file entries.
%%--------------------------------------------------------------------
+encode(Bin, ssh2_pubkey) ->
+ ssh2_pubkey_encode(Bin);
encode(Entries, Type) ->
iolist_to_binary(lists:map(fun({Key, Attributes}) ->
do_encode(Type, Key, Attributes)
@@ -133,12 +143,11 @@ rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
#'Dss-Parms'{p = erlint(SizeP, P),
q = erlint(SizeQ, Q),
g = erlint(SizeG, G)}};
-rfc4716_pubkey_decode(<<?UINT32(Len), Type:Len/binary,
+rfc4716_pubkey_decode(<<?UINT32(Len), ECDSA_SHA2_etc:Len/binary,
?UINT32(SizeId), Id:SizeId/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary>>) when Type == <<"ecdsa-sha2-nistp256">>;
- Type == <<"ecdsa-sha2-nistp384">>;
- Type == <<"ecdsa-sha2-nistp521">> ->
- {#'ECPoint'{point = Q}, Id}.
+ ?UINT32(SizeQ), Q:SizeQ/binary>>) ->
+ <<"ecdsa-sha2-", Id/binary>> = ECDSA_SHA2_etc,
+ {#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}.
openssh_decode(Bin, FileType) ->
Lines = binary:split(Bin, <<"\n">>, [global]),
@@ -192,62 +201,41 @@ do_openssh_decode(known_hosts = FileType, [Line | Lines], Acc) ->
end;
do_openssh_decode(openssh_public_key = FileType, [Line | Lines], Acc) ->
- case split_n(2, Line, []) of
- [KeyType, Base64Enc] when KeyType == <<"ssh-rsa">>;
- KeyType == <<"ssh-dss">>;
- KeyType == <<"ecdsa-sha2-nistp256">>;
- KeyType == <<"ecdsa-sha2-nistp384">>;
- KeyType == <<"ecdsa-sha2-nistp521">> ->
+ [KeyType, Base64Enc | Comment0] = split_n(2, Line, []),
+ KnownKeyType =
+ case KeyType of
+ <<"ssh-rsa">> -> true;
+ <<"ssh-dss">> -> true;
+ <<"ecdsa-sha2-",Curve/binary>> -> is_ssh_curvename(Curve);
+ _ -> false
+ end,
+
+ case Comment0 of
+ [] when KnownKeyType==true ->
do_openssh_decode(FileType, Lines,
[{openssh_pubkey_decode(KeyType, Base64Enc),
[]} | Acc]);
- [KeyType, Base64Enc | Comment0] when KeyType == <<"ssh-rsa">>;
- KeyType == <<"ssh-dss">>;
- KeyType == <<"ecdsa-sha2-nistp256">>;
- KeyType == <<"ecdsa-sha2-nistp384">>;
- KeyType == <<"ecdsa-sha2-nistp521">> ->
+ _ when KnownKeyType==true ->
Comment = string:strip(string_decode(iolist_to_binary(Comment0)), right, $\n),
do_openssh_decode(FileType, Lines,
[{openssh_pubkey_decode(KeyType, Base64Enc),
[{comment, Comment}]} | Acc])
end.
+
decode_comment([]) ->
[];
decode_comment(Comment) ->
[{comment, string_decode(iolist_to_binary(Comment))}].
-openssh_pubkey_decode(<<"ssh-rsa">>, Base64Enc) ->
- <<?UINT32(StrLen), _:StrLen/binary,
- ?UINT32(SizeE), E:SizeE/binary,
- ?UINT32(SizeN), N:SizeN/binary>>
- = base64:mime_decode(Base64Enc),
- #'RSAPublicKey'{modulus = erlint(SizeN, N),
- publicExponent = erlint(SizeE, E)};
-
-openssh_pubkey_decode(<<"ssh-dss">>, Base64Enc) ->
- <<?UINT32(StrLen), _:StrLen/binary,
- ?UINT32(SizeP), P:SizeP/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary,
- ?UINT32(SizeG), G:SizeG/binary,
- ?UINT32(SizeY), Y:SizeY/binary>>
- = base64:mime_decode(Base64Enc),
- {erlint(SizeY, Y),
- #'Dss-Parms'{p = erlint(SizeP, P),
- q = erlint(SizeQ, Q),
- g = erlint(SizeG, G)}};
-
-openssh_pubkey_decode(<<"ecdsa-sha2-", Id/binary>>, Base64Enc) ->
- %% rfc5656#section-3.1
- <<?UINT32(StrLen), _:StrLen/binary,
- ?UINT32(SizeId), Id:SizeId/binary,
- ?UINT32(SizeQ), Q:SizeQ/binary>>
- = base64:mime_decode(Base64Enc),
- {#'ECPoint'{point = Q}, Id};
-
-openssh_pubkey_decode(KeyType, Base64Enc) ->
- {KeyType, base64:mime_decode(Base64Enc)}.
+openssh_pubkey_decode(Type, Base64Enc) ->
+ try
+ ssh2_pubkey_decode(Type, base64:mime_decode(Base64Enc))
+ catch
+ _:_ ->
+ {Type, base64:mime_decode(Base64Enc)}
+ end.
erlint(MPIntSize, MPIntValue) ->
@@ -372,12 +360,9 @@ line_end("") ->
line_end(Comment) ->
[" ", Comment, "\n"].
-key_type(#'RSAPublicKey'{}) ->
- <<"ssh-rsa">>;
-key_type({_, #'Dss-Parms'{}}) ->
- <<"ssh-dss">>;
-key_type({#'ECPoint'{}, Id}) ->
- <<"ecdsa-sha2-",Id/binary>>.
+key_type(#'RSAPublicKey'{}) -> <<"ssh-rsa">>;
+key_type({_, #'Dss-Parms'{}}) -> <<"ssh-dss">>;
+key_type({#'ECPoint'{}, {namedCurve,Curve}}) -> <<"ecdsa-sha2-", (public_key:oid2ssh_curvename(Curve))/binary>>.
comma_list_encode([Option], []) ->
Option;
@@ -408,25 +393,48 @@ ssh2_pubkey_encode({Y, #'Dss-Parms'{p = P, q = Q, g = G}}) ->
QBin/binary,
GBin/binary,
YBin/binary>>;
-ssh2_pubkey_encode({#'ECPoint'{point = Q}, Id}) ->
- TypeStr = <<"ecdsa-sha2-", Id/binary>>,
+ssh2_pubkey_encode(Key={#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
+ TypeStr = key_type(Key),
StrLen = size(TypeStr),
+ IdB = public_key:oid2ssh_curvename(OID),
<<?UINT32(StrLen), TypeStr:StrLen/binary,
- (string(Id))/binary,
+ (string(IdB))/binary,
(string(Q))/binary>>.
-is_key_field(<<"ssh-dss">>) ->
- true;
-is_key_field(<<"ssh-rsa">>) ->
- true;
-is_key_field(<<"ecdsa-sha2-nistp256">>) ->
- true;
-is_key_field(<<"ecdsa-sha2-nistp384">>) ->
- true;
-is_key_field(<<"ecdsa-sha2-nistp521">>) ->
- true;
-is_key_field(_) ->
- false.
+
+ssh2_pubkey_decode(Bin = <<?UINT32(Len), Type:Len/binary, _/binary>>) ->
+ ssh2_pubkey_decode(Type, Bin).
+
+ssh2_pubkey_decode(<<"ssh-rsa">>,
+ <<?UINT32(Len), _:Len/binary,
+ ?UINT32(SizeE), E:SizeE/binary,
+ ?UINT32(SizeN), N:SizeN/binary>>) ->
+ #'RSAPublicKey'{modulus = erlint(SizeN, N),
+ publicExponent = erlint(SizeE, E)};
+
+ssh2_pubkey_decode(<<"ssh-dss">>,
+ <<?UINT32(Len), _:Len/binary,
+ ?UINT32(SizeP), P:SizeP/binary,
+ ?UINT32(SizeQ), Q:SizeQ/binary,
+ ?UINT32(SizeG), G:SizeG/binary,
+ ?UINT32(SizeY), Y:SizeY/binary>>) ->
+ {erlint(SizeY, Y),
+ #'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,
+ ?UINT32(SizeQ), Q:SizeQ/binary>>) ->
+ <<"ecdsa-sha2-", Id/binary>> = ECDSA_SHA2_etc,
+ {#'ECPoint'{point = Q}, {namedCurve,public_key:ssh_curvename2oid(Id)}}.
+
+
+
+is_key_field(<<"ssh-dss">>) -> true;
+is_key_field(<<"ssh-rsa">>) -> true;
+is_key_field(<<"ecdsa-sha2-",Id/binary>>) -> is_ssh_curvename(Id);
+is_key_field(_) -> false.
is_bits_field(Part) ->
try list_to_integer(binary_to_list(Part)) of
@@ -546,3 +554,8 @@ string(X) when is_binary(X) ->
<< ?STRING(X) >>;
string(X) ->
<< ?STRING(list_to_binary(X)) >>.
+
+is_ssh_curvename(Id) -> try public_key:ssh_curvename2oid(Id) of _ -> true
+ catch _:_ -> false
+ end.
+
diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl
index 2f4cc64c2a..8288f68f7f 100644
--- a/lib/public_key/src/public_key.erl
+++ b/lib/public_key/src/public_key.erl
@@ -47,6 +47,7 @@
pkix_normalize_name/1,
pkix_path_validation/3,
ssh_decode/2, ssh_encode/2,
+ ssh_curvename2oid/1, oid2ssh_curvename/1,
pkix_crls_validate/3,
pkix_dist_point/1,
pkix_dist_points/1,
@@ -711,7 +712,9 @@ pkix_crls_validate(OtpCert, DPAndCRLs0, Options) ->
%%--------------------------------------------------------------------
--spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}].
+-spec ssh_decode(binary(), public_key | ssh_file()) -> [{public_key(), Attributes::list()}]
+ ; (binary(), ssh2_pubkey) -> public_key()
+ .
%%
%% Description: Decodes a ssh file-binary. In the case of know_hosts
%% or auth_keys the binary may include one or more lines of the
@@ -724,12 +727,15 @@ ssh_decode(SshBin, Type) when is_binary(SshBin),
Type == rfc4716_public_key;
Type == openssh_public_key;
Type == auth_keys;
- Type == known_hosts ->
+ Type == known_hosts;
+ Type == ssh2_pubkey ->
pubkey_ssh:decode(SshBin, Type).
%%--------------------------------------------------------------------
--spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) ->
- binary().
+-spec ssh_encode([{public_key(), Attributes::list()}], ssh_file()) -> binary()
+ ; (public_key(), ssh2_pubkey) -> binary()
+ .
+%%
%% Description: Encodes a list of ssh file entries (public keys and
%% attributes) to a binary. Possible attributes depends on the file
%% type.
@@ -738,10 +744,30 @@ ssh_encode(Entries, Type) when is_list(Entries),
Type == rfc4716_public_key;
Type == openssh_public_key;
Type == auth_keys;
- Type == known_hosts ->
+ Type == known_hosts;
+ Type == ssh2_pubkey ->
pubkey_ssh:encode(Entries, Type).
%%--------------------------------------------------------------------
+-spec ssh_curvename2oid(binary()) -> oid().
+
+%% Description: Converts from the ssh name of elliptic curves to
+%% the OIDs.
+%%--------------------------------------------------------------------
+ssh_curvename2oid(<<"nistp256">>) -> ?'secp256r1';
+ssh_curvename2oid(<<"nistp384">>) -> ?'secp384r1';
+ssh_curvename2oid(<<"nistp521">>) -> ?'secp521r1'.
+
+%%--------------------------------------------------------------------
+-spec oid2ssh_curvename(oid()) -> binary().
+
+%% Description: Converts from elliptic curve OIDs to the ssh name.
+%%--------------------------------------------------------------------
+oid2ssh_curvename(?'secp256r1') -> <<"nistp256">>;
+oid2ssh_curvename(?'secp384r1') -> <<"nistp384">>;
+oid2ssh_curvename(?'secp521r1') -> <<"nistp521">>.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
do_verify(DigestOrPlainText, DigestType, Signature,
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index b6ee29efbb..049018b21c 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -391,8 +391,9 @@ handle_option([{negotiation_timeout, _} = Opt|Rest], SocketOptions, SshOptions)
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{parallel_login, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
-handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
- handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
+%% (Is handled by proplists:unfold above:)
+%% handle_option([parallel_login|Rest], SocketOptions, SshOptions) ->
+%% handle_option(Rest, SocketOptions, [handle_ssh_option({parallel_login,true}) | SshOptions]);
handle_option([{minimal_remote_max_packet_size, _} = Opt|Rest], SocketOptions, SshOptions) ->
handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
handle_option([{id_string, _ID} = Opt|Rest], SocketOptions, SshOptions) ->
diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl
index da64e4abf9..fc9d60c500 100644
--- a/lib/ssh/src/ssh.hrl
+++ b/lib/ssh/src/ssh.hrl
@@ -37,13 +37,16 @@
-define(FALSE, 0).
-define(TRUE, 1).
%% basic binary constructors
--define(BOOLEAN(X), X:8/unsigned-big-integer).
--define(BYTE(X), X:8/unsigned-big-integer).
--define(UINT16(X), X:16/unsigned-big-integer).
--define(UINT32(X), X:32/unsigned-big-integer).
--define(UINT64(X), X:64/unsigned-big-integer).
+-define(BOOLEAN(X), (X):8/unsigned-big-integer).
+-define(BYTE(X), (X):8/unsigned-big-integer).
+-define(UINT16(X), (X):16/unsigned-big-integer).
+-define(UINT32(X), (X):32/unsigned-big-integer).
+-define(UINT64(X), (X):64/unsigned-big-integer).
-define(STRING(X), ?UINT32((size(X))), (X)/binary).
+-define(DEC_BIN(X,Len), ?UINT32(Len), X:Len/binary ).
+-define(DEC_MPINT(I,Len), ?UINT32(Len), I:Len/big-signed-integer-unit:8 ).
+
%% building macros
-define(boolean(X),
case X of
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 0c16e19701..04749fcf8e 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -44,15 +44,15 @@ publickey_msg([Alg, #ssh{user = User,
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),
- case encode_public_key(StrAlgo, Key) of
+ {ok, PrivKey} ->
+ StrAlgo = atom_to_list(Alg),
+ case encode_public_key(StrAlgo, ssh_transport:extract_public_key(PrivKey)) of
not_ok ->
not_ok;
PubKeyBlob ->
SigData = build_sig_data(SessionId,
User, Service, PubKeyBlob, StrAlgo),
- Sig = ssh_transport:sign(SigData, Hash, Key),
+ Sig = ssh_transport:sign(SigData, Hash, PrivKey),
SigBlob = list_to_binary([?string(StrAlgo), ?binary(Sig)]),
ssh_transport:ssh_packet(
#ssh_msg_userauth_request{user = User,
@@ -430,12 +430,6 @@ 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('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) ->
@@ -487,47 +481,18 @@ keyboard_interact_fun(KbdInteractFun, Name, Instr, PromptInfos, NumPrompts) ->
language = "en"}})
end.
-decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
- ?UINT32(Len1), E:Len1/big-signed-integer-unit:8,
- ?UINT32(Len2), N:Len2/big-signed-integer-unit:8>>
- ,"ssh-rsa") ->
- {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}};
-decode_public_key_v2(<<?UINT32(Len0), _:Len0/binary,
- ?UINT32(Len1), P:Len1/big-signed-integer-unit:8,
- ?UINT32(Len2), Q:Len2/big-signed-integer-unit:8,
- ?UINT32(Len3), G:Len3/big-signed-integer-unit:8,
- ?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("ssh-rsa", #'RSAPrivateKey'{publicExponent = E, modulus = N}) ->
- ssh_bits:encode(["ssh-rsa",E,N], [string,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])
+decode_public_key_v2(Bin, _Type) ->
+ try
+ public_key:ssh_decode(Bin, ssh2_pubkey)
+ of
+ Key -> {ok, Key}
+ catch
+ _:_ -> {error, bad_format}
end.
-ecdsa_id({namedCurve,?'secp256r1'}) -> "nistp256";
-ecdsa_id({namedCurve,?'secp384r1'}) -> "nistp384";
-ecdsa_id({namedCurve,?'secp521r1'}) -> "nistp521".
+encode_public_key(_Alg, Key) ->
+ try
+ public_key:ssh_encode(Key, ssh2_pubkey)
+ catch
+ _:_ -> not_ok
+ end.
diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl
index 4e6d58cbff..c087ce14d7 100644
--- a/lib/ssh/src/ssh_file.erl
+++ b/lib/ssh/src/ssh_file.erl
@@ -276,12 +276,13 @@ 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({#'ECPoint'{},{namedCurve,Curve}}, Alg) ->
+ case atom_to_list(Alg) of
+ "ecdsa-sha2-"++IdS ->
+ Curve == public_key:ssh_curvename2oid(list_to_binary(IdS));
+ _ ->
+ false
+ end;
key_match(_, _) ->
false.
diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl
index cfa11903fb..b6c4496be2 100644
--- a/lib/ssh/src/ssh_message.erl
+++ b/lib/ssh/src/ssh_message.erl
@@ -30,7 +30,7 @@
-include("ssh_auth.hrl").
-include("ssh_transport.hrl").
--export([encode/1, decode/1, encode_host_key/1, decode_keyboard_interactive_prompts/2]).
+-export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]).
encode(#ssh_msg_global_request{
name = Name,
@@ -227,8 +227,8 @@ encode(#ssh_msg_kexdh_reply{
f = F,
h_sig = Signature
}) ->
- EncKey = encode_host_key(Key),
- EncSign = encode_sign(Key, Signature),
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncSign = encode_signature(Key, Signature),
ssh_bits:encode([?SSH_MSG_KEXDH_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
encode(#ssh_msg_kex_dh_gex_request{
@@ -255,16 +255,16 @@ encode(#ssh_msg_kex_dh_gex_reply{
f = F,
h_sig = Signature
}) ->
- EncKey = encode_host_key(Key),
- EncSign = encode_sign(Key, Signature),
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncSign = encode_signature(Key, Signature),
ssh_bits:encode([?SSH_MSG_KEX_DH_GEX_REPLY, EncKey, F, EncSign], [byte, binary, mpint, binary]);
encode(#ssh_msg_kex_ecdh_init{q_c = Q_c}) ->
ssh_bits:encode([?SSH_MSG_KEX_ECDH_INIT, Q_c], [byte, mpint]);
encode(#ssh_msg_kex_ecdh_reply{public_host_key = Key, q_s = Q_s, h_sig = Sign}) ->
- EncKey = encode_host_key(Key),
- EncSign = encode_sign(Key, Sign),
+ EncKey = public_key:ssh_encode(Key, ssh2_pubkey),
+ EncSign = encode_signature(Key, Sign),
ssh_bits:encode([?SSH_MSG_KEX_ECDH_REPLY, EncKey, Q_s, EncSign], [byte, binary, mpint, binary]);
encode(#ssh_msg_ignore{data = Data}) ->
@@ -280,8 +280,7 @@ encode(#ssh_msg_debug{always_display = Bool,
%% Connection Messages
-decode(<<?BYTE(?SSH_MSG_GLOBAL_REQUEST), ?UINT32(Len), Name:Len/binary,
- ?BYTE(Bool), Data/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_GLOBAL_REQUEST), ?DEC_BIN(Name,__0), ?BYTE(Bool), Data/binary>>) ->
#ssh_msg_global_request{
name = Name,
want_reply = erl_boolean(Bool),
@@ -292,8 +291,7 @@ decode(<<?BYTE(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>) ->
decode(<<?BYTE(?SSH_MSG_REQUEST_FAILURE)>>) ->
#ssh_msg_request_failure{};
decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN),
- ?UINT32(Len), Type:Len/binary,
- ?UINT32(Sender), ?UINT32(Window), ?UINT32(Max),
+ ?DEC_BIN(Type,__0), ?UINT32(Sender), ?UINT32(Window), ?UINT32(Max),
Data/binary>>) ->
#ssh_msg_channel_open{
channel_type = binary_to_list(Type),
@@ -313,7 +311,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION), ?UINT32(Recipient), ?UINT32(
data = Data
};
decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?UINT32(Recipient), ?UINT32(Reason),
- ?UINT32(Len0), Desc:Len0/binary, ?UINT32(Len1), Lang:Len1/binary >>) ->
+ ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1) >> ) ->
#ssh_msg_channel_open_failure{
recipient_channel = Recipient,
reason = Reason,
@@ -326,13 +324,13 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?UINT32(Recipient), ?UINT32(Byte
bytes_to_add = Bytes
};
-decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?UINT32(Len), Data:Len/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?DEC_BIN(Data,__0)>>) ->
#ssh_msg_channel_data{
recipient_channel = Recipient,
data = Data
};
decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
- ?UINT32(DataType), ?UINT32(Len), Data:Len/binary>>) ->
+ ?UINT32(DataType), ?DEC_BIN(Data,__0)>>) ->
#ssh_msg_channel_extended_data{
recipient_channel = Recipient,
data_type_code = DataType,
@@ -347,8 +345,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_CLOSE), ?UINT32(Recipient)>>) ->
recipient_channel = Recipient
};
decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST), ?UINT32(Recipient),
- ?UINT32(Len), RequestType:Len/binary,
- ?BYTE(Bool), Data/binary>>) ->
+ ?DEC_BIN(RequestType,__0), ?BYTE(Bool), Data/binary>>) ->
#ssh_msg_channel_request{
recipient_channel = Recipient,
request_type = unicode:characters_to_list(RequestType),
@@ -366,9 +363,7 @@ decode(<<?BYTE(?SSH_MSG_CHANNEL_FAILURE), ?UINT32(Recipient)>>) ->
%%% Auth Messages
decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST),
- ?UINT32(Len0), User:Len0/binary,
- ?UINT32(Len1), Service:Len1/binary,
- ?UINT32(Len2), Method:Len2/binary,
+ ?DEC_BIN(User,__0), ?DEC_BIN(Service,__1), ?DEC_BIN(Method,__2),
Data/binary>>) ->
#ssh_msg_userauth_request{
user = unicode:characters_to_list(User),
@@ -378,7 +373,7 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST),
};
decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE),
- ?UINT32(Len0), Auths:Len0/binary,
+ ?DEC_BIN(Auths,__0),
?BYTE(Bool)>>) ->
#ssh_msg_userauth_failure {
authentications = unicode:characters_to_list(Auths),
@@ -388,16 +383,14 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE),
decode(<<?BYTE(?SSH_MSG_USERAUTH_SUCCESS)>>) ->
#ssh_msg_userauth_success{};
-decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER),
- ?UINT32(Len0), Banner:Len0/binary,
- ?UINT32(Len1), Lang:Len1/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER), ?DEC_BIN(Banner,__0), ?DEC_BIN(Lang,__1) >>) ->
#ssh_msg_userauth_banner{
message = Banner,
language = Lang
};
-decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
- ?UINT32(Len1), Inst:Len1/binary, ?UINT32(Len2), Lang:Len2/binary,
+decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST),
+ ?DEC_BIN(Name,__0), ?DEC_BIN(Inst,__1), ?DEC_BIN(Lang,__2),
?UINT32(NumPromtps), Data/binary>>) ->
#ssh_msg_userauth_info_request{
name = Name,
@@ -407,15 +400,14 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST), ?UINT32(Len0), Name:Len0/binary,
data = Data};
%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
-decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?UINT32(Len0), Prompt:Len0/binary,
- ?UINT32(Len1), Lang:Len1/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?DEC_BIN(Prompt,__0), ?DEC_BIN(Lang,__1) >>) ->
#ssh_msg_userauth_passwd_changereq{
prompt = Prompt,
languge = Lang
};
%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
-decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?UINT32(Len), Alg:Len/binary, KeyBlob/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?DEC_BIN(Alg,__0), KeyBlob/binary>>) ->
#ssh_msg_userauth_pk_ok{
algorithm_name = Alg,
key_blob = KeyBlob
@@ -430,18 +422,15 @@ decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
decode(<<?BYTE(?SSH_MSG_KEXINIT), Cookie:128, Data/binary>>) ->
decode_kex_init(Data, [Cookie, ssh_msg_kexinit], 10);
-decode(<<"dh",?BYTE(?SSH_MSG_KEXDH_INIT), ?UINT32(Len), E:Len/big-signed-integer-unit:8>>) ->
+decode(<<"dh",?BYTE(?SSH_MSG_KEXDH_INIT), ?DEC_MPINT(E,__0)>>) ->
#ssh_msg_kexdh_init{e = E
};
-decode(<<"dh", ?BYTE(?SSH_MSG_KEXDH_REPLY),
- ?UINT32(Len0), Key:Len0/binary,
- ?UINT32(Len1), F:Len1/big-signed-integer-unit:8,
- ?UINT32(Len2), Hashsign:Len2/binary>>) ->
+decode(<<"dh", ?BYTE(?SSH_MSG_KEXDH_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
#ssh_msg_kexdh_reply{
- public_host_key = decode_host_key(Key),
+ public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
f = F,
- h_sig = decode_sign(Hashsign)
+ h_sig = decode_signature(Hashsign)
};
decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST), ?UINT32(Min), ?UINT32(N), ?UINT32(Max)>>) ->
@@ -456,57 +445,48 @@ decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) ->
n = N
};
-decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP),
- ?UINT32(Len0), Prime:Len0/big-signed-integer-unit:8,
- ?UINT32(Len1), Generator:Len1/big-signed-integer-unit:8>>) ->
+decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?DEC_MPINT(Prime,__0), ?DEC_MPINT(Generator,__1) >>) ->
#ssh_msg_kex_dh_gex_group{
p = Prime,
g = Generator
};
-decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?UINT32(Len), E:Len/big-signed-integer-unit:8>>) ->
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?DEC_MPINT(E,__0)>>) ->
#ssh_msg_kex_dh_gex_init{
e = E
};
-decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY),
- ?UINT32(Len0), Key:Len0/binary,
- ?UINT32(Len1), F:Len1/big-signed-integer-unit:8,
- ?UINT32(Len2), Hashsign:Len2/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
#ssh_msg_kex_dh_gex_reply{
- public_host_key = decode_host_key(Key),
+ public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
f = F,
- h_sig = decode_sign(Hashsign)
+ h_sig = decode_signature(Hashsign)
};
-decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_INIT),
- ?UINT32(Len0), Q_c:Len0/big-signed-integer-unit:8>>) ->
+decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_INIT), ?DEC_MPINT(Q_c,__0)>>) ->
#ssh_msg_kex_ecdh_init{
q_c = Q_c
};
decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_REPLY),
- ?UINT32(Len1), Key:Len1/binary,
- ?UINT32(Len2), Q_s:Len2/big-signed-integer-unit:8,
- ?UINT32(Len3), Sig:Len3/binary>>) ->
+ ?DEC_BIN(Key,__1), ?DEC_MPINT(Q_s,__2), ?DEC_BIN(Sig,__3)>>) ->
#ssh_msg_kex_ecdh_reply{
- public_host_key = decode_host_key(Key),
+ public_host_key = public_key:ssh_decode(Key, ssh2_pubkey),
q_s = Q_s,
- h_sig = decode_sign(Sig)
+ h_sig = decode_signature(Sig)
};
-decode(<<?SSH_MSG_SERVICE_REQUEST, ?UINT32(Len0), Service:Len0/binary>>) ->
+decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) ->
#ssh_msg_service_request{
name = unicode:characters_to_list(Service)
};
-decode(<<?SSH_MSG_SERVICE_ACCEPT, ?UINT32(Len0), Service:Len0/binary>>) ->
+decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) ->
#ssh_msg_service_accept{
name = unicode:characters_to_list(Service)
};
-decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
- ?UINT32(Len0), Desc:Len0/binary, ?UINT32(Len1), Lang:Len1/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) ->
#ssh_msg_disconnect{
code = Code,
description = unicode:characters_to_list(Desc),
@@ -514,8 +494,7 @@ decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
};
%% Accept bad disconnects from ancient openssh clients that doesn't send language tag. Use english as a work-around.
-decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
- ?UINT32(Len0), Desc:Len0/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0)>>) ->
#ssh_msg_disconnect{
code = Code,
description = unicode:characters_to_list(Desc),
@@ -525,21 +504,25 @@ decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code),
decode(<<?SSH_MSG_NEWKEYS>>) ->
#ssh_msg_newkeys{};
-decode(<<?BYTE(?SSH_MSG_IGNORE), ?UINT32(Len), Data:Len/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_IGNORE), ?DEC_BIN(Data,__0)>>) ->
#ssh_msg_ignore{data = Data};
decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) ->
#ssh_msg_unimplemented{sequence = Seq};
-decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?UINT32(Len0), Msg:Len0/binary,
- ?UINT32(Len1), Lang:Len1/binary>>) ->
+decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?DEC_BIN(Msg,__0), ?DEC_BIN(Lang,__1)>>) ->
#ssh_msg_debug{always_display = erl_boolean(Bool),
message = Msg,
language = Lang}.
+%%%================================================================
+%%%
+%%% Helper functions
+%%%
+
decode_keyboard_interactive_prompts(<<>>, Acc) ->
lists:reverse(Acc);
-decode_keyboard_interactive_prompts(<<?UINT32(Len), Prompt:Len/binary, ?BYTE(Bool), Bin/binary>>,
+decode_keyboard_interactive_prompts(<<?DEC_BIN(Prompt,__0), ?BYTE(Bool), Bin/binary>>,
Acc) ->
decode_keyboard_interactive_prompts(Bin, [{Prompt, erl_boolean(Bool)} | Acc]).
@@ -555,69 +538,25 @@ decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) ->
%% See rfc 4253 7.1
X = 0,
list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
-decode_kex_init(<<?UINT32(Len), Data:Len/binary, Rest/binary>>, Acc, N) ->
+decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
Names = string:tokens(unicode:characters_to_list(Data), ","),
decode_kex_init(Rest, [Names | Acc], N -1).
+%%%================================================================
+%%%
+%%% Signature decode/encode
+%%%
-decode_sign(<<?UINT32(Len), _Alg:Len/binary, ?UINT32(_), Signature/binary>>) ->
+decode_signature(<<?DEC_BIN(_Alg,__0), ?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,
- modulus = N};
-
-decode_host_key(<<"ssh-dss">>,
- <<?UINT32(Len0), P:Len0/big-signed-integer-unit:8,
- ?UINT32(Len1), Q:Len1/big-signed-integer-unit:8,
- ?UINT32(Len2), G:Len2/big-signed-integer-unit:8,
- ?UINT32(Len3), Y:Len3/big-signed-integer-unit:8>>) ->
- {Y, #'Dss-Parms'{p = P,
- q = Q,
- 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]);
-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) ->
+encode_signature(#'RSAPublicKey'{}, Signature) ->
ssh_bits:encode(["ssh-rsa", Signature],[string, binary]);
-encode_sign(#'DSAPrivateKey'{}, Signature) ->
+encode_signature({_, #'Dss-Parms'{}}, Signature) ->
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]).
-
+encode_signature({#'ECPoint'{}, {namedCurve,OID}}, Signature) ->
+ CurveName = public_key:oid2ssh_curvename(OID),
+ ssh_bits:encode([<<"ecdsa-sha2-",CurveName/binary>>, Signature], [binary,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 a6438e69d4..8b65806dc6 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -44,6 +44,7 @@
handle_kexdh_reply/2,
handle_kex_ecdh_init/2,
handle_kex_ecdh_reply/2,
+ extract_public_key/1,
unpack/3, decompress/2, ssh_packet/2, pack/2, msg_data/1,
sign/3, verify/4]).
@@ -65,8 +66,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 to supported_algorithms/2
+%% default_algorithms(kex) -> % Example of how to disable an algorithm
+%% supported_algorithms(kex, ['ecdh-sha2-nistp521']);
default_algorithms(Alg) ->
supported_algorithms(Alg).
@@ -117,11 +118,11 @@ supported_algorithms(compression) ->
'zlib'
]).
-supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
- [{client2server,As1},{server2client,As2}] = supported_algorithms(Key),
- [{client2server,As1--BL1},{server2client,As2--BL2}];
-supported_algorithms(Key, BlackList) ->
- supported_algorithms(Key) -- BlackList.
+%% Dialyzer complains when not called...supported_algorithms(Key, [{client2server,BL1},{server2client,BL2}]) ->
+%% Dialyzer complains when not called... [{client2server,As1},{server2client,As2}] = supported_algorithms(Key),
+%% Dialyzer complains when not called... [{client2server,As1--BL1},{server2client,As2--BL2}];
+%% Dialyzer complains when not called...supported_algorithms(Key, BlackList) ->
+%% Dialyzer complains when not called... supported_algorithms(Key) -- BlackList.
select_crypto_supported(L) ->
Sup = [{ec_curve,crypto_supported_curves()} | crypto:supports()],
@@ -328,9 +329,7 @@ verify_algorithm(#alg{encrypt = undefined}) -> false;
verify_algorithm(#alg{decrypt = undefined}) -> false;
verify_algorithm(#alg{compress = undefined}) -> false;
verify_algorithm(#alg{decompress = undefined}) -> false;
-
-verify_algorithm(#alg{kex = Kex}) -> lists:member(Kex, supported_algorithms(kex));
-verify_algorithm(_) -> false.
+verify_algorithm(#alg{kex = Kex}) -> lists:member(Kex, supported_algorithms(kex)).
%%%----------------------------------------------------------------
%%%
@@ -380,13 +379,15 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
1=<E, E=<(P-1) ->
{Public, Private} = generate_key(dh, [P,G]),
K = compute_key(dh, E, Private, [P,G]),
- Key = get_host_key(Ssh0),
- H = kex_h(Ssh0, Key, E, Public, K),
- H_SIG = sign_host_key(Ssh0, Key, H),
- {SshPacket, Ssh1} = ssh_packet(#ssh_msg_kexdh_reply{public_host_key = Key,
- f = Public,
- h_sig = H_SIG
- }, Ssh0),
+ MyPrivHostKey = get_host_key(Ssh0),
+ MyPubHostKey = extract_public_key(MyPrivHostKey),
+ H = kex_h(Ssh0, MyPubHostKey, E, Public, K),
+ H_SIG = sign_host_key(Ssh0, MyPrivHostKey, H),
+ {SshPacket, Ssh1} =
+ ssh_packet(#ssh_msg_kexdh_reply{public_host_key = MyPubHostKey,
+ f = Public,
+ h_sig = H_SIG
+ }, Ssh0),
{ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}},
shared_secret = K,
exchanged_hash = H,
@@ -401,7 +402,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E},
})
end.
-handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey,
+handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = PeerPubHostKey,
f = F,
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}}} = Ssh0) ->
@@ -409,9 +410,9 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey,
if
1=<F, F=<(P-1)->
K = compute_key(dh, F, Private, [P,G]),
- H = kex_h(Ssh0, HostKey, Public, F, K),
+ H = kex_h(Ssh0, PeerPubHostKey, Public, F, K),
- case verify_host_key(Ssh0, HostKey, H, H_SIG) of
+ case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
{ok, SshPacket, Ssh#ssh{shared_secret = K,
@@ -480,11 +481,12 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
K = compute_key(dh, E, Private, [P,G]),
if
1<K, K<(P-1) ->
- HostKey = get_host_key(Ssh0),
- H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, E, Public, K),
- H_SIG = sign_host_key(Ssh0, HostKey, H),
+ MyPrivHostKey = get_host_key(Ssh0),
+ MyPubHostKey = extract_public_key(MyPrivHostKey),
+ H = kex_h(Ssh0, MyPubHostKey, Min, NBits, Max, P, G, E, Public, K),
+ H_SIG = sign_host_key(Ssh0, MyPrivHostKey, H),
{SshPacket, Ssh} =
- ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
+ ssh_packet(#ssh_msg_kex_dh_gex_reply{public_host_key = MyPubHostKey,
f = Public,
h_sig = H_SIG}, Ssh0),
{ok, SshPacket, Ssh#ssh{shared_secret = K,
@@ -508,7 +510,7 @@ handle_kex_dh_gex_init(#ssh_msg_kex_dh_gex_init{e = E},
})
end.
-handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
+handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = PeerPubHostKey,
f = F,
h_sig = H_SIG},
#ssh{keyex_key = {{Private, Public}, {G, P}},
@@ -520,9 +522,9 @@ handle_kex_dh_gex_reply(#ssh_msg_kex_dh_gex_reply{public_host_key = HostKey,
K = compute_key(dh, F, Private, [P,G]),
if
1<K, K<(P-1) ->
- H = kex_h(Ssh0, HostKey, Min, NBits, Max, P, G, Public, F, K),
+ H = kex_h(Ssh0, PeerPubHostKey, Min, NBits, Max, P, G, Public, F, K),
- case verify_host_key(Ssh0, HostKey, H, H_SIG) of
+ case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
{ok, SshPacket, Ssh#ssh{shared_secret = K,
@@ -565,11 +567,12 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
true ->
{MyPublic, MyPrivate} = generate_key(ecdh, Curve),
K = compute_key(ecdh, PeerPublic, MyPrivate, Curve),
- HostKey = get_host_key(Ssh0),
- H = kex_h(Ssh0, Curve, HostKey, PeerPublic, MyPublic, K),
- H_SIG = sign_host_key(Ssh0, HostKey, H),
+ MyPrivHostKey = get_host_key(Ssh0),
+ MyPubHostKey = extract_public_key(MyPrivHostKey),
+ H = kex_h(Ssh0, Curve, MyPubHostKey, PeerPublic, MyPublic, K),
+ H_SIG = sign_host_key(Ssh0, MyPrivHostKey, H),
{SshPacket, Ssh1} =
- ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = HostKey,
+ ssh_packet(#ssh_msg_kex_ecdh_reply{public_host_key = MyPubHostKey,
q_s = MyPublic,
h_sig = H_SIG},
Ssh0),
@@ -587,7 +590,7 @@ handle_kex_ecdh_init(#ssh_msg_kex_ecdh_init{q_c = PeerPublic},
})
end.
-handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = HostKey,
+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}} = Ssh0
@@ -596,8 +599,8 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = HostKey,
case ecdh_validate_public_key(PeerPublic, Curve) of
true ->
K = compute_key(ecdh, PeerPublic, MyPrivate, Curve),
- H = kex_h(Ssh0, Curve, HostKey, MyPublic, PeerPublic, K),
- case verify_host_key(Ssh0, HostKey, H, H_SIG) of
+ H = kex_h(Ssh0, Curve, PeerPubHostKey, MyPublic, PeerPublic, K),
+ case verify_host_key(Ssh0, PeerPubHostKey, H, H_SIG) of
ok ->
{SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0),
{ok, SshPacket, Ssh#ssh{shared_secret = K,
@@ -622,7 +625,61 @@ handle_kex_ecdh_reply(#ssh_msg_kex_ecdh_reply{public_host_key = HostKey,
end.
-ecdh_validate_public_key(_, _) -> true. % FIXME: Far too many false positives :)
+%%%----------------------------------------------------------------
+%%%
+%%% Standards for Efficient Cryptography Group, "Elliptic Curve Cryptography", SEC 1
+%%% Section 3.2.2.1
+%%%
+
+ecdh_validate_public_key(Key, Curve) ->
+ case key_size(Curve) of
+ undefined ->
+ false;
+
+ Sz ->
+ case dec_key(Key, Sz) of
+ {ok,Q} ->
+ case crypto:ec_curve(Curve) of
+ {{prime_field,P}, {A, B, _Seed},
+ _P0Bin, _OrderBin, _CoFactorBin} ->
+ on_curve(Q, bin2int(A), bin2int(B), bin2int(P))
+ end;
+
+ {error,compressed_not_implemented} -> % Be a bit generous...
+ true;
+
+ _Error ->
+ false
+ end
+ end.
+
+
+on_curve({X,Y}, A, B, P) when 0 =< X,X =< (P-1),
+ 0 =< Y,Y =< (P-1) ->
+ %% Section 3.2.2.1, point 2
+ (Y*Y) rem P == (X*X*X + A*X + B) rem P;
+on_curve(_, _, _, _) ->
+ false.
+
+
+bin2int(B) ->
+ Sz = erlang:bit_size(B),
+ <<I:Sz/big-unsigned-integer>> = B,
+ I.
+
+key_size(secp256r1) -> 256;
+key_size(secp384r1) -> 384;
+key_size(secp521r1) -> 528; % Round 521 up to closest 8-bits.
+key_size(_) -> undefined.
+
+
+dec_key(Key, NBits) ->
+ Size = 8 + 2*NBits,
+ case <<Key:Size>> of
+ <<4:8, X:NBits, Y:NBits>> -> {ok,{X,Y}};
+ <<4:8, _/binary>> -> {error,bad_format};
+ _ -> {error,compressed_not_implemented}
+ end.
%%%----------------------------------------------------------------
handle_new_keys(#ssh_msg_newkeys{}, Ssh0) ->
@@ -659,13 +716,20 @@ get_host_key(SSH) ->
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(#'ECPrivateKey'{parameters = {namedCurve,OID}}) -> sha(OID);
sign_host_key_sha(#'RSAPrivateKey'{}) -> sha;
sign_host_key_sha(#'DSAPrivateKey'{}) -> sha.
+extract_public_key(#'RSAPrivateKey'{modulus = N, publicExponent = E}) ->
+ #'RSAPublicKey'{modulus = N, publicExponent = E};
+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}}.
+
+
verify_host_key(SSH, PublicKey, Digest, Signature) ->
case verify(Digest, host_key_sha(PublicKey), Signature, PublicKey) of
false ->
@@ -674,14 +738,16 @@ verify_host_key(SSH, PublicKey, Digest, Signature) ->
known_host_key(SSH, PublicKey, public_algo(PublicKey))
end.
-host_key_sha(#'RSAPublicKey'{}) -> sha;
-host_key_sha({_, #'Dss-Parms'{}}) -> sha;
-host_key_sha({#'ECPoint'{},Id}) -> sha(list_to_atom(binary_to_list(Id))).
+host_key_sha(#'RSAPublicKey'{}) -> sha;
+host_key_sha({_, #'Dss-Parms'{}}) -> sha;
+host_key_sha({#'ECPoint'{},{namedCurve,OID}}) -> sha(OID).
public_algo(#'RSAPublicKey'{}) -> 'ssh-rsa';
public_algo({_, #'Dss-Parms'{}}) -> 'ssh-dss';
-public_algo({#'ECPoint'{},Id}) -> list_to_atom("ecdsa-sha2-" ++ binary_to_list(Id)).
+public_algo({#'ECPoint'{},{namedCurve,OID}}) ->
+ Curve = public_key:oid2ssh_curvename(OID),
+ list_to_atom("ecdsa-sha2-" ++ binary_to_list(Curve)).
accepted_host(Ssh, PeerName, Opts) ->
@@ -933,17 +999,12 @@ 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,
+verify(PlainText, Hash, Sig, {#'ECPoint'{},_} = Key) ->
<<?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});
+ public_key:verify(PlainText, Hash, DerEncodedSig, Key);
verify(PlainText, Hash, Sig, Key) ->
public_key:verify(PlainText, Hash, Sig, Key).
@@ -1336,52 +1397,58 @@ hash(K, H, Ki, N, HASH) ->
hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HASH).
kex_h(SSH, Key, E, F, K) ->
+ KeyBin = public_key:ssh_encode(Key, ssh2_pubkey),
L = ssh_bits:encode([SSH#ssh.c_version, SSH#ssh.s_version,
SSH#ssh.c_keyinit, SSH#ssh.s_keyinit,
- ssh_message:encode_host_key(Key), E,F,K],
+ KeyBin, E,F,K],
[string,string,binary,binary,binary,
mpint,mpint,mpint]),
crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L).
%% crypto:hash(sha,L).
kex_h(SSH, Curve, Key, Q_c, Q_s, K) ->
+ KeyBin = public_key:ssh_encode(Key, ssh2_pubkey),
L = ssh_bits:encode([SSH#ssh.c_version, SSH#ssh.s_version,
SSH#ssh.c_keyinit, SSH#ssh.s_keyinit,
- ssh_message:encode_host_key(Key), Q_c, Q_s, K],
+ KeyBin, Q_c, Q_s, K],
[string,string,binary,binary,binary,
mpint,mpint,mpint]),
crypto:hash(sha(Curve), L).
kex_h(SSH, Key, Min, NBits, Max, Prime, Gen, E, F, K) ->
L = if Min==-1; Max==-1 ->
+ KeyBin = public_key:ssh_encode(Key, ssh2_pubkey),
Ts = [string,string,binary,binary,binary,
uint32,
mpint,mpint,mpint,mpint,mpint],
ssh_bits:encode([SSH#ssh.c_version,SSH#ssh.s_version,
SSH#ssh.c_keyinit,SSH#ssh.s_keyinit,
- ssh_message:encode_host_key(Key), NBits, Prime, Gen, E,F,K],
+ KeyBin, NBits, Prime, Gen, E,F,K],
Ts);
true ->
+ KeyBin = public_key:ssh_encode(Key, ssh2_pubkey),
Ts = [string,string,binary,binary,binary,
uint32,uint32,uint32,
mpint,mpint,mpint,mpint,mpint],
ssh_bits:encode([SSH#ssh.c_version,SSH#ssh.s_version,
SSH#ssh.c_keyinit,SSH#ssh.s_keyinit,
- ssh_message:encode_host_key(Key), Min, NBits, Max,
+ KeyBin, Min, NBits, Max,
Prime, Gen, E,F,K], Ts)
end,
crypto:hash(sha((SSH#ssh.algorithms)#alg.kex), L).
-sha('nistp256') -> sha256;
-sha('secp256r1')-> sha256;
-sha('nistp384') -> sha384;
-sha('secp384r1')-> sha384;
-sha('nistp521') -> sha512;
-sha('secp521r1')-> sha512;
+
+sha(secp256r1) -> sha256;
+sha(secp384r1) -> sha384;
+sha(secp521r1) -> sha512;
sha('diffie-hellman-group1-sha1') -> sha;
sha('diffie-hellman-group14-sha1') -> sha;
sha('diffie-hellman-group-exchange-sha1') -> sha;
-sha('diffie-hellman-group-exchange-sha256') -> sha256.
+sha('diffie-hellman-group-exchange-sha256') -> sha256;
+sha(?'secp256r1') -> sha(secp256r1);
+sha(?'secp384r1') -> sha(secp384r1);
+sha(?'secp521r1') -> sha(secp521r1).
+
mac_key_size('hmac-sha1') -> 20*8;
mac_key_size('hmac-sha1-96') -> 20*8;
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 87eaeec1bc..a269192785 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -403,18 +403,13 @@ 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) ->
+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,
+ parameters = Param = {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([{ {PKey,Param}, [{comment, "Test"}]}], UserDir).
setup_auth_keys(Keys, Dir) ->
AuthKeys = public_key:ssh_encode(Keys, auth_keys),
diff --git a/lib/ssh/test/ssh_trpt_test_lib.erl b/lib/ssh/test/ssh_trpt_test_lib.erl
index caf9bac3b6..772e50df87 100644
--- a/lib/ssh/test/ssh_trpt_test_lib.erl
+++ b/lib/ssh/test/ssh_trpt_test_lib.erl
@@ -743,7 +743,7 @@ print_traces(S) ->
[case Len-length(Acc)-1 of
0 ->
io_lib:format(Fmt,Args);
- N ->
+ _N ->
io_lib:format(lists:concat(['~p --------~n',Fmt]),
[Len-length(Acc)-1|Args])
end | Acc]