aboutsummaryrefslogblamecommitdiffstats
path: root/lib/ssh/src/ssh_bits.erl
blob: 1570e5ed4d25c26443acb41a7ed0bdda9ca431d8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10

                   
  
                                                        
  




                                                                      
  



                                                                         
  










                                                    

                                          
                                                     

                                                      


                   
                    
                                     

                            
                                         































































                                                                       




                                                    
 




                                           




                                                             







                                                      









                                     













































                                              
        
 


                




                                                            







                                              









                                                                      



                                             
                                
                                       

                                         
                 






































                                                                           
                                                      


                                                                         
                                                                       






















                                                                         



                                                      



















































































                                                                       






                                                        
 
 



                            
                                
 


                           
 



                                         
 



                                         

 
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2005-2013. 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
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%

%%

%%% Description : SSH 1/2 pdu elements encode/decode

-module(ssh_bits).

-include("ssh.hrl").

-export([encode/2, decode/2]).
%%-export([decode/1, decode/2, decode/3]).
-export([mpint/1,  bignum/1, string/1, name_list/1]).
%%-export([b64_encode/1, b64_decode/1]).
%%-export([install_messages/1, uninstall_messages/1]).

%% integer utils
-export([isize/1]).
-export([random/1]).
%%-export([xor_bits/2, fill_bits/2]).
-export([i2bin/2, bin2i/1]).

%%-import(lists, [foreach/2, reverse/1]).

-define(name_list(X), 
	(fun(B) -> ?binary(B) end)(list_to_binary(name_concat(X)))).


name_concat([Name]) when is_atom(Name) -> atom_to_list(Name);
name_concat([Name]) when is_list(Name) -> Name;
name_concat([Name|Ns]) -> 
    if is_atom(Name) ->
	    [atom_to_list(Name),"," | name_concat(Ns)];
       is_list(Name) ->
	    [Name,"," | name_concat(Ns)]
    end;
name_concat([]) -> [].


name_list(Ns) ->
    ?name_list(Ns).
    

string(Str) ->
    ?string(Str).


%% MP representaion  (SSH2)
mpint(X) when X < 0 ->
    if X == -1 ->
	    <<0,0,0,1,16#ff>>;	    
       true ->
	    mpint_neg(X,0,[])
    end;
mpint(X) ->
    if X == 0 ->
	    <<0,0,0,0>>;
       true ->
	    mpint_pos(X,0,[])
    end.

mpint_neg(-1,I,Ds=[MSB|_]) ->
    if MSB band 16#80 =/= 16#80 ->
	    <<?UINT32((I+1)), (list_to_binary([255|Ds]))/binary>>;
       true ->
	    (<<?UINT32(I), (list_to_binary(Ds))/binary>>)
    end;
mpint_neg(X,I,Ds)  ->
    mpint_neg(X bsr 8,I+1,[(X band 255)|Ds]).
    
mpint_pos(0,I,Ds=[MSB|_]) ->
    if MSB band 16#80 == 16#80 ->
	    <<?UINT32((I+1)), (list_to_binary([0|Ds]))/binary>>;
       true ->
	    (<<?UINT32(I), (list_to_binary(Ds))/binary>>)
    end;
mpint_pos(X,I,Ds) ->
    mpint_pos(X bsr 8,I+1,[(X band 255)|Ds]).


%% BIGNUM representation SSH1
bignum(X) ->
    XSz = isize(X),
    Pad = (8 - (XSz rem 8)) rem 8,
    <<?UINT16(XSz),0:Pad/unsigned-integer,X:XSz/big-unsigned-integer>>.


%% install_messages(Codes) ->
%%     foreach(fun({Name, Code, Ts}) ->
%% 		    put({msg_name,Code}, {Name,Ts}),
%% 		    put({msg_code,Name}, {Code,Ts})
%% 	    end, Codes).

%% uninstall_messages(Codes) ->
%%     foreach(fun({Name, Code, _Ts}) ->
%% 		    erase({msg_name,Code}),
%% 		    erase({msg_code,Name})
%% 	    end, Codes).

%%
%% Encode a record, the type spec is expected to be 
%% in process dictionary under the key {msg_code, RecodeName}
%%
%% encode(Record) ->
%%     case get({msg_code, element(1, Record)}) of
%% 	undefined ->
%% 	    {error, unimplemented};
%% 	{Code, Ts} ->
%% 	    Data = enc(tl(tuple_to_list(Record)), Ts),
%% 	    list_to_binary([Code, Data])
%%     end.

encode(List, Types) ->
    list_to_binary(enc(List, Types)).

%%
%% Encode record element
%%
enc(Xs, Ts) ->
    enc(Xs, Ts, 0).

enc(Xs, [boolean|Ts], Offset) ->
    X = hd(Xs),
    [?boolean(X) | enc(tl(Xs), Ts, Offset+1)];
