1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22
23%%------------------------------------------------------------------
24-module(ssh_message).
25
26-include_lib("public_key/include/public_key.hrl").
27
28-include("ssh.hrl").
29-include("ssh_connect.hrl").
30-include("ssh_auth.hrl").
31-include("ssh_transport.hrl").
32
33-export([encode/1, decode/1, decode_keyboard_interactive_prompts/2]).
34-export([ssh2_pubkey_decode/1,
35         ssh2_pubkey_encode/1,
36         ssh2_privkey_decode2/1,
37         %% experimental:
38         ssh2_privkey_encode/1
39        ]).
40
41-behaviour(ssh_dbg).
42-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
43
44ucl(B) ->
45    try unicode:characters_to_list(B) of
46	L when is_list(L) -> L;
47	{error,_Matched,Rest} -> throw({error,{bad_unicode,Rest}})
48    catch
49	_:_ -> throw({error,bad_unicode})
50    end.
51
52-define(unicode_list(B), ucl(B)).
53
54%%%================================================================
55%%%
56%%% Encode/decode messages
57%%%
58
59encode(#ssh_msg_global_request{
60	  name = Name,
61	  want_reply = Bool,
62	  data = Data}) ->
63    <<?Ebyte(?SSH_MSG_GLOBAL_REQUEST), ?Estring(Name), ?Eboolean(Bool), ?'E...'(Data)>>;
64
65encode(#ssh_msg_request_success{data = Data}) ->
66    <<?Ebyte(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>;
67
68encode(#ssh_msg_request_failure{}) ->
69    <<?Ebyte(?SSH_MSG_REQUEST_FAILURE)>>;
70
71encode(#ssh_msg_channel_open{
72	  channel_type = Type,
73	  sender_channel = Sender,
74	  initial_window_size = Window,
75	  maximum_packet_size = Max,
76	  data = Data
77	 }) ->
78    <<?Ebyte(?SSH_MSG_CHANNEL_OPEN), ?Estring(Type), ?Euint32(Sender), ?Euint32(Window), ?Euint32(Max), ?'E...'(Data)>>;
79
80encode(#ssh_msg_channel_open_confirmation{
81	  recipient_channel = Recipient,
82	  sender_channel = Sender,
83	  initial_window_size = InitWindowSize,
84	  maximum_packet_size = MaxPacketSize,
85	  data = Data
86	 }) ->
87    <<?Ebyte(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION),
88      ?Euint32(Recipient), ?Euint32(Sender), ?Euint32(InitWindowSize), ?Euint32(MaxPacketSize),
89      ?'E...'(Data)>>;
90
91encode(#ssh_msg_channel_open_failure{
92	  recipient_channel = Recipient,
93	  reason = Reason,
94	  description = Desc,
95	  lang = Lang
96	 }) ->
97    <<?Ebyte(?SSH_MSG_CHANNEL_OPEN_FAILURE), ?Euint32(Recipient),?Euint32(Reason), ?Estring(Desc), ?Estring(Lang)>>;
98
99encode(#ssh_msg_channel_window_adjust{
100	  recipient_channel = Recipient,
101	  bytes_to_add = Bytes
102	 }) ->
103    <<?Ebyte(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?Euint32(Recipient), ?Euint32(Bytes)>>;
104
105encode(#ssh_msg_channel_data{
106	  recipient_channel = Recipient,
107	  data = Data
108	 }) ->
109   <<?Ebyte(?SSH_MSG_CHANNEL_DATA), ?Euint32(Recipient), ?Ebinary(Data)>>;
110
111encode(#ssh_msg_channel_extended_data{
112	  recipient_channel = Recipient,
113	  data_type_code = DataType,
114	  data = Data
115	 }) ->
116    <<?Ebyte(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?Euint32(Recipient), ?Euint32(DataType), ?Ebinary(Data)>>;
117
118encode(#ssh_msg_channel_eof{recipient_channel = Recipient
119			   }) ->
120    <<?Ebyte(?SSH_MSG_CHANNEL_EOF), ?Euint32(Recipient)>>;
121
122encode(#ssh_msg_channel_close{
123	   recipient_channel = Recipient
124	  }) ->
125    <<?Ebyte(?SSH_MSG_CHANNEL_CLOSE), ?Euint32(Recipient)>>;
126
127encode(#ssh_msg_channel_request{
128	  recipient_channel = Recipient,
129	  request_type = Type,
130	  want_reply = Bool,
131	  data  = Data
132	 }) ->
133    <<?Ebyte(?SSH_MSG_CHANNEL_REQUEST), ?Euint32(Recipient), ?Estring(Type), ?Eboolean(Bool), ?'E...'(Data)>>;
134
135encode(#ssh_msg_channel_success{
136	  recipient_channel = Recipient
137	 }) ->
138    <<?Ebyte(?SSH_MSG_CHANNEL_SUCCESS), ?Euint32(Recipient)>>;
139
140encode(#ssh_msg_channel_failure{
141	  recipient_channel = Recipient
142	 }) ->
143    <<?Ebyte(?SSH_MSG_CHANNEL_FAILURE), ?Euint32(Recipient)>>;
144
145encode(#ssh_msg_userauth_request{
146	  user = User,
147	  service = Service,
148	  method = Method,
149	  data = Data
150	 }) ->
151    <<?Ebyte(?SSH_MSG_USERAUTH_REQUEST), ?Estring_utf8(User), ?Estring(Service), ?Estring(Method), ?'E...'(Data)>>;
152
153encode(#ssh_msg_userauth_failure{
154	  authentications = Auths,
155	  partial_success = Bool
156	 }) ->
157    <<?Ebyte(?SSH_MSG_USERAUTH_FAILURE), ?Estring(Auths), ?Eboolean(Bool)>>;
158
159encode(#ssh_msg_userauth_success{}) ->
160    <<?Ebyte(?SSH_MSG_USERAUTH_SUCCESS)>>;
161
162encode(#ssh_msg_userauth_banner{
163       message = Banner,
164       language = Lang
165      }) ->
166    <<?Ebyte(?SSH_MSG_USERAUTH_BANNER), ?Estring_utf8(Banner), ?Estring(Lang)>>;
167
168encode(#ssh_msg_userauth_pk_ok{
169	  algorithm_name = Alg,
170	  key_blob = KeyBlob
171	 }) ->
172    <<?Ebyte(?SSH_MSG_USERAUTH_PK_OK), ?Estring(Alg), ?Ebinary(KeyBlob)>>;
173
174encode(#ssh_msg_userauth_passwd_changereq{prompt = Prompt,
175					  languge = Lang
176					 })->
177    <<?Ebyte(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?Estring_utf8(Prompt), ?Estring(Lang)>>;
178
179encode(#ssh_msg_userauth_info_request{
180	  name = Name,
181	  instruction = Inst,
182	  language_tag = Lang,
183	  num_prompts = NumPromtps,
184	  data = Data}) ->
185    <<?Ebyte(?SSH_MSG_USERAUTH_INFO_REQUEST), ?Estring_utf8(Name), ?Estring_utf8(Inst), ?Estring(Lang),
186      ?Euint32(NumPromtps), ?'E...'(Data)>>;
187
188encode(#ssh_msg_userauth_info_response{
189	  num_responses = Num,
190	  data = Data}) ->
191    lists:foldl(fun %%("", Acc) -> Acc;  % commented out since it seem wrong
192		    (Response, Acc) -> <<Acc/binary, ?Estring_utf8(Response)>>
193		end,
194		<<?Ebyte(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?Euint32(Num)>>,
195		Data);
196
197encode(#ssh_msg_disconnect{
198	  code = Code,
199	  description = Desc,
200	  language = Lang
201	 }) ->
202    <<?Ebyte(?SSH_MSG_DISCONNECT), ?Euint32(Code), ?Estring_utf8(Desc), ?Estring(Lang)>>;
203
204encode(#ssh_msg_service_request{
205	  name = Service
206	 }) ->
207    <<?Ebyte(?SSH_MSG_SERVICE_REQUEST), ?Estring_utf8(Service)>>;
208
209encode(#ssh_msg_service_accept{
210	  name = Service
211	 }) ->
212    <<?Ebyte(?SSH_MSG_SERVICE_ACCEPT), ?Estring_utf8(Service)>>;
213
214encode(#ssh_msg_ext_info{
215          nr_extensions = N,
216          data = Data
217         }) ->
218    lists:foldl(fun({ExtName,ExtVal}, Acc) ->
219                        <<Acc/binary, ?Estring(ExtName), ?Estring(ExtVal)>>
220                end,
221                <<?Ebyte(?SSH_MSG_EXT_INFO), ?Euint32(N)>>,
222                Data);
223
224encode(#ssh_msg_newkeys{}) ->
225    <<?Ebyte(?SSH_MSG_NEWKEYS)>>;
226
227encode(#ssh_msg_kexinit{
228	  cookie = Cookie,
229	  kex_algorithms = KeyAlgs,
230	  server_host_key_algorithms = HostKeyAlgs,
231	  encryption_algorithms_client_to_server = EncAlgC2S,
232	  encryption_algorithms_server_to_client = EncAlgS2C,
233	  mac_algorithms_client_to_server = MacAlgC2S,
234	  mac_algorithms_server_to_client = MacAlgS2C,
235	  compression_algorithms_client_to_server = CompAlgS2C,
236	  compression_algorithms_server_to_client = CompAlgC2S,
237	  languages_client_to_server = LangC2S,
238	  languages_server_to_client = LangS2C,
239	  first_kex_packet_follows = Bool,
240	  reserved = Reserved
241	 }) ->
242    <<?Ebyte(?SSH_MSG_KEXINIT), Cookie/binary,
243      ?Ename_list(KeyAlgs), ?Ename_list(HostKeyAlgs), ?Ename_list(EncAlgC2S), ?Ename_list(EncAlgS2C), ?Ename_list(MacAlgC2S),
244      ?Ename_list(MacAlgS2C), ?Ename_list(CompAlgS2C), ?Ename_list(CompAlgC2S), ?Ename_list(LangC2S), ?Ename_list(LangS2C),
245      ?Eboolean(Bool), ?Euint32(Reserved)>>;
246
247encode(#ssh_msg_kexdh_init{e = E}) ->
248    <<?Ebyte(?SSH_MSG_KEXDH_INIT), ?Empint(E)>>;
249
250encode(#ssh_msg_kexdh_reply{
251	  public_host_key = {Key,SigAlg},
252	  f = F,
253	  h_sig = Signature
254	 }) ->
255    EncKey = ssh2_pubkey_encode(Key),
256    EncSign = encode_signature(Key, SigAlg, Signature),
257    <<?Ebyte(?SSH_MSG_KEXDH_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
258
259encode(#ssh_msg_kex_dh_gex_request{
260	  min = Min,
261	  n = N,
262	  max = Max
263	 }) ->
264    <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REQUEST), ?Euint32(Min), ?Euint32(N), ?Euint32(Max)>>;
265
266encode(#ssh_msg_kex_dh_gex_request_old{n = N}) ->
267    <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?Euint32(N)>>;
268
269encode(#ssh_msg_kex_dh_gex_group{p = Prime, g = Generator}) ->
270    <<?Ebyte(?SSH_MSG_KEX_DH_GEX_GROUP), ?Empint(Prime), ?Empint(Generator)>>;
271
272encode(#ssh_msg_kex_dh_gex_init{e = Public}) ->
273    <<?Ebyte(?SSH_MSG_KEX_DH_GEX_INIT), ?Empint(Public)>>;
274
275encode(#ssh_msg_kex_dh_gex_reply{
276	  %% Will be private key encode_host_key extracts only the public part!
277	  public_host_key = {Key,SigAlg},
278	  f = F,
279	  h_sig = Signature
280	 }) ->
281    EncKey = ssh2_pubkey_encode(Key),
282    EncSign = encode_signature(Key, SigAlg, Signature),
283    <<?Ebyte(?SSH_MSG_KEX_DH_GEX_REPLY), ?Ebinary(EncKey), ?Empint(F), ?Ebinary(EncSign)>>;
284
285encode(#ssh_msg_kex_ecdh_init{q_c = Q_c}) ->
286    <<?Ebyte(?SSH_MSG_KEX_ECDH_INIT), ?Ebinary(Q_c)>>;
287
288encode(#ssh_msg_kex_ecdh_reply{public_host_key = {Key,SigAlg}, q_s = Q_s, h_sig = Sign}) ->
289    EncKey = ssh2_pubkey_encode(Key),
290    EncSign = encode_signature(Key, SigAlg, Sign),
291    <<?Ebyte(?SSH_MSG_KEX_ECDH_REPLY), ?Ebinary(EncKey), ?Ebinary(Q_s), ?Ebinary(EncSign)>>;
292
293encode(#ssh_msg_ignore{data = Data}) ->
294    <<?Ebyte(?SSH_MSG_IGNORE), ?Estring_utf8(Data)>>;
295
296encode(#ssh_msg_unimplemented{sequence = Seq}) ->
297    <<?Ebyte(?SSH_MSG_UNIMPLEMENTED), ?Euint32(Seq)>>;
298
299encode(#ssh_msg_debug{always_display = Bool,
300		      message = Msg,
301		      language = Lang}) ->
302    <<?Ebyte(?SSH_MSG_DEBUG), ?Eboolean(Bool), ?Estring_utf8(Msg), ?Estring(Lang)>>.
303
304
305%% Connection Messages
306decode(<<?BYTE(?SSH_MSG_GLOBAL_REQUEST), ?DEC_BIN(Name,__0), ?BYTE(Bool), Data/binary>>) ->
307    #ssh_msg_global_request{
308       name = Name,
309       want_reply = erl_boolean(Bool),
310       data = Data
311      };
312decode(<<?BYTE(?SSH_MSG_REQUEST_SUCCESS), Data/binary>>) ->
313    #ssh_msg_request_success{data = Data};
314decode(<<?BYTE(?SSH_MSG_REQUEST_FAILURE)>>) ->
315    #ssh_msg_request_failure{};
316decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN),
317	 ?DEC_BIN(Type,__0), ?UINT32(Sender), ?UINT32(Window), ?UINT32(Max),
318	 Data/binary>>) ->
319    #ssh_msg_channel_open{
320       channel_type = binary_to_list(Type),
321       sender_channel = Sender,
322       initial_window_size = Window,
323       maximum_packet_size = Max,
324       data = Data
325      };
326decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_CONFIRMATION), ?UINT32(Recipient), ?UINT32(Sender),
327	 ?UINT32(InitWindowSize), ?UINT32(MaxPacketSize),
328	 Data/binary>>) ->
329    #ssh_msg_channel_open_confirmation{
330       recipient_channel = Recipient,
331       sender_channel = Sender,
332       initial_window_size = InitWindowSize,
333       maximum_packet_size = MaxPacketSize,
334       data = Data
335      };
336decode(<<?BYTE(?SSH_MSG_CHANNEL_OPEN_FAILURE),  ?UINT32(Recipient), ?UINT32(Reason),
337	 ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1) >> ) ->
338    #ssh_msg_channel_open_failure{
339       recipient_channel = Recipient,
340       reason = Reason,
341       description = ?unicode_list(Desc),
342       lang = Lang
343      };
344decode(<<?BYTE(?SSH_MSG_CHANNEL_WINDOW_ADJUST), ?UINT32(Recipient), ?UINT32(Bytes)>>) ->
345    #ssh_msg_channel_window_adjust{
346       recipient_channel = Recipient,
347       bytes_to_add = Bytes
348    };
349
350decode(<<?BYTE(?SSH_MSG_CHANNEL_DATA), ?UINT32(Recipient), ?DEC_BIN(Data,__0)>>) ->
351    #ssh_msg_channel_data{
352       recipient_channel = Recipient,
353       data = Data
354    };
355decode(<<?BYTE(?SSH_MSG_CHANNEL_EXTENDED_DATA), ?UINT32(Recipient),
356	 ?UINT32(DataType), ?DEC_BIN(Data,__0)>>) ->
357    #ssh_msg_channel_extended_data{
358       recipient_channel = Recipient,
359       data_type_code = DataType,
360       data = Data
361      };
362decode(<<?BYTE(?SSH_MSG_CHANNEL_EOF), ?UINT32(Recipient)>>) ->
363    #ssh_msg_channel_eof{
364       recipient_channel = Recipient
365      };
366decode(<<?BYTE(?SSH_MSG_CHANNEL_CLOSE),  ?UINT32(Recipient)>>) ->
367    #ssh_msg_channel_close{
368       recipient_channel = Recipient
369      };
370decode(<<?BYTE(?SSH_MSG_CHANNEL_REQUEST), ?UINT32(Recipient),
371	 ?DEC_BIN(RequestType,__0), ?BYTE(Bool), Data/binary>>=Bytes) ->
372    try
373        #ssh_msg_channel_request{
374           recipient_channel = Recipient,
375           request_type = ?unicode_list(RequestType),
376           want_reply = erl_boolean(Bool),
377           data  = Data
378          }
379    catch _:_ ->
380            %% Faulty, RFC4254 says:
381            %% "If the request is not recognized or is not
382            %% supported for the channel, SSH_MSG_CHANNEL_FAILURE is returned."
383            %% So we provoke such a message to be sent
384            #ssh_msg_channel_request{
385               recipient_channel = Recipient,
386               request_type = faulty_msg,
387               data = Bytes
388              }
389    end;
390decode(<<?BYTE(?SSH_MSG_CHANNEL_SUCCESS),  ?UINT32(Recipient)>>) ->
391    #ssh_msg_channel_success{
392       recipient_channel = Recipient
393      };
394decode(<<?BYTE(?SSH_MSG_CHANNEL_FAILURE),  ?UINT32(Recipient)>>) ->
395    #ssh_msg_channel_failure{
396       recipient_channel = Recipient
397      };
398
399%%% Auth Messages
400decode(<<?BYTE(?SSH_MSG_USERAUTH_REQUEST),
401	 ?DEC_BIN(User,__0), ?DEC_BIN(Service,__1), ?DEC_BIN(Method,__2),
402	 Data/binary>>) ->
403    #ssh_msg_userauth_request{
404       user =    ?unicode_list(User),
405       service = ?unicode_list(Service),
406       method =  ?unicode_list(Method),
407       data = Data
408      };
409
410decode(<<?BYTE(?SSH_MSG_USERAUTH_FAILURE),
411	 ?DEC_BIN(Auths,__0),
412	 ?BYTE(Bool)>>) ->
413    #ssh_msg_userauth_failure {
414       authentications = ?unicode_list(Auths),
415       partial_success = erl_boolean(Bool)
416      };
417
418decode(<<?BYTE(?SSH_MSG_USERAUTH_SUCCESS)>>) ->
419    #ssh_msg_userauth_success{};
420
421decode(<<?BYTE(?SSH_MSG_USERAUTH_BANNER), ?DEC_BIN(Banner,__0), ?DEC_BIN(Lang,__1) >>) ->
422    #ssh_msg_userauth_banner{
423       message = Banner,
424       language = Lang
425      };
426
427decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_REQUEST),
428	 ?DEC_BIN(Name,__0), ?DEC_BIN(Inst,__1), ?DEC_BIN(Lang,__2),
429	 ?UINT32(NumPromtps), Data/binary>>) ->
430    #ssh_msg_userauth_info_request{
431       name = Name,
432       instruction = Inst,
433       language_tag = Lang,
434       num_prompts = NumPromtps,
435       data = Data};
436
437%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
438decode(<<?BYTE(?SSH_MSG_USERAUTH_PASSWD_CHANGEREQ), ?DEC_BIN(Prompt,__0), ?DEC_BIN(Lang,__1) >>) ->
439    #ssh_msg_userauth_passwd_changereq{
440       prompt = Prompt,
441       languge = Lang
442      };
443
444%%% Unhandled message, also masked by same 1:st byte value as ?SSH_MSG_USERAUTH_INFO_REQUEST:
445decode(<<?BYTE(?SSH_MSG_USERAUTH_PK_OK), ?DEC_BIN(Alg,__0), KeyBlob/binary>>) ->
446    #ssh_msg_userauth_pk_ok{
447       algorithm_name = Alg,
448       key_blob = KeyBlob
449      };
450
451decode(<<?BYTE(?SSH_MSG_USERAUTH_INFO_RESPONSE), ?UINT32(Num), Data/binary>>) ->
452    #ssh_msg_userauth_info_response{
453       num_responses = Num,
454       data = Data};
455
456decode(<<?BYTE(?SSH_MSG_EXT_INFO), ?UINT32(N), BinData/binary>>) ->
457    Data = bin_foldr(
458             fun(Bin,Acc) when length(Acc) == N ->
459                     {Bin,Acc};
460                (<<?DEC_BIN(V0,__0), ?DEC_BIN(V1,__1), Rest/binary>>, Acc) ->
461                     {Rest,[{binary_to_list(V0),binary_to_list(V1)}|Acc]}
462             end, [], BinData),
463    #ssh_msg_ext_info{
464       nr_extensions = N,
465       data = Data
466      };
467
468%%% Keyexchange messages
469decode(<<?BYTE(?SSH_MSG_KEXINIT), Cookie:128, Data/binary>>) ->
470    decode_kex_init(Data, [Cookie, ssh_msg_kexinit], 10);
471
472decode(<<"dh",?BYTE(?SSH_MSG_KEXDH_INIT), ?DEC_MPINT(E,__0)>>) ->
473    #ssh_msg_kexdh_init{e = E
474		       };
475
476decode(<<"dh", ?BYTE(?SSH_MSG_KEXDH_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
477    #ssh_msg_kexdh_reply{
478       public_host_key = ssh2_pubkey_decode(Key),
479       f = F,
480       h_sig = decode_signature(Hashsign)
481      };
482
483decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST), ?UINT32(Min), ?UINT32(N), ?UINT32(Max)>>) ->
484    #ssh_msg_kex_dh_gex_request{
485       min = Min,
486       n = N,
487       max = Max
488      };
489
490decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_REQUEST_OLD), ?UINT32(N)>>) ->
491    #ssh_msg_kex_dh_gex_request_old{
492       n = N
493      };
494
495decode(<<"dh_gex",?BYTE(?SSH_MSG_KEX_DH_GEX_GROUP), ?DEC_MPINT(Prime,__0), ?DEC_MPINT(Generator,__1) >>) ->
496    #ssh_msg_kex_dh_gex_group{
497       p = Prime,
498       g = Generator
499      };
500
501decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_INIT), ?DEC_MPINT(E,__0)>>) ->
502    #ssh_msg_kex_dh_gex_init{
503       e = E
504      };
505
506decode(<<?BYTE(?SSH_MSG_KEX_DH_GEX_REPLY), ?DEC_BIN(Key,__0), ?DEC_MPINT(F,__1), ?DEC_BIN(Hashsign,__2)>>) ->
507    #ssh_msg_kex_dh_gex_reply{
508       public_host_key = ssh2_pubkey_decode(Key),
509       f = F,
510       h_sig = decode_signature(Hashsign)
511      };
512
513decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_INIT), ?DEC_BIN(Q_c,__0)>>) ->
514    #ssh_msg_kex_ecdh_init{
515       q_c = Q_c
516      };
517
518decode(<<"ecdh",?BYTE(?SSH_MSG_KEX_ECDH_REPLY),
519	 ?DEC_BIN(Key,__1), ?DEC_BIN(Q_s,__2), ?DEC_BIN(Sig,__3)>>) ->
520    #ssh_msg_kex_ecdh_reply{
521       public_host_key = ssh2_pubkey_decode(Key),
522       q_s = Q_s,
523       h_sig = decode_signature(Sig)
524      };
525
526decode(<<?SSH_MSG_SERVICE_REQUEST, ?DEC_BIN(Service,__0)>>) ->
527    #ssh_msg_service_request{
528       name = ?unicode_list(Service)
529      };
530
531decode(<<?SSH_MSG_SERVICE_ACCEPT, ?DEC_BIN(Service,__0)>>) ->
532    #ssh_msg_service_accept{
533       name = ?unicode_list(Service)
534      };
535
536decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0), ?DEC_BIN(Lang,__1)>>) ->
537    #ssh_msg_disconnect{
538       code = Code,
539       description = ?unicode_list(Desc),
540       language = Lang
541      };
542
543%% Accept bad disconnects from ancient openssh clients that doesn't send language tag.  Use english as a work-around.
544decode(<<?BYTE(?SSH_MSG_DISCONNECT), ?UINT32(Code), ?DEC_BIN(Desc,__0)>>) ->
545    #ssh_msg_disconnect{
546       code = Code,
547       description = ?unicode_list(Desc),
548       language = <<"en">>
549      };
550
551decode(<<?SSH_MSG_NEWKEYS>>) ->
552    #ssh_msg_newkeys{};
553
554decode(<<?BYTE(?SSH_MSG_IGNORE), ?DEC_BIN(Data,__0)>>) ->
555    #ssh_msg_ignore{data = Data};
556
557decode(<<?BYTE(?SSH_MSG_UNIMPLEMENTED), ?UINT32(Seq)>>) ->
558    #ssh_msg_unimplemented{sequence = Seq};
559
560decode(<<?BYTE(?SSH_MSG_DEBUG), ?BYTE(Bool), ?DEC_BIN(Msg,__0), ?DEC_BIN(Lang,__1)>>) ->
561    #ssh_msg_debug{always_display = erl_boolean(Bool),
562		   message = Msg,
563		   language = Lang}.
564
565
566%%%================================================================
567%%%
568%%% Encode/decode ssh public/private keys
569%%%
570
571%%%-------- public key --------
572ssh2_pubkey_encode(#'RSAPublicKey'{modulus = N, publicExponent = E}) ->
573    <<?STRING(<<"ssh-rsa">>), ?Empint(E), ?Empint(N)>>;
574ssh2_pubkey_encode({Y,  #'Dss-Parms'{p = P, q = Q, g = G}}) ->
575    <<?STRING(<<"ssh-dss">>), ?Empint(P), ?Empint(Q), ?Empint(G), ?Empint(Y)>>;
576ssh2_pubkey_encode({#'ECPoint'{point = Q}, {namedCurve,OID}}) ->
577    Curve = public_key:oid2ssh_curvename(OID),
578    KeyType = <<"ecdsa-sha2-", Curve/binary>>,
579    <<?STRING(KeyType), ?STRING(Curve), ?Estring(Q)>>;
580ssh2_pubkey_encode({ed_pub, ed25519, Key}) ->
581    <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>;
582ssh2_pubkey_encode({ed_pub, ed448, Key}) ->
583    <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>;
584ssh2_pubkey_encode({ed_priv, ed25519, Key, _}) ->
585    <<?STRING(<<"ssh-ed25519">>), ?Estring(Key)>>;
586ssh2_pubkey_encode({ed_priv, ed448, Key, _}) ->
587    <<?STRING(<<"ssh-ed448">>), ?Estring(Key)>>.
588
589%%%--------
590ssh2_pubkey_decode(KeyBlob) ->
591    {Key,_RestBlob} = ssh2_pubkey_decode2(KeyBlob),
592    Key.
593
594ssh2_pubkey_decode2(<<?UINT32(7), "ssh-rsa",
595                      ?DEC_INT(E, _EL),
596                      ?DEC_INT(N, _NL),
597                      Rest/binary>>) ->
598    {#'RSAPublicKey'{modulus = N,
599                     publicExponent = E
600                    }, Rest};
601ssh2_pubkey_decode2(<<?UINT32(7), "ssh-dss",
602                      ?DEC_INT(P, _PL),
603                      ?DEC_INT(Q, _QL),
604                      ?DEC_INT(G, _GL),
605                      ?DEC_INT(Y, _YL),
606                      Rest/binary>>) ->
607    {{Y, #'Dss-Parms'{p = P,
608                      q = Q,
609                      g = G}
610     }, Rest};
611ssh2_pubkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
612    Sz = TL-11,
613    <<_Curve:Sz/binary,
614      ?DEC_BIN(SshName, _IL),
615      ?DEC_BIN(Q, _QL),
616      Rest/binary>> = KeyRest,
617    OID = public_key:ssh_curvename2oid(SshName),
618    {{#'ECPoint'{point = Q}, {namedCurve,OID}
619     }, Rest};
620ssh2_pubkey_decode2(<<?UINT32(11), "ssh-ed25519",
621                      ?DEC_BIN(Key, _L),
622                      Rest/binary>>) ->
623    {{ed_pub, ed25519, Key},
624     Rest};
625ssh2_pubkey_decode2(<<?UINT32(9), "ssh-ed448",
626                      ?DEC_BIN(Key, _L),
627                      Rest/binary>>) ->
628    {{ed_pub, ed448, Key},
629     Rest}.
630
631%%%-------- private key --------
632
633%% dialyser... ssh2_privkey_decode(KeyBlob) ->
634%% dialyser...     {Key,_RestBlob} = ssh2_privkey_decode2(KeyBlob),
635%% dialyser...     Key.
636%% See sshkey_private_serialize_opt in sshkey.c
637
638ssh2_privkey_encode(#'RSAPrivateKey'
639                    {version = 'two-prime', % Found this in public_key:generate_key/1 ..
640                     modulus = N,
641                     publicExponent = E,
642                     privateExponent = D,
643                     prime1 = P,
644                     prime2 = Q,
645                     %% exponent1, % D_mod_P_1
646                     %% exponent2, % D_mod_Q_1
647                     coefficient = IQMP
648                    }) ->
649    <<?STRING(<<"ssh-rsa">>),
650      ?Empint(N), % Yes, N and E is reversed relative pubkey format
651      ?Empint(E), % --"--
652      ?Empint(D),
653      ?Empint(IQMP),
654      ?Empint(P),
655      ?Empint(Q)>>;
656
657ssh2_privkey_encode(#'DSAPrivateKey'
658                    {version = 0,
659                     p = P,
660                     q = Q,
661                     g = G,
662                     y = Y,
663                     x = X
664                    }) ->
665    <<?STRING(<<"ssh-dss">>),
666      ?Empint(P),
667      ?Empint(Q),
668      ?Empint(G),
669      ?Empint(Y), % Publ key
670      ?Empint(X)  % Priv key
671    >>;
672
673ssh2_privkey_encode(#'ECPrivateKey'
674                    {version = 1,
675                     parameters = {namedCurve,OID},
676                     privateKey = Priv,
677                     publicKey = Q
678                    }) ->
679    CurveName = public_key:oid2ssh_curvename(OID),
680    <<?STRING(<<"ecdsa-sha2-",CurveName/binary>>),
681      ?STRING(<<"ecdsa-sha2-",CurveName/binary>>), % Yes
682      ?STRING(Q),
683      ?STRING(Priv)>>;
684
685ssh2_privkey_encode({ed_pri, Alg, Pub, Priv}) ->
686    Name = atom_to_binary(Alg),
687    <<?STRING(<<"ssh-",Name/binary>>),
688      ?STRING(Pub),
689      ?STRING(Priv)>>.
690
691%%%--------
692ssh2_privkey_decode2(<<?UINT32(7), "ssh-rsa",
693                       ?DEC_INT(N, _NL), % Yes, N and E is reversed relative pubkey format
694                       ?DEC_INT(E, _EL), % --"--
695                       ?DEC_INT(D, _DL),
696                       ?DEC_INT(IQMP, _IQMPL),
697                       ?DEC_INT(P, _PL),
698                       ?DEC_INT(Q, _QL),
699                       Rest/binary>>) ->
700    {#'RSAPrivateKey'{version = 'two-prime', % Found this in public_key:generate_key/1 ..
701                      modulus = N,
702                      publicExponent = E,
703                      privateExponent = D,
704                      prime1 = P,
705                      prime2 = Q,
706                      %exponent1, % D_mod_P_1
707                      %exponent2, % D_mod_Q_1
708                      coefficient = IQMP
709                     }, Rest};
710ssh2_privkey_decode2(<<?UINT32(7), "ssh-dss",
711                       ?DEC_INT(P, _PL),
712                       ?DEC_INT(Q, _QL),
713                       ?DEC_INT(G, _GL),
714                       ?DEC_INT(Y, _YL), % Publ key
715                       ?DEC_INT(X, _XL), % Priv key
716                       Rest/binary>>) ->
717    {#'DSAPrivateKey'{version = 0,
718                      p = P,
719                      q = Q,
720                      g = G,
721                      y = Y,
722                      x = X
723                     }, Rest};
724ssh2_privkey_decode2(<<?UINT32(TL), "ecdsa-sha2-",KeyRest/binary>>) ->
725    Sz = TL-11,
726    <<_Curve:Sz/binary,
727      ?DEC_BIN(CurveName, _SNN),
728      ?DEC_BIN(Q, _QL),
729      ?DEC_BIN(Priv, _PrivL),
730      Rest/binary>> = KeyRest,
731    OID = public_key:ssh_curvename2oid(CurveName),
732    {#'ECPrivateKey'{version = 1,
733                     parameters = {namedCurve,OID},
734                     privateKey = Priv,
735                     publicKey = Q
736                    }, Rest};
737ssh2_privkey_decode2(<<?UINT32(11), "ssh-ed25519",
738                       ?DEC_BIN(Pub,_Lpub),
739                       ?DEC_BIN(Priv,_Lpriv),
740                       Rest/binary>>) ->
741    {{ed_pri, ed25519, Pub, Priv}, Rest};
742ssh2_privkey_decode2(<<?UINT32(9), "ssh-ed448",
743                       ?DEC_BIN(Pub,_Lpub),
744                       ?DEC_BIN(Priv,_Lpriv),
745                       Rest/binary>>) ->
746    {{ed_pri, ed448, Pub, Priv}, Rest}.
747
748
749%%%================================================================
750%%%
751%%% Helper functions
752%%%
753
754bin_foldr(Fun, Acc, Bin) ->
755    lists:reverse(bin_foldl(Fun, Acc, Bin)).
756
757bin_foldl(_, Acc, <<>>) -> Acc;
758bin_foldl(Fun, Acc0, Bin0) ->
759    case Fun(Bin0,Acc0) of
760        {Bin0,Acc0} ->
761            Acc0;
762        {Bin,Acc} ->
763            bin_foldl(Fun, Acc, Bin)
764    end.
765
766%%%----------------------------------------------------------------
767decode_keyboard_interactive_prompts(<<>>, Acc) ->
768    lists:reverse(Acc);
769decode_keyboard_interactive_prompts(<<0>>, Acc) ->
770    lists:reverse(Acc);
771decode_keyboard_interactive_prompts(<<?DEC_BIN(Prompt,__0), ?BYTE(Bool), Bin/binary>>,
772				    Acc) ->
773    decode_keyboard_interactive_prompts(Bin, [{Prompt, erl_boolean(Bool)} | Acc]).
774
775%%%----------------------------------------------------------------
776erl_boolean(0) ->
777    false;
778erl_boolean(1) ->
779    true.
780
781%%%----------------------------------------------------------------
782decode_kex_init(<<?BYTE(Bool), ?UINT32(X)>>, Acc, 0) ->
783    list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
784decode_kex_init(<<?BYTE(Bool)>>, Acc, 0) ->
785    %% The mandatory trailing UINT32 is missing. Assume the value it anyhow must have
786    %% See rfc 4253 7.1
787    X = 0,
788    list_to_tuple(lists:reverse([X, erl_boolean(Bool) | Acc]));
789decode_kex_init(<<?DEC_BIN(Data,__0), Rest/binary>>, Acc, N) ->
790    Names = string:tokens(?unicode_list(Data), ","),
791    decode_kex_init(Rest, [Names | Acc], N -1).
792
793
794%%%================================================================
795%%%
796%%% Signature decode/encode
797%%%
798
799decode_signature(<<?DEC_BIN(Alg,__0), ?UINT32(_), Signature/binary>>) ->
800    {binary_to_list(Alg), Signature}.
801
802
803encode_signature(#'RSAPublicKey'{}, SigAlg, Signature) ->
804    SignName = list_to_binary(atom_to_list(SigAlg)),
805    <<?Ebinary(SignName), ?Ebinary(Signature)>>;
806encode_signature({_, #'Dss-Parms'{}}, _SigAlg, Signature) ->
807    <<?Ebinary(<<"ssh-dss">>), ?Ebinary(Signature)>>;
808encode_signature({#'ECPoint'{}, {namedCurve,OID}}, _SigAlg, Signature) ->
809    Curve = public_key:oid2ssh_curvename(OID),
810    <<?Ebinary(<<"ecdsa-sha2-",Curve/binary>>), ?Ebinary(Signature)>>;
811encode_signature({ed_pub, ed25519,_}, _SigAlg, Signature) ->
812    <<?Ebinary(<<"ssh-ed25519">>), ?Ebinary(Signature)>>;
813encode_signature({ed_pub, ed448,_}, _SigAlg, Signature) ->
814    <<?Ebinary(<<"ssh-ed448">>), ?Ebinary(Signature)>>.
815
816
817
818%%%################################################################
819%%%#
820%%%# Tracing
821%%%#
822
823ssh_dbg_trace_points() -> [ssh_messages, raw_messages].
824
825ssh_dbg_flags(ssh_messages) -> [c];
826ssh_dbg_flags(raw_messages) -> [c].
827
828ssh_dbg_on(P) when P==ssh_messages ;
829                   P==raw_messages ->
830    dbg:tp(?MODULE,encode,1,x),
831    dbg:tp(?MODULE,decode,1,x).
832
833ssh_dbg_off(P) when P==ssh_messages ;
834                    P==raw_messages ->
835    dbg:ctpg(?MODULE,encode,1),
836    dbg:ctpg(?MODULE,decode,1).
837
838ssh_dbg_format(ssh_messages, {call,{?MODULE,encode,[Msg]}}) ->
839    Name = string:to_upper(atom_to_list(element(1,Msg))),
840    ["Going to send ",Name,":\n",
841     wr_record(ssh_dbg:shrink_bin(Msg))
842    ];
843ssh_dbg_format(ssh_messages, {return_from, {?MODULE,encode,1}, _Ret}) ->
844    skip;
845
846ssh_dbg_format(ssh_messages, {call, {?MODULE,decode,[_]}}) ->
847    skip;
848ssh_dbg_format(ssh_messages, {return_from,{?MODULE,decode,1},Msg}) ->
849    Name = string:to_upper(atom_to_list(element(1,Msg))),
850    ["Received ",Name,":\n",
851     wr_record(ssh_dbg:shrink_bin(Msg)),
852     case Msg of
853         #ssh_msg_userauth_request{service = "ssh-connection",
854                                   method = "publickey",
855                                   data = <<_,?DEC_BIN(Alg,__0),_/binary>>} ->
856             io_lib:format("  data decoded: ~s ... ~n", [Alg]);
857
858         #ssh_msg_channel_request{request_type = "env",
859                                  data = <<?DEC_BIN(Var,__0),?DEC_BIN(Val,__1)>>} ->
860             io_lib:format("  data decoded: ~s = ~s~n", [Var, Val]);
861
862         #ssh_msg_channel_request{request_type = "exec",
863                                  data = <<?DEC_BIN(Cmnd,__0)>>} ->
864             io_lib:format("  data decoded: ~s~n", [Cmnd]);
865
866         #ssh_msg_channel_request{request_type = "pty-req",
867                                  data = <<?DEC_BIN(BTermName,_TermLen),
868                                           ?UINT32(Width),?UINT32(Height),
869                                           ?UINT32(PixWidth), ?UINT32(PixHeight),
870                                           Modes/binary>>} ->
871             io_lib:format("  data decoded: terminal = ~s~n"
872                           "                width x height = ~p x ~p~n"
873                           "                pix-width x pix-height = ~p x ~p~n"
874                           "                pty-opts = ~p~n",
875                           [BTermName, Width,Height, PixWidth, PixHeight,
876                            ssh_connection:decode_pty_opts(Modes)]);
877         _ ->
878             ""
879     end
880    ];
881
882ssh_dbg_format(raw_messages, {call,{?MODULE,decode,[BytesPT]}}) ->
883    ["Received plain text bytes (shown after decryption):\n",
884     io_lib:format("~p",[BytesPT])
885    ];
886ssh_dbg_format(raw_messages, {return_from, {?MODULE,decode,1}, _Ret}) ->
887    skip;
888
889ssh_dbg_format(raw_messages, {call, {?MODULE,encode,[_]}}) ->
890    skip;
891ssh_dbg_format(raw_messages, {return_from,{?MODULE,encode,1},BytesPT}) ->
892    ["Going to send plain text bytes (shown before encryption):\n",
893     io_lib:format("~p",[BytesPT])
894    ].
895
896
897?wr_record(ssh_msg_disconnect);
898?wr_record(ssh_msg_ignore);
899?wr_record(ssh_msg_unimplemented);
900?wr_record(ssh_msg_debug);
901?wr_record(ssh_msg_service_request);
902?wr_record(ssh_msg_service_accept);
903?wr_record(ssh_msg_kexinit);
904?wr_record(ssh_msg_kexdh_init);
905?wr_record(ssh_msg_kexdh_reply);
906?wr_record(ssh_msg_newkeys);
907?wr_record(ssh_msg_ext_info);
908?wr_record(ssh_msg_kex_dh_gex_request);
909?wr_record(ssh_msg_kex_dh_gex_request_old);
910?wr_record(ssh_msg_kex_dh_gex_group);
911?wr_record(ssh_msg_kex_dh_gex_init);
912?wr_record(ssh_msg_kex_dh_gex_reply);
913?wr_record(ssh_msg_kex_ecdh_init);
914?wr_record(ssh_msg_kex_ecdh_reply);
915
916?wr_record(ssh_msg_userauth_request);
917?wr_record(ssh_msg_userauth_failure);
918?wr_record(ssh_msg_userauth_success);
919?wr_record(ssh_msg_userauth_banner);
920?wr_record(ssh_msg_userauth_passwd_changereq);
921?wr_record(ssh_msg_userauth_pk_ok);
922?wr_record(ssh_msg_userauth_info_request);
923?wr_record(ssh_msg_userauth_info_response);
924
925?wr_record(ssh_msg_global_request);
926?wr_record(ssh_msg_request_success);
927?wr_record(ssh_msg_request_failure);
928?wr_record(ssh_msg_channel_open);
929?wr_record(ssh_msg_channel_open_confirmation);
930?wr_record(ssh_msg_channel_open_failure);
931?wr_record(ssh_msg_channel_window_adjust);
932?wr_record(ssh_msg_channel_data);
933?wr_record(ssh_msg_channel_extended_data);
934?wr_record(ssh_msg_channel_eof);
935?wr_record(ssh_msg_channel_close);
936?wr_record(ssh_msg_channel_request);
937?wr_record(ssh_msg_channel_success);
938?wr_record(ssh_msg_channel_failure);
939
940wr_record(R) -> io_lib:format('~p~n',[R]).
941
942