diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/ssh/src/ssh_bits.erl | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/ssh/src/ssh_bits.erl')
-rwxr-xr-x | lib/ssh/src/ssh_bits.erl | 483 |
1 files changed, 483 insertions, 0 deletions
diff --git a/lib/ssh/src/ssh_bits.erl b/lib/ssh/src/ssh_bits.erl new file mode 100755 index 0000000000..21ddc5e8fe --- /dev/null +++ b/lib/ssh/src/ssh_bits.erl @@ -0,0 +1,483 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-2009. 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/1, encode/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([irandom/1, irandom/3]). +-export([random/1, random/3]). +-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}) -> + %% ?dbg(true, "install msg: ~s = ~w ~w~n", +%% [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}) -> + %% ?dbg(true, "uninstall msg: ~s = ~w ~w~n", +%% [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, [Type|Ts], Offset) -> + case Type of + boolean -> + X=hd(Xs), + [?boolean(X) | enc(tl(Xs), Ts, Offset+1)]; + byte -> + X=hd(Xs), + [?byte(X) | enc(tl(Xs), Ts,Offset+1)]; + uint16 -> + X=hd(Xs), + [?uint16(X) | enc(tl(Xs), Ts,Offset+2)]; + uint32 -> + X=hd(Xs), + [?uint32(X) | enc(tl(Xs), Ts,Offset+4)]; + uint64 -> + X=hd(Xs), + [?uint64(X) | enc(tl(Xs), Ts,Offset+8)]; + mpint -> + Y=mpint(hd(Xs)), + [Y | enc(tl(Xs), Ts,Offset+size(Y))]; + bignum -> + Y=bignum(hd(Xs)), + [Y | enc(tl(Xs),Ts,Offset+size(Y))]; + string -> + X0=hd(Xs), + Y=?string(X0), + [Y | enc(tl(Xs),Ts,Offset+size(Y))]; + binary -> + X0=hd(Xs), + Y=?binary(X0), + [Y | enc(tl(Xs), Ts,Offset+size(Y))]; + name_list -> + X0=hd(Xs), + Y=?name_list(X0), + [Y | enc(tl(Xs), Ts, Offset+size(Y))]; + cookie -> + [random(16) | enc(tl(Xs), Ts, Offset+16)]; + {pad,N} -> + K = (N - (Offset rem N)) rem N, + [fill_bits(K,0) | enc(Xs, Ts, Offset+K)]; + '...' when Ts==[] -> + X=hd(Xs), + if is_binary(X) -> + [X]; + is_list(X) -> + [list_to_binary(X)]; + X==undefined -> + [] + end + 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, 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), reverse([X | Acc])} + end; +decode(_Binary, Offset, [], Acc) -> + {Offset, 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>>. + +%% +%% irandom(N) +%% +%% Generate a N bits size random number +%% note that the top most bit is always set +%% to guarantee that the number is N bits +%% +irandom(Bits) -> + irandom(Bits, 1, 0). + +%% irandom_odd(Bits) -> +%% irandom(Bits, 1, 1). + +%% +%% irandom(N, Top, Bottom) +%% +%% Generate a N bits size random number +%% Where Top = 0 - do not set top bit +%% = 1 - set the most significant bit +%% = 2 - set two most significant bits +%% Bot = 0 - do not set the least signifcant bit +%% Bot = 1 - set the least signifcant bit (i.e always odd) +%% +irandom(0, _Top, _Bottom) -> + 0; +irandom(Bits, Top, Bottom) -> + Bytes = (Bits+7) div 8, + Skip = (8-(Bits rem 8)) rem 8, + TMask = case Top of + 0 -> 0; + 1 -> 16#80; + 2 -> 16#c0 + end, + BMask = case Bottom of + 0 -> 0; + 1 -> (1 bsl Skip) + end, + <<X:Bits/big-unsigned-integer, _:Skip>> = random(Bytes, TMask, BMask), + X. + +%% +%% random/1 +%% Generate N random bytes +%% +random(N) -> + random(N, 0, 0). + +random(N, TMask, BMask) -> + list_to_binary(rnd(N, TMask, BMask)). + +%% random/3 +%% random(Bytes, TopMask, BotMask) +%% where +%% Bytes is the number of bytes to generate +%% TopMask is bitwised or'ed to the first byte +%% BotMask is bitwised or'ed to the last byte +%% +rnd(0, _TMask, _BMask) -> + []; +rnd(1, TMask, BMask) -> + [(rand8() bor TMask) bor BMask]; +rnd(N, TMask, BMask) -> + [(rand8() bor TMask) | rnd_n(N-1, BMask)]. + +rnd_n(1, BMask) -> + [rand8() bor BMask]; +rnd_n(I, BMask) -> + [rand8() | rnd_n(I-1, BMask)]. + +rand8() -> + (rand32() bsr 8) band 16#ff. + +rand32() -> + random:uniform(16#100000000) -1. + +%% +%% 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). + + |