enc(Xs, [byte|Ts], Offset) ->
    X = hd(Xs),
    [?byte(X) | enc(tl(Xs), Ts,Offset+1)];
enc(Xs, [uint16|Ts], Offset) ->
    X = hd(Xs),
    [?uint16(X) | enc(tl(Xs), Ts,Offset+2)];
enc(Xs, [uint32 |Ts], Offset) ->
    X = hd(Xs),
    [?uint32(X) | enc(tl(Xs), Ts,Offset+4)];
enc(Xs, [uint64|Ts], Offset) ->
    X = hd(Xs),
    [?uint64(X) | enc(tl(Xs), Ts,Offset+8)];
enc(Xs, [mpint|Ts], Offset) ->
    Y = mpint(hd(Xs)),
    [Y | enc(tl(Xs), Ts,Offset+size(Y))];
enc(Xs, [bignum|Ts], Offset) ->
    Y = bignum(hd(Xs)),
    [Y | enc(tl(Xs),Ts,Offset+size(Y))];
enc(Xs, [string|Ts], Offset) ->
    X0 = hd(Xs),
    Y = ?string(X0),
    [Y | enc(tl(Xs),Ts,Offset+size(Y))];
enc(Xs, [binary|Ts], Offset) ->
     X0 = hd(Xs),
    Y = ?binary(X0),
    [Y | enc(tl(Xs), Ts,Offset+size(Y))];
enc(Xs, [name_list|Ts], Offset) ->
    X0 = hd(Xs),
    Y = ?name_list(X0),
    [Y | enc(tl(Xs), Ts, Offset+size(Y))];
enc(Xs, [cookie|Ts], Offset) ->
    [random(16) | enc(tl(Xs), Ts, Offset+16)];
enc(Xs, [{pad,N}|Ts], Offset) ->
    K = (N - (Offset rem N)) rem N,
    [fill_bits(K,0) | enc(Xs, Ts, Offset+K)];
enc(Xs, ['...'| []], _Offset) ->
    X = hd(Xs),
    if is_binary(X) ->
	    [X];
       is_list(X) ->
	    [list_to_binary(X)];
       X==undefined ->
	    []
    end;

enc([], [],_) ->
    [].

%%
%% Decode a SSH record the type is encoded as the first byte
%% and the type spec MUST be installed in {msg_name, ID}
%%

%% decode(Binary = <<?BYTE(ID), _/binary>>) ->
%%     case get({msg_name, ID}) of
%% 	undefined ->
%% 	    {unknown, Binary};
%% 	{Name, Ts} ->
%% 	    {_, Elems} = decode(Binary,1,Ts),
%% 	    list_to_tuple([Name | Elems])
%%     end.

%%
%% Decode a binary form offset 0
%%

decode(Binary, Types) when is_binary(Binary) andalso is_list(Types) ->
    {_,Elems} = decode(Binary, 0, Types),
    Elems.


%% %%
%% %% Decode a binary from byte offset Offset
%% %% return {UpdatedOffset, DecodedElements}
%% %%
decode(Binary, Offset, Types) ->
     decode(Binary, Offset, Types, []).

decode(Binary, Offset, [Type|Ts], Acc) ->
     case Type of
	boolean ->
	    <<_:Offset/binary, ?BOOLEAN(X0), _/binary>> = Binary,
	    X = if X0 == 0 -> false; true -> true end,
	    decode(Binary, Offset+1, Ts, [X | Acc]);

	byte ->
	    <<_:Offset/binary, ?BYTE(X), _/binary>> = Binary,
	    decode(Binary, Offset+1, Ts, [X | Acc]);

	uint16 ->
	    <<_:Offset/binary, ?UINT16(X), _/binary>> = Binary,
	    decode(Binary, Offset+2, Ts, [X | Acc]);

	uint32 ->
	    <<_:Offset/binary, ?UINT32(X), _/binary>> = Binary,
	    decode(Binary, Offset+4, Ts, [X | Acc]);

	uint64 ->
	    <<_:Offset/binary, ?UINT64(X), _/binary>> = Binary,
	    decode(Binary, Offset+8, Ts, [X | Acc]);

	mpint ->
	    <<_:Offset/binary, ?UINT32(L), X0:L/binary,_/binary>> = Binary,
	    Sz = L*8,
	    <<X:Sz/big-signed-integer>> = X0,
	    decode(Binary, Offset+4+L, Ts, [X | Acc]);

	bignum ->
	    <<_:Offset/binary, ?UINT16(Bits),_/binary>> = Binary,
	    L = (Bits+7) div 8,
	    Pad = (8 - (Bits rem 8)) rem 8,
	    <<_:Offset/binary, _:16, _:Pad, X:Bits/big-unsigned-integer,
	     _/binary>> = Binary,
	    decode(Binary, Offset+2+L, Ts, [X | Acc]);

	string ->
	    Size = size(Binary),
	    if Size < Offset + 4  ->
		    %% empty string at end
		    {Size, lists:reverse(["" | Acc])};
	       true ->
		    <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> =
			Binary,
		    decode(Binary, Offset+4+L, Ts, [binary_to_list(X) |
						    Acc])
	    end;

	binary ->
	    <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> = Binary,
	    decode(Binary, Offset+4+L, Ts, [X | Acc]);

	name_list ->
	    <<_:Offset/binary,?UINT32(L), X:L/binary,_/binary>> = Binary,
	    List = string:tokens(binary_to_list(X), ","),
	    decode(Binary, Offset+4+L, Ts, [List | Acc]);

	cookie ->
	    <<_:Offset/binary, X:16/binary, _/binary>> = Binary,
	    decode(Binary, Offset+16, Ts, [X | Acc]);

	{pad,N} -> %% pad offset to a multiple of N
	    K = (N - (Offset rem N)) rem N,
	    decode(Binary, Offset+K, Ts, Acc);
	    
	
	'...' when Ts==[] ->
	    <<_:Offset/binary, X/binary>> = Binary,
	    {Offset+size(X), lists:reverse([X | Acc])}
     end;
 decode(_Binary, Offset, [], Acc) ->
     {Offset, lists:reverse(Acc)}.



