diff options
author | Ingela Anderton Andin <ingela@erlang.org> | 2012-01-24 17:55:54 +0100 |
---|---|---|
committer | Ingela Anderton Andin <ingela@erlang.org> | 2012-02-10 15:24:38 +0100 |
commit | 2916e7f2aa523411717c3ed4e275ab96cb03b6ee (patch) | |
tree | 1a41a632fbd21b415579a4c9f66da4157b5e6d5e /lib/ssh/src | |
parent | c3fb91d203412c16d008b4c36fb13c0d776d8c46 (diff) | |
download | otp-2916e7f2aa523411717c3ed4e275ab96cb03b6ee.tar.gz otp-2916e7f2aa523411717c3ed4e275ab96cb03b6ee.tar.bz2 otp-2916e7f2aa523411717c3ed4e275ab96cb03b6ee.zip |
Use the public_key application for all public key handling
Also improved test suites to avoid copying of users keys to
test server directories as this is a security liability
Diffstat (limited to 'lib/ssh/src')
-rw-r--r-- | lib/ssh/src/Makefile | 6 | ||||
-rw-r--r-- | lib/ssh/src/ssh_auth.erl | 87 | ||||
-rw-r--r-- | lib/ssh/src/ssh_dsa.erl | 42 | ||||
-rw-r--r-- | lib/ssh/src/ssh_file.erl | 600 | ||||
-rw-r--r-- | lib/ssh/src/ssh_rsa.erl | 233 | ||||
-rw-r--r-- | lib/ssh/src/ssh_transport.erl | 105 |
6 files changed, 332 insertions, 741 deletions
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index da31d87369..a49f207564 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -101,8 +101,8 @@ INTERNAL_HRL_FILES = $(ASN_HRLS) ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa$(EBIN) - +ERL_COMPILE_FLAGS += -pa$(EBIN)\ + -pz $(ERL_TOP)/lib/public_key/ebin # ---------------------------------------------------- # Targets # ---------------------------------------------------- diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 9dbd95886e..a39c664c45 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,11 +21,13 @@ -module(ssh_auth). --include("ssh.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include("ssh.hrl"). -include("ssh_auth.hrl"). -include("ssh_transport.hrl"). + -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, @@ -40,26 +42,29 @@ publickey_msg([Cb, #ssh{user = User, session_id = SessionId, service = Service, opts = Opts} = Ssh]) -> + + Alg = algorithm(Cb), + Hash = sha, %% Maybe option?! ssh_bits:install_messages(userauth_pk_messages()), - Alg = Cb:alg_name(), + case ssh_file:private_identity_key(Alg, Opts) of - {ok, PrivKey} -> - PubKeyBlob = ssh_file:encode_public_key(PrivKey), + {ok, Key} -> + PubKeyBlob = ssh_file:encode_public_key(Key), SigData = build_sig_data(SessionId, - User, Service, Alg, PubKeyBlob), - Sig = Cb:sign(PrivKey, SigData), - SigBlob = list_to_binary([?string(Alg), ?binary(Sig)]), + User, Service, Key, PubKeyBlob), + Sig = sign(SigData, Hash, Key), + SigBlob = list_to_binary([?string(algorithm(Key)), ?binary(Sig)]), ssh_transport:ssh_packet( #ssh_msg_userauth_request{user = User, service = Service, method = "publickey", data = [?TRUE, - ?string(Alg), + ?string(algorithm(Key)), ?binary(PubKeyBlob), ?binary(SigBlob)]}, Ssh); - _Error -> - not_ok + _Error -> + not_ok end. password_msg([#ssh{opts = Opts, io_cb = IoCb, @@ -192,12 +197,12 @@ handle_userauth_request(#ssh_msg_userauth_request{user = User, ?TRUE -> case verify_sig(SessionId, User, "ssh-connection", Alg, KeyBlob, SigWLen, Opts) of - ok -> + true -> {authorized, User, ssh_transport:ssh_packet( #ssh_msg_userauth_success{}, Ssh)}; - {error, Reason} -> - {not_authorized, {User, {error, Reason}}, + false -> + {not_authorized, {User, {error, "Invalid signature"}}, ssh_transport:ssh_packet(#ssh_msg_userauth_failure{ authentications="publickey,password", partial_success = false}, Ssh)} @@ -312,35 +317,65 @@ get_password_option(Opts, User) -> end. verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) -> - case ssh_file:lookup_user_key(User, Alg, Opts) of + {ok, Key} = ssh_file:decode_public_key_v2(KeyBlob, Alg), + case ssh_file:lookup_user_key(Key, User, Alg, Opts) of {ok, OurKey} -> - {ok, Key} = ssh_file:decode_public_key_v2(KeyBlob, Alg), case OurKey of Key -> - NewSig = build_sig_data(SessionId, - User, Service, Alg, KeyBlob), + PlainText = build_sig_data(SessionId, + User, Service, Key, KeyBlob), <<?UINT32(AlgSigLen), AlgSig:AlgSigLen/binary>> = SigWLen, <<?UINT32(AlgLen), _Alg:AlgLen/binary, - ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, - M = alg_to_module(Alg), - M:verify(OurKey, NewSig, Sig); + ?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig, + verify(PlainText, sha, Sig, Key); _ -> {error, key_unacceptable} end; Error -> Error end. -build_sig_data(SessionId, User, Service, Alg, KeyBlob) -> +build_sig_data(SessionId, User, Service, Key, KeyBlob) -> Sig = [?binary(SessionId), ?SSH_MSG_USERAUTH_REQUEST, ?string(User), ?string(Service), ?binary(<<"publickey">>), ?TRUE, - ?string(Alg), + ?string(algorithm(Key)), ?binary(KeyBlob)], list_to_binary(Sig). +algorithm(ssh_rsa) -> + "ssh-rsa"; +algorithm(ssh_dsa) -> + "ssh-dss"; +algorithm(#'RSAPrivateKey'{}) -> + "ssh-rsa"; +algorithm(#'DSAPrivateKey'{}) -> + "ssh-dss"; +algorithm({_, #'Dss-Parms'{}}) -> + "ssh-dss"; +algorithm(#'RSAPublicKey'{}) -> + "ssh-rsa". + +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) -> + public_key:sign(SigData, Hash, Key). +%% sign(SigData, _, #'DSAPrivateKey'{} = Key) -> +%% ssh_dsa:sign(Key, SigData). + +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, Key) -> + public_key:verify(PlainText, sha, Sig, Key). +%% verify(PlainText, _Hash, Sig, {_, #'Dss-Parms'{}} = Key) -> +%% ssh_dsa:verify(Key, PlainText, Sig). + decode_keyboard_interactive_prompts(NumPrompts, Data) -> Types = lists:append(lists:duplicate(NumPrompts, [string, boolean])), pairwise_tuplify(ssh_bits:decode(Data, Types)). @@ -412,12 +447,8 @@ userauth_pk_messages() -> binary]} % key blob ]. -alg_to_module("ssh-dss") -> - ssh_dsa; -alg_to_module("ssh-rsa") -> - ssh_rsa. - other_cb(ssh_rsa) -> ssh_dsa; other_cb(ssh_dsa) -> ssh_rsa. + diff --git a/lib/ssh/src/ssh_dsa.erl b/lib/ssh/src/ssh_dsa.erl index cb2632beac..6650b8b99b 100644 --- a/lib/ssh/src/ssh_dsa.erl +++ b/lib/ssh/src/ssh_dsa.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,28 +23,14 @@ -module(ssh_dsa). --export([verify/3]). +-export([verify/3, verify/4]). -export([sign/2]). -export([alg_name/0]). -include("ssh.hrl"). +-include_lib("public_key/include/public_key.hrl"). -%% start() -> -%% crypto:start(). - -%% sign_file(File, Opts) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:private_host_dsa_key(user, Opts), -%% sign(Key, Bin). - -%% verify_file(File, Sig) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:public_host_key(user, dsa), -%% verify(Key, Bin, Sig). - -sign(_Private=#ssh_key { private={P,Q,G,X} },Mb) -> +sign(_Private= #'DSAPrivateKey'{p = P, q = Q, g = G, x = X},Mb) -> K = ssh_bits:irandom(160) rem Q, R = ssh_math:ipow(G, K, P) rem Q, Ki = ssh_math:invert(K, Q), @@ -52,28 +38,18 @@ sign(_Private=#ssh_key { private={P,Q,G,X} },Mb) -> S = (Ki * (M + X*R)) rem Q, <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>. - -%% the paramiko client sends a bad sig sometimes, -%% instead of crashing, we nicely return error, the -%% typcally manifests itself as Sb being 39 bytes -%% instead of 40. +verify(PlainText, sha, Sig, {Y, {_, P, Q, G}}) -> + verify(#ssh_key{type = dsa, + public = {P,Q,G,Y}}, PlainText, Sig). verify(Public, Mb, Sb) -> case catch xverify(Public, Mb, Sb) of {'EXIT', _Reason} -> - %store({Public, Mb, Sb, _Reason}), - {error, inconsistent_key}; + false; ok -> - %store({Public, Mb, Sb, ok}) - ok + true end. -%% store(Term) -> -%% {ok, Fd} = file:open("/tmp/dsa", [append]), -%% io:format(Fd, "~p~n~n~n", [Term]), -%% file:close(Fd). - - xverify(_Public=#ssh_key { public={P,Q,G,Y} },Mb,Sb) -> <<R0:160/big-unsigned-integer, S0:160/big-unsigned-integer>> = Sb, ?ssh_assert(R0 >= 0 andalso R0 < Q andalso diff --git a/lib/ssh/src/ssh_file.erl b/lib/ssh/src/ssh_file.erl index 12180f56bb..64dc286b92 100644 --- a/lib/ssh/src/ssh_file.erl +++ b/lib/ssh/src/ssh_file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,21 +23,19 @@ -module(ssh_file). --include("ssh.hrl"). --include("PKCS-1.hrl"). --include("DSS.hrl"). - +-include_lib("public_key/include/public_key.hrl"). -include_lib("kernel/include/file.hrl"). +-include("ssh.hrl"). + -export([public_host_dsa_key/2,private_host_dsa_key/2, public_host_rsa_key/2,private_host_rsa_key/2, public_host_key/2,private_host_key/2, - lookup_host_key/3, add_host_key/3, % del_host_key/2, - lookup_user_key/3, ssh_dir/2, file_name/3]). + lookup_host_key/3, add_host_key/3, + lookup_user_key/4, ssh_dir/2, file_name/3]). -export([private_identity_key/2, public_identity_key/2]). -%% identity_keys/2]). -export([encode_public_key/1, decode_public_key_v2/2]). @@ -48,38 +46,149 @@ -define(PERM_700, 8#700). -define(PERM_644, 8#644). + %% API public_host_dsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_dsa_key.pub", Opts), - read_public_key_v2(File, "ssh-dss"). + decode(File, public_key). private_host_dsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_dsa_key", Opts), - read_private_key_v2(File, "ssh-dss"). + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). public_host_rsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_rsa_key.pub", Opts), - read_public_key_v2(File, "ssh-rsa"). + decode(File, public_key). private_host_rsa_key(Type, Opts) -> File = file_name(Type, "ssh_host_rsa_key", Opts), - read_private_key_v2(File, "ssh-rsa"). + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). public_host_key(Type, Opts) -> File = file_name(Type, "ssh_host_key", Opts), - case read_private_key_v1(File,public) of - {error, enoent} -> - read_public_key_v1(File++".pub"); - Result -> - Result + decode(File, public_key). + +private_host_key(Type, Opts) -> + File = file_name(Type, "ssh_host_key", Opts), + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). + +private_identity_key(Alg, Opts) -> + File = file_name(user, identity_key_filename(Alg), Opts), + Password = proplists:get_value(password, Opts, ignore), + decode(File, Password). + +public_identity_key(Alg, Opts) -> + File = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts), + decode(File, public_key). + +encode_public_key(#'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]). + +decode(File, Password) -> + try + {ok, decode_ssh_file(read_ssh_file(File), Password)} + catch + throw:Reason -> + {error, Reason}; + error:Reason -> + {error, Reason} + end. + +read_ssh_file(File) -> + {ok, Bin} = file:read_file(File), + Bin. + +%% Public key +decode_ssh_file(SshBin, public_key) -> + public_key:ssh_decode(SshBin, public_key); + +%% Private Key +decode_ssh_file(Pem, Password) -> + case public_key:pem_decode(Pem) of + [{_, _, not_encrypted} = Entry] -> + public_key:pem_entry_decode(Entry); + [Entry] when Password =/= ignore -> + public_key:pem_entry_decode(Entry, Password); + _ -> + throw("No pass phrase provided for private key file") end. + + +%% lookup_host_key +%% return {ok, Key(s)} or {error, not_found} +%% + +lookup_host_key(Host, Alg, Opts) -> + Host1 = replace_localhost(Host), + do_lookup_host_key(Host1, Alg, Opts). -private_host_key(Type, Opts) -> - File = file_name(Type, "ssh_host_key", Opts), - read_private_key_v1(File,private). +add_host_key(Host, Key, Opts) -> + Host1 = add_ip(replace_localhost(Host)), + KnownHosts = file_name(user, "known_hosts", Opts), + case file:open(KnownHosts, [write,append]) of + {ok, Fd} -> + ok = file:change_mode(KnownHosts, ?PERM_644), + Res = add_key_fd(Fd, Host1, Key), + file:close(Fd), + Res; + Error -> + Error + end. + +lookup_user_key(Key, User, Alg, Opts) -> + SshDir = ssh_dir({remoteuser,User}, Opts), + case lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys", Opts) of + {ok, Key} -> + {ok, Key}; + _ -> + lookup_user_key_f(Key, User, SshDir, Alg, "authorized_keys2", Opts) + end. + + +%% +%% Utils +%% +%% server use this to find individual keys for +%% an individual user when user tries to login +%% with publickey +ssh_dir({remoteuser, User}, Opts) -> + case proplists:get_value(user_dir_fun, Opts) of + undefined -> + case proplists:get_value(user_dir, Opts) of + undefined -> + default_user_dir(); + Dir -> + Dir + end; + FUN -> + FUN(User) + end; +%% client use this to find client ssh keys +ssh_dir(user, Opts) -> + case proplists:get_value(user_dir, Opts, false) of + false -> default_user_dir(); + D -> D + end; + +%% server use this to find server host keys +ssh_dir(system, Opts) -> + proplists:get_value(system_dir, Opts, "/etc/ssh"). + + +file_name(Type, Name, Opts) -> + FN = filename:join(ssh_dir(Type, Opts), Name), + %%?dbg(?DBG_PATHS, "file_name: ~p\n", [FN]), + FN. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% in: "host" out: "host,1.2.3.4. add_ip(Host) -> @@ -98,449 +207,112 @@ replace_localhost("localhost") -> replace_localhost(Host) -> Host. -%% lookup_host_key -%% return {ok, Key(s)} or {error, not_found} -%% - -lookup_host_key(Host, Alg, Opts) -> - Host1 = replace_localhost(Host), - do_lookup_host_key(Host1, Alg, Opts). - do_lookup_host_key(Host, Alg, Opts) -> - case file:open(file_name(user, "known_hosts", Opts), [read]) of + case file:open(file_name(user, "known_hosts", Opts), [read, binary]) of {ok, Fd} -> Res = lookup_host_key_fd(Fd, Host, Alg), file:close(Fd), - Res; + {ok, Res}; {error, enoent} -> {error, not_found}; Error -> Error end. -add_host_key(Host, Key, Opts) -> - Host1 = add_ip(replace_localhost(Host)), - KnownHosts = file_name(user, "known_hosts", Opts), - case file:open(KnownHosts, [write,append]) of - {ok, Fd} -> - ok = file:change_mode(KnownHosts, ?PERM_644), - Res = add_key_fd(Fd, Host1, Key), - file:close(Fd), - Res; - Error -> - Error - end. - -%% del_host_key(Host, Opts) -> -%% Host1 = replace_localhost(Host), -%% case file:open(file_name(user, "known_hosts", Opts),[write,read]) of -%% {ok, Fd} -> -%% Res = del_key_fd(Fd, Host1), -%% file:close(Fd), -%% Res; -%% Error -> -%% Error -%% end. - identity_key_filename("ssh-dss") -> "id_dsa"; identity_key_filename("ssh-rsa") -> "id_rsa". -private_identity_key(Alg, Opts) -> - Path = file_name(user, identity_key_filename(Alg), Opts), - read_private_key_v2(Path, Alg). - -public_identity_key(Alg, Opts) -> - Path = file_name(user, identity_key_filename(Alg) ++ ".pub", Opts), - read_public_key_v2(Path, Alg). - - -read_public_key_v2(File, Type) -> - case file:read_file(File) of - {ok,Bin} -> - List = binary_to_list(Bin), - case lists:prefix(Type, List) of - true -> - List1 = lists:nthtail(length(Type), List), - K_S = ssh_bits:b64_decode(List1), - decode_public_key_v2(K_S, Type); - false -> - {error, bad_format} - end; - Error -> - Error - end. - decode_public_key_v2(K_S, "ssh-rsa") -> case ssh_bits:decode(K_S,[string,mpint,mpint]) of ["ssh-rsa", E, N] -> - {ok, #ssh_key { type = rsa, - public = {N,E}, - comment=""}}; + {ok, #'RSAPublicKey'{publicExponent = E, modulus = N}}; _ -> {error, bad_format} end; -decode_public_key_v2(K_S, "ssh-dss") -> - case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of - ["ssh-dss",P,Q,G,Y] -> - {ok,#ssh_key { type = dsa, - public = {P,Q,G,Y} - }}; - _A -> - {error, bad_format} - end; +decode_public_key_v2(K_S, "ssh-dss") -> + case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of + ["ssh-dss",P,Q,G,Y] -> + {ok, {Y, #'Dss-Parms'{p = P, q = Q, g = G}}}; + _A -> + {error, bad_format} + end; decode_public_key_v2(_, _) -> {error, bad_format}. - -read_public_key_v1(File) -> - case file:read_file(File) of - {ok,Bin} -> - List = binary_to_list(Bin), - case io_lib:fread("~d ~d ~d ~s", List) of - {ok,[_Sz,E,N,Comment],_} -> - {ok,#ssh_key { type = rsa, - public ={N,E}, - comment = Comment }}; - _Error -> - {error, bad_format} - end; - Error -> - Error - end. - -%% pem_type("ssh-dss") -> "DSA"; -%% pem_type("ssh-rsa") -> "RSA". - -read_private_key_v2(File, Type) -> - case file:read_file(File) of - {ok, PemBin} -> - case catch (public_key:pem_decode(PemBin)) of - [{_, Bin, not_encrypted}] -> - decode_private_key_v2(Bin, Type); - Error -> %% Note we do not handle password encrypted keys at the moment - {error, Error} - end; - {error, Reason} -> - {error, Reason} - end. -%% case file:read_file(File) of -%% {ok,Bin} -> -%% case read_pem(binary_to_list(Bin), pem_type(Type)) of -%% {ok,Bin1} -> -%% decode_private_key_v2(Bin1, Type); -%% Error -> -%% Error -%% end; -%% Error -> -%% Error -%% end. - -decode_private_key_v2(Private,"ssh-rsa") -> - case 'PKCS-1':decode( 'RSAPrivateKey', Private) of - {ok,RSA} -> %% FIXME Check for two-prime version - {ok, #ssh_key { type = rsa, - public = {RSA#'RSAPrivateKey'.modulus, - RSA#'RSAPrivateKey'.publicExponent}, - private = {RSA#'RSAPrivateKey'.modulus, - RSA#'RSAPrivateKey'.privateExponent} - }}; - Error -> - Error - end; -decode_private_key_v2(Private, "ssh-dss") -> - case 'DSS':decode('DSAPrivateKey', Private) of - {ok,DSA} -> %% FIXME Check for two-prime version - {ok, #ssh_key { type = dsa, - public = {DSA#'DSAPrivateKey'.p, - DSA#'DSAPrivateKey'.q, - DSA#'DSAPrivateKey'.g, - DSA#'DSAPrivateKey'.y}, - private= {DSA#'DSAPrivateKey'.p, - DSA#'DSAPrivateKey'.q, - DSA#'DSAPrivateKey'.g, - DSA#'DSAPrivateKey'.x} - }}; - _ -> - {error,bad_format} - end. - -%% SSH1 private key format -%% <<"SSH PRIVATE KEY FILE FORMATE 1.1\n" 0:8 -%% CipherNum:8, Reserved:32, -%% NSz/uint32, N/bignum, E/bignum, Comment/string, -%% -%% [ R0:8 R1:8 R0:8 R1:8, D/bignum, IQMP/bignum, Q/bignum, P/bignum, Pad(8)]>> -%% -%% where [ ] is encrypted using des3 (ssh1 version) and -%% a posssibly empty pass phrase using md5(passphase) as key -%% - -read_private_key_v1(File, Type) -> - case file:read_file(File) of - {ok,<<"SSH PRIVATE KEY FILE FORMAT 1.1\n",0, - CipherNum,_Resereved:32,Bin/binary>>} -> - decode_private_key_v1(Bin, CipherNum,Type); - {ok,_} -> - {error, bad_format}; - Error -> - Error - end. - -decode_private_key_v1(Bin, CipherNum, Type) -> - case ssh_bits:decode(Bin,0,[uint32, bignum, bignum, string]) of - {Offset,[_NSz,N,E,Comment]} -> - if Type == public -> - {ok,#ssh_key { type=rsa, - public={N,E}, - comment=Comment}}; - Type == private -> - <<_:Offset/binary, Encrypted/binary>> = Bin, - case ssh_bits:decode(decrypt1(Encrypted, CipherNum),0, - [uint32, bignum, bignum, - bignum, bignum,{pad,8}]) of - {_,[_,D,IQMP,Q,P]} -> - {ok,#ssh_key { type=rsa, - public={N,E}, - private={D,IQMP,Q,P}, - comment=Comment}}; - _ -> - {error,bad_format} - end - end; - _ -> - {error,bad_format} - end. - - -decrypt1(Bin, CipherNum) -> - decrypt1(Bin, CipherNum,""). - -decrypt1(Bin, CipherNum, Phrase) -> - if CipherNum == ?SSH_CIPHER_NONE; Phrase == "" -> - Bin; - CipherNum == ?SSH_CIPHER_3DES -> - <<K1:8/binary, K2:8/binary>> = erlang:md5(Phrase), - K3 = K1, - IV = <<0,0,0,0,0,0,0,0>>, - Bin1 = crypto:des_cbc_decrypt(K3,IV,Bin), - Bin2 = crypto:des_cbc_encrypt(K2,IV,Bin1), - crypto:des_cbc_decrypt(K1,IV,Bin2) - end. - -%% encrypt1(Bin, CipherNum) -> -%% encrypt1(Bin, CipherNum,""). - -%% encrypt1(Bin, CipherNum, Phrase) -> -%% if CipherNum == ?SSH_CIPHER_NONE; Phrase == "" -> -%% Bin; -%% CipherNum == ?SSH_CIPHER_3DES -> -%% <<K1:8/binary, K2:8/binary>> = erlang:md5(Phrase), -%% K3 = K1, -%% IV = <<0,0,0,0,0,0,0,0>>, -%% Bin1 = crypto:des_cbc_encrypt(K1,IV,Bin), -%% Bin2 = crypto:des_cbc_decrypt(K2,IV,Bin1), -%% crypto:des_cbc_encrypt(K3,IV,Bin2) -%% end. - -lookup_host_key_fd(Fd, Host, Alg) -> +lookup_host_key_fd(Fd, Host, KeyType) -> case io:get_line(Fd, '') of eof -> {error, not_found}; Line -> - case string:tokens(Line, " ") of - [HostList, Alg, KeyData] -> -%% io:format(" ~p lookup_host_key_fd: HostList ~p Alg ~p KeyData ~p\n", -%% [Host, HostList, Alg, KeyData]), - case lists:member(Host, string:tokens(HostList, ",")) of - true -> - decode_public_key_v2(ssh_bits:b64_decode(KeyData), Alg); - false -> - lookup_host_key_fd(Fd, Host, Alg) - end; - _ -> - lookup_host_key_fd(Fd, Host, Alg) + case public_key:ssh_decode(Line, known_hosts) of + [{Key, Attributes}] -> + handle_host(Fd, Host, proplists:get_value(hostnames, Attributes), Key, KeyType); + [] -> + lookup_host_key_fd(Fd, Host, KeyType) end end. - - -%% del_key_fd(Fd, Host) -> -%% del_key_fd(Fd, Host, 0, 0). - -%% del_key_fd(Fd, Host, ReadPos0, WritePos0) -> -%% case io:get_line(Fd, '') of -%% eof -> -%% if ReadPos0 == WritePos0 -> -%% ok; -%% true -> -%% file:truncate(Fd) -%% end; -%% Line -> -%% {ok,ReadPos1} = file:position(Fd, cur), -%% case string:tokens(Line, " ") of -%% [HostList, _Type, _KeyData] -> -%% case lists:member(Host, string:tokens(HostList, ",")) of -%% true -> -%% del_key_fd(Fd, Host, ReadPos1, WritePos0); -%% false -> -%% if ReadPos0 == WritePos0 -> -%% del_key_fd(Fd, Host, ReadPos1, ReadPos1); -%% true -> -%% file:position(Fd, WritePos0), -%% file:write(Fd, Line), -%% {ok,WritePos1} = file:position(Fd,cur), -%% del_key_fd(Fd, Host, ReadPos1, WritePos1) -%% end -%% end; -%% _ -> -%% if ReadPos0 == WritePos0 -> -%% del_key_fd(Fd, Host, ReadPos1, ReadPos1); -%% true -> -%% file:position(Fd, WritePos0), -%% file:write(Fd, Line), -%% {ok,WritePos1} = file:position(Fd,cur), -%% del_key_fd(Fd, Host, ReadPos1, WritePos1) -%% end -%% end -%% end. - - -add_key_fd(Fd, Host, Key) -> - case Key#ssh_key.type of - rsa -> - {N,E} = Key#ssh_key.public, - DK = ssh_bits:b64_encode( - ssh_bits:encode(["ssh-rsa",E,N], - [string,mpint,mpint])), - file:write(Fd, [Host, " ssh-rsa ", DK, "\n"]); - dsa -> - {P,Q,G,Y} = Key#ssh_key.public, - DK = ssh_bits:b64_encode( - ssh_bits:encode(["ssh-dss",P,Q,G,Y], - [string,mpint,mpint,mpint,mpint])), - file:write(Fd, [Host, " ssh-dss ", DK, "\n"]) +handle_host(Fd, Host, HostList, Key, KeyType) -> + Host1 = host_name(Host), + case lists:member(Host1, HostList) and key_match(Key, KeyType) of + true -> + Key; + false -> + lookup_host_key_fd(Fd, Host, KeyType) end. +host_name(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +host_name(List) -> + List. -%% read_pem(Cs, Type) -> -%% case read_line(Cs) of -%% {"-----BEGIN "++Rest,Cs1} -> -%% case string:tokens(Rest, " ") of -%% [Type, "PRIVATE", "KEY-----"] -> -%% read_pem64(Cs1, [], Type); -%% _ -> -%% {error, bad_format} -%% end; -%% {"",Cs1} when Cs1 =/= "" -> -%% read_pem(Cs1,Type); -%% {_,""} -> -%% {error, bad_format} -%% end. - -%% read_pem64(Cs, Acc, Type) -> -%% case read_line(Cs) of -%% {"-----END "++Rest,_Cs1} -> -%% case string:tokens(Rest, " ") of -%% [Type, "PRIVATE", "KEY-----"] -> -%% {ok,ssh_bits:b64_decode(append(reverse(Acc)))}; -%% Toks -> -%% error_logger:format("ssh: TOKENS=~p\n", [Toks]), -%% {error, bad_format} -%% end; -%% {B64, Cs1} when Cs1 =/= "" -> -%% read_pem64(Cs1, [B64|Acc], Type); -%% _What -> -%% {error, bad_format} -%% end. - - -%% read_line(Cs) -> read_line(Cs,[]). -%% read_line([$\r,$\n|T], Acc) -> {reverse(Acc), T}; -%% read_line([$\n|T], Acc) -> {reverse(Acc), T}; -%% read_line([C|T], Acc) -> read_line(T,[C|Acc]); -%% read_line([], Acc) -> {reverse(Acc),[]}. - -lookup_user_key(User, Alg, Opts) -> - SshDir = ssh_dir({remoteuser,User}, Opts), - case lookup_user_key_f(User, SshDir, Alg, "authorized_keys", Opts) of - {ok, Key} -> - {ok, Key}; - _ -> - lookup_user_key_f(User, SshDir, Alg, "authorized_keys2", Opts) - end. +key_match(#'RSAPublicKey'{}, "ssh-rsa") -> + true; +key_match({_, #'Dss-Parms'{}}, "ssh-dss") -> + true; +key_match(_, _) -> + false. + +add_key_fd(Fd, Host,Key) -> + SshBin = public_key:ssh_encode([{Key, [{hostnames, [Host]}]}], known_hosts), + file:write(Fd, SshBin). -lookup_user_key_f(_User, [], _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, [], _Alg, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_User, nouserdir, _Alg, _F, _Opts) -> +lookup_user_key_f(_, _User, nouserdir, _Alg, _F, _Opts) -> {error, nouserdir}; -lookup_user_key_f(_User, Dir, Alg, F, _Opts) -> +lookup_user_key_f(Key, _User, Dir, _Alg, F, _Opts) -> FileName = filename:join(Dir, F), - case file:open(FileName, [read]) of + case file:open(FileName, [read, binary]) of {ok, Fd} -> - Res = lookup_user_key_fd(Fd, Alg), + Res = lookup_user_key_fd(Fd, Key), file:close(Fd), Res; {error, Reason} -> {error, {{openerr, Reason}, {file, FileName}}} end. -lookup_user_key_fd(Fd, Alg) -> +lookup_user_key_fd(Fd, Key) -> case io:get_line(Fd, '') of eof -> {error, not_found}; Line -> - case string:tokens(Line, " ") of - [Alg, KeyData, _] -> - %% io:format("lookup_user_key_fd: HostList ~p Alg ~p KeyData ~p\n", - %% [HostList, Alg, KeyData]), - decode_public_key_v2(ssh_bits:b64_decode(KeyData), Alg); - _Other -> - %%?dbg(false, "key_fd Other: ~w ~w\n", [Alg, _Other]), - lookup_user_key_fd(Fd, Alg) + case public_key:ssh_decode(Line, auth_keys) of + [{AuthKey, _}] -> + case is_auth_key(Key, AuthKey) of + true -> + {ok, Key}; + false -> + lookup_user_key_fd(Fd, Key) + end; + [] -> + lookup_user_key_fd(Fd, Key) end end. +is_auth_key(Key, Key) -> + true; +is_auth_key(_,_) -> + false. -encode_public_key(#ssh_key{type = rsa, public = {N, E}}) -> - ssh_bits:encode(["ssh-rsa",E,N], - [string,mpint,mpint]); -encode_public_key(#ssh_key{type = dsa, public = {P,Q,G,Y}}) -> - ssh_bits:encode(["ssh-dss",P,Q,G,Y], - [string,mpint,mpint,mpint,mpint]). - -%% -%% Utils -%% - -%% server use this to find individual keys for -%% an individual user when user tries to login -%% with publickey -ssh_dir({remoteuser, User}, Opts) -> - case proplists:get_value(user_dir_fun, Opts) of - undefined -> - case proplists:get_value(user_dir, Opts) of - undefined -> - default_user_dir(); - Dir -> - Dir - end; - FUN -> - FUN(User) - end; - -%% client use this to find client ssh keys -ssh_dir(user, Opts) -> - case proplists:get_value(user_dir, Opts, false) of - false -> default_user_dir(); - D -> D - end; - -%% server use this to find server host keys -ssh_dir(system, Opts) -> - proplists:get_value(system_dir, Opts, "/etc/ssh"). - -file_name(Type, Name, Opts) -> - FN = filename:join(ssh_dir(Type, Opts), Name), - %%?dbg(?DBG_PATHS, "file_name: ~p\n", [FN]), - FN. default_user_dir()-> {ok,[[Home|_]]} = init:get_argument(home), diff --git a/lib/ssh/src/ssh_rsa.erl b/lib/ssh/src/ssh_rsa.erl index 77c411b09f..03c55319b7 100644 --- a/lib/ssh/src/ssh_rsa.erl +++ b/lib/ssh/src/ssh_rsa.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,40 +23,23 @@ -module(ssh_rsa). +-include_lib("public_key/include/public_key.hrl"). + -export([verify/3, sign/2]). -export([alg_name/0]). -include("ssh.hrl"). --include("PKCS-1.hrl"). - -define(MGF(Seed,Len), mgf1((Seed),(Len))). -define(HASH(X), crypto:sha((X))). -define(HLen, 20). -%% start() -> -%% crypto:start(). - -%% sign_file(File) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:private_host_rsa_key(user), -%% sign(Key, Bin). - -%% verify_file(File, Sig) -> -%% start(), -%% {ok,Bin} = file:read_file(File), -%% {ok,Key} = ssh_file:public_host_rsa_key(user), -%% verify(Key, Bin, Sig). - sign(Private,Mb) -> rsassa_pkcs1_v1_5_sign(Private,Mb). verify(Public,Mb,Sb) -> rsassa_pkcs1_v1_5_verify(Public,Mb,Sb). - - %% Integer to octet string i2osp(X, XLen) -> ssh_bits:i2bin(X, XLen). @@ -65,125 +48,23 @@ i2osp(X, XLen) -> os2ip(X) -> ssh_bits:bin2i(X). -%% decrypt1, M = message representative -%% rsaep(#ssh_key { public={N,E}}, M) -> -%% ?ssh_assert(M >= 0 andalso M =< N-1, out_of_range), -%% ssh_math:ipow(M, E, N). - -%% encrypt1, C = cipher representative -%% rsadp(#ssh_key { public={N,_}, private={_,D}}, C) -> -%% ?ssh_assert(C >= 0 andalso C =< N-1, out_of_range), -%% ssh_math:ipow(C, D, N). - %% sign1, M = message representative -rsasp1(#ssh_key { public={N,_}, private={_,D}}, M) -> +rsasp1(#'RSAPrivateKey'{modulus = N, privateExponent = D}, M) -> ?ssh_assert((M >= 0 andalso M =< N-1), out_of_range), ssh_math:ipow(M, D, N). %% verify1, S =signature representative -rsavp1(#ssh_key { public={N,E}}, S) -> +rsavp1(#'RSAPublicKey'{publicExponent = E, modulus = N}, S) -> ?ssh_assert(S >= 0 andalso S =< N-1, out_of_range), ssh_math:ipow(S, E, N). -%% M messaage -%% rsaes_oaep_encrypt(Public, M) -> -%% rsaes_oaep_encrypt(Public, M, <<>>). - -%% rsaes_oaep_encrypt(Public=#ssh_key { public={N,_E}}, M, L) -> -%% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), -%% K = (ssh_bits:isize(N)+7) div 8, -%% MLen = size(M), -%% %% LLen = size(L), -%% ?ssh_assert(MLen =< K - 2*?HLen - 2, message_to_long), -%% LHash = ?HASH(L), -%% PS = ssh_bits:fill_bits(K - MLen - 2*?HLen - 2, 0), -%% DB = <<LHash/binary, PS/binary, 16#01, M/binary>>, -%% Seed = ssh_bits:random(?HLen), -%% DbMask = ?MGF(Seed, K - ?HLen - 1), -%% MaskedDB = ssh_bits:xor_bits(DB, DbMask), -%% SeedMask = ?MGF(MaskedDB, ?HLen), -%% MaskedSeed = ssh_bits:xor_bits(Seed, SeedMask), -%% EM = <<16#00, MaskedSeed/binary, MaskedDB/binary>>, -%% Mc = os2ip(EM), -%% C = rsaep(Public, Mc), -%% i2osp(C, K). - -%% rsaes_oaep_decrypt(Key, C) -> -%% rsaes_oaep_decrypt(Key, C, <<>>). - -%% rsaes_oaep_decrypt(Private=#ssh_key { public={N,_},private={_,_}},Cb,L) -> -%% ?ssh_assert(size(L) =< 16#ffffffffffffffff, label_to_long), -%% K = (ssh_bits:isize(N)+7) div 8, -%% ?ssh_assert(K == 2*?HLen + 2, decryption_error), -%% C = os2ip(Cb), -%% M = rsadp(Private, C), -%% EM = i2osp(M, K), -%% LHash = ?HASH(L), -%% MLen = K - ?HLen -1, -%% case EM of -%% <<16#00, MaskedSeed:?HLen/binary, MaskedDB:MLen>> -> -%% SeedMask = ?MGF(MaskedDB, ?HLen), -%% Seed = ssh_bits:xor_bits(MaskedSeed, SeedMask), -%% DbMask = ?MGF(Seed, K - ?HLen - 1), -%% DB = ssh_bits:xor_bits(MaskedDB, DbMask), -%% PSLen = K - MLen - 2*?HLen - 2, -%% case DB of -%% <<LHash:?HLen, _PS:PSLen/binary, 16#01, M/binary>> -> -%% M; -%% _ -> -%% exit(decryption_error) -%% end; -%% _ -> -%% exit(decryption_error) -%% end. - - -%% rsaes_pkcs1_v1_5_encrypt(Public=#ssh_key { public={N,_}}, M) -> -%% K = (ssh_bits:isize(N)+7) div 8, -%% MLen = size(M), -%% ?ssh_assert(MLen =< K - 11, message_to_long), -%% PS = ssh_bits:random(K - MLen - 3), -%% EM = <<16#00,16#02,PS/binary,16#00,M/binary>>, -%% Mc = os2ip(EM), -%% C = rsaep(Public, Mc), -%% i2osp(C, K). - - -%% rsaes_pkcs1_v1_5_decrypt(Private=#ssh_key { public={N,_},private={_,_}}, -%% Cb) -> -%% K = (ssh_bits:isize(N)+7) div 8, -%% CLen = size(Cb), -%% ?ssh_assert(CLen == K andalso K >= 11, decryption_error), -%% C = os2ip(Cb), -%% M = rsadp(Private, C), -%% EM = i2osp(M, K), -%% PSLen = K - CLen - 3, -%% case EM of -%% <<16#00, 16#02, _PS:PSLen/binary, 16#00, M>> -> -%% M; -%% _ -> -%% exit(decryption_error) -%% end. - -%% rsassa_pss_sign(Private=#ssh_key { public={N,_},private={_,_}},Mb) -> -%% ModBits = ssh_bits:isize(N), -%% K = (ModBits+7) div 8, -%% EM = emsa_pss_encode(Mb, ModBits - 1), -%% M = os2ip(EM), -%% S = rsasp1(Private, M), -%% i2osp(S, K). - -%% rsassa_pss_verify(Public=#ssh_key { public={N,_E}},Mb,Sb) -> -%% ModBits = ssh_bits:isize(N), -%% K = (ModBits+7) div 8, -%% ?ssh_assert(size(Sb) == K, invalid_signature), -%% S = os2ip(Sb), -%% M = rsavp1(Public,S), -%% EMLen = (ModBits-1+7) div 8, -%% EM = i2osp(M, EMLen), -%% emsa_pss_verify(Mb, EM, ModBits-1). - +rsassa_pkcs1_v1_5_sign(#'RSAPrivateKey'{modulus = N} = Private, Mb) -> + K = (ssh_bits:isize(N)+7) div 8, + EM = emsa_pkcs1_v1_5_encode(Mb, K), + M = os2ip(EM), + S = rsasp1(Private, M), + i2osp(S, K); rsassa_pkcs1_v1_5_sign(Private=#ssh_key { public={N,_},private={_,_D}},Mb) -> K = (ssh_bits:isize(N)+7) div 8, @@ -192,7 +73,7 @@ rsassa_pkcs1_v1_5_sign(Private=#ssh_key { public={N,_},private={_,_D}},Mb) -> S = rsasp1(Private, M), i2osp(S, K). -rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) -> +rsassa_pkcs1_v1_5_verify(#'RSAPublicKey'{modulus = N} = Public, Mb, Sb) -> K = (ssh_bits:isize(N)+7) div 8, ?ssh_assert(size(Sb) == K, invalid_signature), S = os2ip(Sb), @@ -200,9 +81,9 @@ rsassa_pkcs1_v1_5_verify(Public=#ssh_key { public={N,_E}}, Mb, Sb) -> EM = i2osp(M, K), %?dbg(true, "verify K=~p S=~w ~n#M=~w~n#EM=~w~n", [K, S, M, EM]), case emsa_pkcs1_v1_5_encode(Mb, K) of - EM -> ok; - _S -> - {error, invalid_signature} + EM -> true; + _S -> false + %%{error, invalid_signature} end. @@ -211,87 +92,13 @@ emsa_pkcs1_v1_5_encode(M, EMLen) -> %% Must use speical xxNull types here! Alg = #'AlgorithmNull' { algorithm = ?'id-sha1', parameters = <<>> }, - {ok,TCode} = 'PKCS-1':encode('DigestInfoNull', - #'DigestInfoNull'{ digestAlgorithm = Alg, - digest = H }), - T = list_to_binary(TCode), - TLen = size(T), + TCode = public_key:der_encode('DigestInfoNull', + #'DigestInfoNull'{ digestAlgorithm = Alg, + digest = H }), + TLen = size(TCode), ?ssh_assert(EMLen >= TLen + 11, message_to_short), PS = ssh_bits:fill_bits(EMLen - TLen - 3, 16#ff), - <<16#00, 16#01, PS/binary, 16#00, T/binary>>. - - -%% emsa_pss_encode(M, EMBits) -> -%% emsa_pss_encode(M, EMBits, 0). - -%% emsa_pss_encode(M, EMBits, SLen) -> -%% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), -%% EMLen = (EMBits + 7) div 8, -%% MHash = ?HASH(M), -%% ?ssh_assert(EMLen >= ?HLen + SLen + 2, encoding_error), -%% Salt = ssh_bits:random(SLen), -%% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, -%% MHash, Salt], -%% H = ?HASH(M1), -%% PS = ssh_bits:fill_bits(EMLen-SLen-?HLen-2, 0), -%% DB = <<PS/binary, 16#01, Salt/binary>>, -%% DbMask = ?MGF(H, EMLen - ?HLen -1), -%% MaskedDB = ssh_bits:xor_bits(DB, DbMask), -%% ZLen = 8*EMLen - EMBits, -%% NZLen = (8 - (ZLen rem 8)) rem 8, -%% <<_:ZLen, NZ:NZLen, MaskedDB1/binary>> = MaskedDB, -%% MaskedDB2 = <<0:ZLen, NZ:NZLen, MaskedDB1/binary>>, -%% <<MaskedDB2/binary, H/binary, 16#BC>>. - - -%% emsa_pss_verify(M, EM, EMBits) -> -%% emsa_pss_verify(M, EM, EMBits, 0). - -%% emsa_pss_verify(M, EM, EMBits, SLen) -> -%% ?ssh_assert(size(M) =< 16#ffffffffffffffff, message_to_long), -%% EMLen = (EMBits + 7) div 8, -%% MHash = ?HASH(M), -%% ?ssh_assert(EMLen >= ?HLen + SLen + 2, inconsistent), -%% MaskLen = (EMLen - ?HLen - 1)-1, -%% ZLen = 8*EMLen - EMBits, -%% NZLen = (8 - (ZLen rem 8)) rem 8, -%% case EM of -%% <<0:ZLen,Nz:NZLen,MaskedDB1:MaskLen/binary, H:?HLen/binary, 16#BC>> -> -%% MaskedDB = <<0:ZLen,Nz:NZLen,MaskedDB1/binary>>, -%% DbMask = ?MGF(H, EMLen - ?HLen - 1), -%% DB = ssh_bits:xor_bits(MaskedDB, DbMask), -%% PSLen1 = (EMLen - SLen - ?HLen - 2) - 1, -%% PS = ssh_bits:fill_bits(PSLen1, 0), -%% case DB of -%% <<_:ZLen,0:NZLen,PS:PSLen1/binary,16#01,Salt:SLen/binary>> -> -%% M1 = [16#00,16#00,16#00,16#00,16#00,16#00,16#00,16#00, -%% MHash, Salt], -%% case ?HASH(M1) of -%% H -> ok; -%% _ -> exit(inconsistent) -%% end; -%% _ -> -%% exit(inconsistent) -%% end; -%% _ -> -%% exit(inconsistent) -%% end. - - - -%% Mask generating function MGF1 -%% mgf1(MGFSeed, MaskLen) -> -%% T = mgf1_loop(0, ((MaskLen + ?HLen -1) div ?HLen) - 1, MGFSeed, ""), -%% <<R:MaskLen/binary, _/binary>> = T, -%% R. - -%% mgf1_loop(Counter, N, _, T) when Counter > N -> -%% list_to_binary(T); -%% mgf1_loop(Counter, N, MGFSeed, T) -> -%% C = i2osp(Counter, 4), -%% mgf1_loop(Counter+1, N, MGFSeed, [T, ?HASH([MGFSeed, C])]). - - + <<16#00, 16#01, PS/binary, 16#00, TCode/binary>>. alg_name() -> diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index de3e29e2f1..f610c71efa 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,10 +23,11 @@ -module(ssh_transport). --include("ssh_transport.hrl"). +-include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/inet.hrl"). +-include("ssh_transport.hrl"). -include("ssh.hrl"). --include_lib("kernel/include/inet.hrl"). -export([connect/5, accept/4]). -export([versions/2, hello_version_msg/1]). @@ -331,6 +332,7 @@ handle_kexdh_init(#ssh_msg_kexdh_init{e = E}, Ssh0) -> }, Ssh0), %%?dbg(?DBG_KEX, "shared_secret: ~s ~n", [fmt_binary(K, 16, 4)]), %%?dbg(?DBG_KEX, "hash: ~s ~n", [fmt_binary(H, 16, 4)]), + %%Hash = crypto:sha(PlainText), {ok, SshPacket, Ssh1#ssh{keyex_key = {{Private, Public}, {G, P}}, shared_secret = K, exchanged_hash = H, @@ -364,6 +366,7 @@ handle_kexdh_reply(#ssh_msg_kexdh_reply{public_host_key = HostKey, f = F, H = kex_h(Ssh0, HostKey, Public, F, K), %%?dbg(?DBG_KEX, "shared_secret: ~s ~n", [fmt_binary(K, 16, 4)]), %%?dbg(?DBG_KEX, "hash: ~s ~n", [fmt_binary(H, 16, 4)]), + %%Hash = crypto:sha(PlainText), case verify_host_key(Ssh0, HostKey, H, H_SIG) of ok -> {SshPacket, Ssh} = ssh_packet(#ssh_msg_newkeys{}, Ssh0), @@ -427,8 +430,8 @@ get_host_key(SSH) -> case ALG#alg.hkey of 'ssh-rsa' -> case Mod:private_host_rsa_key(Scope, Opts) of - {ok,Key=#ssh_key { public={N,E}} } -> - %%?dbg(true, "x~n", []), + {ok, #'RSAPrivateKey'{modulus = N, publicExponent = E} = Key} -> + %%?dbg(true, "x~n", []), {Key, ssh_bits:encode(["ssh-rsa",E,N],[string,mpint,mpint])}; Error -> @@ -437,7 +440,7 @@ get_host_key(SSH) -> end; 'ssh-dss' -> case Mod:private_host_dsa_key(Scope, Opts) of - {ok,Key=#ssh_key { public={P,Q,G,Y}}} -> + {ok, #'DSAPrivateKey'{y = Y, p = P, q = Q, g = G} = Key} -> {Key, ssh_bits:encode(["ssh-dss",P,Q,G,Y], [string,mpint,mpint,mpint,mpint])}; Error -> @@ -447,59 +450,60 @@ get_host_key(SSH) -> exit({error, bad_key_type}) end. -sign_host_key(Ssh, Private, H) -> - ALG = Ssh#ssh.algorithms, - Module = case ALG#alg.hkey of - 'ssh-rsa' -> - ssh_rsa; - 'ssh-dss' -> - ssh_dsa - end, - case catch Module:sign(Private, H) of - {'EXIT', Reason} -> - error_logger:format("SIGN FAILED: ~p\n", [Reason]), - {error, Reason}; - SIG -> - ssh_bits:encode([Module:alg_name() ,SIG],[string,binary]) - end. +sign_host_key(_Ssh, #'RSAPrivateKey'{} = Private, H) -> + Hash = sha, %% Option ?! + Signature = public_key:sign(H, Hash, Private), + ssh_bits:encode(["ssh-rsa", Signature],[string, binary]); +sign_host_key(_Ssh, #'DSAPrivateKey'{} = Private, H) -> + Hash = sha, %% Option ?! + DerSignature = public_key:sign(H, Hash, Private), + #'Dss-Sig-Value'{r = R, s = S} = public_key:der_decode('Dss-Sig-Value', DerSignature), + RawSignature = <<R:160/big-unsigned-integer, S:160/big-unsigned-integer>>, + ssh_bits:encode(["ssh-dss", RawSignature],[string, binary]). verify_host_key(SSH, K_S, H, H_SIG) -> ALG = SSH#ssh.algorithms, case ALG#alg.hkey of 'ssh-rsa' -> - case ssh_bits:decode(K_S,[string,mpint,mpint]) of - ["ssh-rsa", E, N] -> - ["ssh-rsa",SIG] = ssh_bits:decode(H_SIG,[string,binary]), - Public = #ssh_key { type=rsa, public={N,E} }, - case catch ssh_rsa:verify(Public, H, SIG) of - {'EXIT', Reason} -> - error_logger:format("VERIFY FAILED: ~p\n", [Reason]), - {error, bad_signature}; - ok -> - known_host_key(SSH, Public, "ssh-rsa") - end; - _ -> - {error, bad_format} - end; + verify_host_key_rsa(SSH, K_S, H, H_SIG); 'ssh-dss' -> - case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of - ["ssh-dss",P,Q,G,Y] -> - ["ssh-dss",SIG] = ssh_bits:decode(H_SIG,[string,binary]), - Public = #ssh_key { type=dsa, public={P,Q,G,Y} }, - case catch ssh_dsa:verify(Public, H, SIG) of - {'EXIT', Reason} -> - error_logger:format("VERIFY FAILED: ~p\n", [Reason]), - {error, bad_signature}; - ok -> - known_host_key(SSH, Public, "ssh-dss") - end; - _ -> - {error, bad_host_key_format} - end; + verify_host_key_dss(SSH, K_S, H, H_SIG); _ -> {error, bad_host_key_algorithm} end. +verify_host_key_rsa(SSH, K_S, H, H_SIG) -> + case ssh_bits:decode(K_S,[string,mpint,mpint]) of + ["ssh-rsa", E, N] -> + ["ssh-rsa",SIG] = ssh_bits:decode(H_SIG,[string,binary]), + Public = #'RSAPublicKey'{publicExponent = E, modulus = N}, + case public_key:verify(H, sha, SIG, Public) of + false -> + {error, bad_signature}; + true -> + known_host_key(SSH, Public, "ssh-rsa") + end; + _ -> + {error, bad_format} + end. + +verify_host_key_dss(SSH, K_S, H, H_SIG) -> + case ssh_bits:decode(K_S,[string,mpint,mpint,mpint,mpint]) of + ["ssh-dss",P,Q,G,Y] -> + ["ssh-dss",SIG] = ssh_bits:decode(H_SIG,[string,binary]), + Public = {Y, #'Dss-Parms'{p = P, q = Q, g = G}}, + <<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}), + case public_key:verify(H, sha, Signature, Public) of + false -> + {error, bad_signature}; + true -> + known_host_key(SSH, Public, "ssh-dss") + end; + _ -> + {error, bad_host_key_format} + end. + accepted_host(Ssh, PeerName, Opts) -> case proplists:get_value(silently_accept_hosts, Opts, false) of true -> @@ -1055,6 +1059,7 @@ kex_h(SSH, K_S, E, F, K) -> [string,string,binary,binary,binary, mpint,mpint,mpint]), crypto:sha(L). + kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) -> L = if Min==-1; Max==-1 -> @@ -1075,7 +1080,7 @@ kex_h(SSH, K_S, Min, NBits, Max, Prime, Gen, E, F, K) -> Prime, Gen, E,F,K], Ts) end, crypto:sha(L). - + mac_key_size('hmac-sha1') -> 20*8; mac_key_size('hmac-sha1-96') -> 20*8; mac_key_size('hmac-md5') -> 16*8; |