ssh_transport.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,781 行 · 第 1/4 页

ERL
1,781
字号
encrypt_final(SSH) ->    erase(encrypt_ctx),    {ok, SSH#ssh { encrypt = none, 		   encrypt_keys = undefined,		   encrypt_block_size = 8		  }}.encrypt(SSH, Data) ->    case SSH#ssh.encrypt of	none -> 	    Data;	'3des-cbc' ->	    {K1,K2,K3} = SSH#ssh.encrypt_keys,	    IV0 = get(encrypt_ctx),	    ?dbg(?DBG_CRYPTO, "encrypt: IV=~p K1=~p, K2=~p, K3=~p\n",		 [IV0,K1,K2,K3]),	    Enc = crypto:des3_cbc_encrypt(K1,K2,K3,IV0,Data),	    ?dbg(?DBG_CRYPTO, "encrypt: ~p -> ~p\n", [Data, Enc]),	    %% Enc = list_to_binary(E0),	    IV = crypto:des_cbc_ivec(Enc),	    put(encrypt_ctx, IV),	    Enc;	_ ->	    exit({bad_algorithm,SSH#ssh.encrypt})    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Decryption%%   context stored in dictionary as 'decrypt_ctx'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%decrypt_init(SSH) ->    case SSH#ssh.decrypt of	none ->	    {ok,SSH};	'3des-cbc' ->	    {IV,KD} = case SSH#ssh.role of			  client ->			      {hash(SSH, "B", 64),			       hash(SSH, "D", 192)};			  server -> 			      {hash(SSH, "A", 64),			       hash(SSH, "C", 192)}		      end,	    <<K1:8/binary, K2:8/binary, K3:8/binary>> = KD,	    put(decrypt_ctx,  IV),	    {ok,SSH#ssh{ decrypt_keys = {K1,K2,K3},			 decrypt_block_size = 8	}};	_ ->	    exit({bad_algorithm,SSH#ssh.decrypt})    end.decrypt_final(SSH) ->    erase(decrypt_ctx),    {ok, SSH#ssh { decrypt = none, 		   decrypt_keys = undefined,		   decrypt_block_size = 8 }}.decrypt(SSH, Data) ->    case SSH#ssh.decrypt of	none -> 	    Data;	'3des-cbc' ->	    {K1,K2,K3} = SSH#ssh.decrypt_keys,	    IV0 = get(decrypt_ctx),	    ?dbg(?DBG_CRYPTO, "decrypt: IV=~p K1=~p, K2=~p, K3=~p\n",		 [IV0,K1,K2,K3]),	    Dec = crypto:des3_cbc_decrypt(K1,K2,K3,IV0,Data),	    %% Enc = list_to_binary(E0),	    ?dbg(?DBG_CRYPTO, "decrypt: ~p -> ~p\n", [Data, Dec]),	    IV = crypto:des_cbc_ivec(Data),	    put(decrypt_ctx, IV),	    Dec;	_ ->	    exit({bad_algorithm,SSH#ssh.decrypt})    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Compression%%   context stored in dictionary as 'compress_ctx'%%%%     none     REQUIRED        no compression%%     zlib     OPTIONAL        ZLIB (LZ77) compression%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%compress_init(SSH) ->    compress_init(SSH, 1).compress_init(SSH, Level) ->    Compress = SSH#ssh.compress,    ?dbg(?DBG_ZLIB, "compress_init: ~p Level ~p\n", [Compress, Level]),    case Compress of	none ->	    {ok,SSH};	zlib ->	    Z = zlib:open(),	    case zlib:deflateInit(Z, Level) of		ok ->		    put(compress_ctx, Z),		    {ok, SSH};		Error ->		    zlib:close(Z),		    Error	    end;	_ ->	    exit({bad_algorithm,SSH#ssh.compress})    end.compress_final(SSH) ->    case SSH#ssh.compress of	none ->	    {ok, SSH};	zlib ->	    zlib:close(get(compress_ctx)),	    erase(compress_ctx),	    {ok, SSH#ssh { compress = none }};	_ ->	    exit({bad_algorithm,SSH#ssh.compress})    end.compress(SSH, Data) ->    case SSH#ssh.compress of	none ->	    Data;	zlib ->	    Compressed = zlib:deflate(get(compress_ctx), Data, sync),	    ?dbg(?DBG_ZLIB, "deflate: ~p -> ~p\n", [Data, Compressed]),	    list_to_binary(Compressed);	_ ->	    exit({bad_algorithm,SSH#ssh.compress})    end.        %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Decompression%%   context stored in dictionary as 'decompress_ctx'%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%decompress_init(SSH) ->    case SSH#ssh.decompress of	none ->	    {ok,SSH};	zlib ->	    Z = zlib:open(),	    case zlib:inflateInit(Z) of		ok ->		    put(decompress_ctx, Z),		    {ok,SSH};		Error ->		    zlib:close(Z),		    Error	    end;	_ ->	    exit({bad_algorithm,SSH#ssh.decompress})    end.decompress_final(SSH) ->    case SSH#ssh.decompress of	none ->	    {ok, SSH};	zlib ->	    zlib:close(get(decompress_ctx)),	    erase(decompress_ctx),	    {ok, SSH#ssh { decompress = none }};	_ ->	    exit({bad_algorithm,SSH#ssh.decompress})    end.    decompress(SSH, Data) ->    case SSH#ssh.decompress of	none ->	    Data;	zlib ->	    Decompressed = zlib:inflate(get(decompress_ctx), Data),	    ?dbg(?DBG_ZLIB, "inflate: ~p -> ~p\n", [Data, Decompressed]),	    list_to_binary(Decompressed);	_ ->	    exit({bad_algorithm,SSH#ssh.decompress})    end.%%%% macs%%%%     hmac-sha1    REQUIRED        HMAC-SHA1 (digest length = key%%                                  length = 20)%%     hmac-sha1-96 RECOMMENDED     first 96 bits of HMAC-SHA1 (digest%%                                  length = 12, key length = 20)%%     hmac-md5     OPTIONAL        HMAC-MD5 (digest length = key%%                                  length = 16)%%     hmac-md5-96  OPTIONAL        first 96 bits of HMAC-MD5 (digest%%                                  length = 12, key length = 16)%%     none         OPTIONAL        no MAC; NOT RECOMMENDED%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% MAC calculation%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%send_mac_init(SSH) ->    case SSH#ssh.role of	client ->	    Key = hash(SSH, "E", mac_key_size(SSH#ssh.send_mac)),	    {ok, SSH#ssh { send_mac_key = Key }};	server ->	    Key = hash(SSH, "F", mac_key_size(SSH#ssh.send_mac)),	    {ok, SSH#ssh { send_mac_key = Key }}    end.send_mac_final(SSH) ->    {ok, SSH#ssh {  send_mac = none, send_mac_key = undefined }}.send_mac(SSH, Data, Seq) ->    case SSH#ssh.send_mac of	none -> 	    <<>>;	'hmac-sha1' ->	    crypto:sha_mac(SSH#ssh.send_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-sha1-96' ->	    crypto:sha_mac_96(SSH#ssh.send_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-md5' ->	    crypto:md5_mac(SSH#ssh.send_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-md5-96' ->	    crypto:md5_mac_96(SSH#ssh.send_mac_key, [<<?UINT32(Seq)>>, Data]);	_ ->	    exit({bad_algorithm,SSH#ssh.send_mac})    end.	recv_mac_init(SSH) ->    case SSH#ssh.role of	client ->	    Key = hash(SSH, "F", mac_key_size(SSH#ssh.recv_mac)),	    {ok, SSH#ssh { recv_mac_key = Key }};	server ->	    Key = hash(SSH, "E", mac_key_size(SSH#ssh.recv_mac)),	    {ok, SSH#ssh { recv_mac_key = Key }}    end.recv_mac_final(SSH) ->    {ok, SSH#ssh { recv_mac = none, recv_mac_key = undefined }}.recv_mac(SSH, Data, Seq) ->    case SSH#ssh.recv_mac of	none -> 	    <<>>;	'hmac-sha1' ->	    crypto:sha_mac(SSH#ssh.recv_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-sha1-96' ->	    crypto:sha_mac_96(SSH#ssh.recv_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-md5' ->	    crypto:md5_mac(SSH#ssh.recv_mac_key, [<<?UINT32(Seq)>>, Data]);	'hmac-md5-96' ->	    crypto:md5_mac_96(SSH#ssh.recv_mac_key, [<<?UINT32(Seq)>>, Data]);	_ ->	    exit({bad_algorithm,SSH#ssh.recv_mac})    end.%% return N hash bytes (HASH)hash(SSH, Char, Bits) ->    HASH =	case SSH#ssh.kex of	    'diffie-hellman-group1-sha1' ->		fun(Data) -> crypto:sha(Data) end;	    'diffie-hellman-group-exchange-sha1' ->		fun(Data) -> crypto:sha(Data) end;	    _ ->		exit({bad_algorithm,SSH#ssh.kex})	end,    hash(SSH, Char, Bits, HASH).hash(_SSH, _Char, 0, _HASH) ->    <<>>;hash(SSH, Char, N, HASH) ->    K = ssh_bits:mpint(SSH#ssh.shared_secret),    H = SSH#ssh.exchanged_hash,    SessionID = SSH#ssh.session_id,    K1 = HASH([K, H, Char, SessionID]),    Sz = N div 8,    <<Key:Sz/binary, _/binary>> = hash(K, H, K1, N-128, HASH),    ?dbg(?DBG_KEX, "Key ~s: ~s\n", [Char, fmt_binary(Key, 16, 4)]),    Key.hash(_K, _H, Ki, N, _HASH) when N =< 0 ->    Ki;hash(K, H, Ki, N, HASH) ->    Kj = HASH([K, H, Ki]),    hash(K, H, <<Ki/binary, Kj/binary>>, N-128, HASH).%%%% calcuation of H (diffie-hellman-group1-sha1)%% Must use ssh#ssh.algorithms here because new algorithms%% are not install at this point%%kex_h(SSH, K_S, E, F, K) ->    L = ssh_bits:encode([SSH#ssh.c_version, SSH#ssh.s_version,			 SSH#ssh.c_keyinit, SSH#ssh.s_keyinit,			 K_S, E,F,K],			[string,string,string,string,string,			 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 ->		Ts = [string,string,string,string,string,		      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,				 K_S, NBits, Prime, Gen, E,F,K],				Ts);	   true ->		Ts = [string,string,string,string,string,		      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,				 K_S, Min, NBits, Max,				 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;mac_key_size('hmac-md5-96')  -> 16*8;mac_key_size(none) -> 0;mac_key_size(_) -> exit(bad_algoritm).mac_digest_size('hmac-sha1')    -> 20;mac_digest_size('hmac-sha1-96') -> 12;mac_digest_size('hmac-md5')    -> 20;mac_digest_size('hmac-md5-96') -> 12;mac_digest_size(none) -> 0;mac_digest_size(_) -> exit(bad_algoritm).%% integrity_char(send, client) -> "E";%% integrity_char(recv, server) -> "E";%% integrity_char(send, server) -> "F";%% integrity_char(recv, client) -> "F".    valid_mac(SSH, S, Data, Seq) ->    if SSH#ssh.recv_mac_size == 0 ->	    true;       true ->	    {ok,MAC0} = gen_tcp:recv(S, SSH#ssh.recv_mac_size),	    ?dbg(?DBG_MAC, "~p: MAC0=~p\n", [Seq, MAC0]),	    MAC1 = recv_mac(SSH, Data, Seq),	    ?dbg(?DBG_MAC, "~p: MAC1=~p\n", [Seq, MAC1]),	     MAC0 == MAC1    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Diffie-Hellman utils%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%dh_group1() ->    {2, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF}.dh_gen_key(G,P, _Bits) ->    Private = ssh_bits:irandom(ssh_bits:isize(P)-1, 1, 1),    Public = ssh_math:ipow(G, Private, P),    {Private,Public}.%% trim(Str) ->%%     reverse(trim_head(reverse(trim_head(Str)))).trim_tail(Str) ->    reverse(trim_head(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.%%%% DEBUG utils%% Format integers and binaries as hex blocks%%-ifdef(debug).%% fmt_binary(B) ->%%     fmt_binary(B, 0, 0).%% fmt_binary(B, BlockSize) ->%%     fmt_binary(B, BlockSize, 0).fmt_binary(B, BlockSize, GroupSize) ->    fmt_block(fmt_bin(B), BlockSize, GroupSize).fmt_block(Bin, BlockSize, GroupSize) ->    fmt_block(Bin, BlockSize, 0, GroupSize).    fmt_block(Bin, 0, _I, _G) ->    binary_to_list(Bin);fmt_block(Bin, Sz, G, G) when G =/= 0 ->    ["\n" | fmt_block(Bin, Sz, 0, G)];fmt_block(Bin, Sz, I, G) ->    case Bin of	<<Block:Sz/binary, Tail/binary>> ->	    if Tail == <<>> ->		    [binary_to_list(Block)];	       true ->		    [binary_to_list(Block), " " | fmt_block(Tail, Sz, I+1, G)]	    end;	<<>> ->	    [];	_ -> 	    [binary_to_list(Bin)]    end.%% Format integer or binary as hexfmt_bin(X) when integer(X) ->    list_to_binary(io_lib:format("~.16B", [X]));fmt_bin(X) when binary(X) ->    Sz = size(X)*8,    <<Y:Sz/unsigned-big>> = X,    Fmt = "~"++integer_to_list(size(X)*2)++".16.0B",    list_to_binary(io_lib:format(Fmt, [Y])).-endif.%% Retrieve session_id from ssh, needed by public-key authget_session_id(SSH) ->    {ok, SessionID} = call(SSH, get_session_id),    SessionID.await_user(User) ->    receive	{User, ok} ->	    ok    after ?DEFAULT_TIMEOUT ->	    error    end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?