%% HACK WARNING :-)
-define(VERSION_MAGIC, 131).
-define(SMALL_INTEGER_EXT, $a).
-define(INTEGER_EXT,       $b).
-define(SMALL_BIG_EXT,     $n).
-define(LARGE_BIG_EXT,     $o).

isize(N) when N > 0 ->
    case term_to_binary(N) of
	<<?VERSION_MAGIC, ?SMALL_INTEGER_EXT, X>> ->
	    isize_byte(X);
	<<?VERSION_MAGIC, ?INTEGER_EXT, X3,X2,X1,X0>> ->
	    isize_bytes([X3,X2,X1,X0]);
	<<?VERSION_MAGIC, ?SMALL_BIG_EXT, S:8/big-unsigned-integer, 0,
	 Ds:S/binary>> ->
	    K = S - 1,
	    <<_:K/binary, Top>> = Ds,
	    isize_byte(Top)+K*8;
	<<?VERSION_MAGIC, ?LARGE_BIG_EXT, S:32/big-unsigned-integer, 0,
	 Ds:S/binary>> ->
	    K = S - 1,
	    <<_:K/binary, Top>> = Ds,
	    isize_byte(Top)+K*8
    end;
isize(0) -> 0.

%% big endian byte list
isize_bytes([0|L]) ->
    isize_bytes(L);
isize_bytes([Top|L]) ->
    isize_byte(Top) + length(L)*8.

%% Well could be improved
isize_byte(X) ->
    if X >= 2#10000000 -> 8;
       X >= 2#1000000 -> 7;
       X >= 2#100000 -> 6;
       X >= 2#10000 -> 5;
       X >= 2#1000 -> 4;
       X >= 2#100 -> 3;
       X >= 2#10 -> 2;
       X >= 2#1 -> 1;
       true -> 0
    end.

%% Convert integer into binary 
%% When XLen is the wanted size in octets of the output
i2bin(X, XLen) ->
    XSz = isize(X),
    Sz = XLen*8,
    if Sz < XSz -> 
	    exit(integer_to_large);
       true ->
	    (<<X:Sz/big-unsigned-integer>>)
    end.

%% Convert a binary into an integer
%%
bin2i(X) ->
    Sz = size(X)*8,
    <<Y:Sz/big-unsigned-integer>> = X,
    Y.

%%
%% Create a binary with constant bytes 
%%
fill_bits(N,C) ->
    list_to_binary(fill(N,C)).

fill(0,_C) -> [];
fill(1,C) -> [C];
fill(N,C) ->
    Cs = fill(N div 2, C),
    Cs1 = [Cs,Cs],
    if N band 1 == 0 ->
	    Cs1;
       true ->
	    [C,Cs,Cs]
    end.

%% xor 2 binaries
%% xor_bits(XBits, YBits) ->
%%     XSz = size(XBits)*8,
%%     YSz = size(YBits)*8,
%%     Sz = if XSz < YSz -> XSz; true -> YSz end, %% min
%%     <<X:Sz, _/binary>> = XBits,
%%     <<Y:Sz, _/binary>> = YBits,
%%     <<(X bxor Y):Sz>>.


%% random/1
%%   Generate N random bytes
%%
random(N) ->
    crypto:strong_rand_bytes(N).

%% %%
%% %% Base 64 encode/decode
%% %%

%% b64_encode(Bs) when is_list(Bs) ->
%%     base64:encode(Bs);
%% b64_encode(Bin) when is_binary(Bin) ->
%%     base64:encode(Bin).

%% b64_decode(Bin) when is_binary(Bin) ->
%%     base64:mime_decode(Bin);
%% b64_decode(Cs) when is_list(Cs) ->
%%     base64:mime_decode(Cs).