aboutsummaryrefslogblamecommitdiffstats
path: root/lib/asn1/src/asn1rtt_per_common.erl
blob: 3896cb7fa5b3369fbf338356f910906c8c252b85 (plain) (tree)
1
2
3
4
5


                   
                                                        
  










                                                                           







                             


                                     


                                       
                            

                                            
                                    






                                                               

                                                            

                                                              
 



















                                                                       











                                                 












                                             













                                                     
 





                                                         


                                                              





































































































































































                                                                      


















                                                           





























                                                                             



                                         









                                                                      














                                                                        











                                                                    






                                                             











                                 








                                





















































































                                                                            

                                 







































                                                                                                 









                                              







                                                             
%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2012-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(asn1rtt_per_common).

-include("asn1_records.hrl").

-export([decode_fragmented/3,
	 decode_compact_bit_string/1,
	 decode_legacy_bit_string/1,
	 decode_named_bit_string/2,
	 decode_chars/2,decode_chars/3,
	 decode_chars_16bit/1,
	 decode_big_chars/2,
	 decode_oid/1,decode_relative_oid/1,
	 encode_chars/2,encode_chars/3,
	 encode_chars_compact_map/3,
	 encode_chars_16bit/1,encode_big_chars/1,
	 encode_fragmented/2,
	 encode_oid/1,encode_relative_oid/1,
	 encode_unconstrained_number/1,
	 bitstring_from_positions/1,bitstring_from_positions/2,
	 to_bitstring/1,to_bitstring/2,
	 to_named_bitstring/1,to_named_bitstring/2,
	 bs_drop_trailing_zeroes/1,adjust_trailing_zeroes/2,
	 is_default_bitstring/3,is_default_bitstring/5,
	 extension_bitmap/3,
	 open_type_to_binary/1,legacy_open_type_to_binary/1]).

-define('16K',16384).

decode_fragmented(SegSz0, Buf0, Unit) ->
    SegSz = SegSz0 * Unit * ?'16K',
    <<Res:SegSz/bitstring,Buf/bitstring>> = Buf0,
    decode_fragmented_1(Buf, Unit, Res).

decode_fragmented_1(<<0:1,N:7,Buf0/bitstring>>, Unit, Res) ->
    Sz = N*Unit,
    <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
    {<<Res/bitstring,S/bitstring>>,Buf};
decode_fragmented_1(<<1:1,0:1,N:14,Buf0/bitstring>>, Unit, Res) ->
    Sz = N*Unit,
    <<S:Sz/bitstring,Buf/bitstring>> = Buf0,
    {<<Res/bitstring,S/bitstring>>,Buf};
decode_fragmented_1(<<1:1,1:1,SegSz0:6,Buf0/bitstring>>, Unit, Res0) ->
    SegSz = SegSz0 * Unit * ?'16K',
    <<Frag:SegSz/bitstring,Buf/bitstring>> = Buf0,
    Res = <<Res0/bitstring,Frag/bitstring>>,
    decode_fragmented_1(Buf, Unit, Res).

decode_named_bit_string(Val, NNL) ->
    Bits = [B || <<B:1>> <= Val],
    decode_named_bit_string_1(0, Bits, NNL, []).

decode_legacy_bit_string(Val) ->
    [B || <<B:1>> <= Val].

decode_compact_bit_string(Val) ->
    PadLen = (8 - (bit_size(Val) band 7)) band 7,
    {PadLen,<<Val/bitstring,0:PadLen>>}.

decode_chars(Val, N) ->
    [C || <<C:N>> <= Val].

decode_chars(Val, N, Chars) ->
    [element(C+1, Chars) || <<C:N>> <= Val].

decode_chars_16bit(Val) ->
    Cs = [C || <<C:16>> <= Val],
    decode_chars_16bit_1(Cs).

decode_big_chars(Val, N) ->
    decode_big_chars_1(decode_chars(Val, N)).

decode_oid(Octets) ->
    [First|Rest] = dec_subidentifiers(Octets, 0, []),
    Idlist = if
		 First < 40 ->
		     [0,First|Rest];
		 First < 80 ->
		     [1,First - 40|Rest];
		 true ->
		     [2,First - 80|Rest]
	     end,
    list_to_tuple(Idlist).

decode_relative_oid(Octets) ->
    list_to_tuple(dec_subidentifiers(Octets, 0, [])).

encode_chars(Val, NumBits) ->
    << <<C:NumBits>> || C <- Val >>.

encode_chars(Val, NumBits, {Lb,Tab}) ->
    << <<(enc_char(C, Lb, Tab)):NumBits>> || C <- Val >>.

encode_chars_compact_map(Val, NumBits, {Lb,Limit}) ->
    << <<(enc_char_cm(C, Lb, Limit)):NumBits>> || C <- Val >>.

encode_chars_16bit(Val) ->
    L = [case C of
	     {0,0,A,B} -> [A,B];
	     C when is_integer(C) -> [0,C]
	 end || C <- Val],
    iolist_to_binary(L).

encode_big_chars(Val) ->
    L = [case C of
	     {_,_,_,_} -> tuple_to_list(C);
	     C when is_integer(C) -> [<<0,0,0>>,C]
	 end || C <- Val],
    iolist_to_binary(L).

encode_fragmented(Bin, Unit) ->
    encode_fragmented_1(Bin, Unit, 4).

encode_oid(Val) when is_tuple(Val) ->
    encode_oid(tuple_to_list(Val));
encode_oid(Val) ->
    iolist_to_binary(e_object_identifier(Val)).

encode_relative_oid(Val) when is_tuple(Val) ->
    encode_relative_oid(tuple_to_list(Val));
encode_relative_oid(Val) when is_list(Val) ->
    list_to_binary([e_object_element(X)||X <- Val]).

encode_unconstrained_number(Val) when Val >= 0 ->
    if
	Val < 16#80 ->
	    [1,Val];
	Val < 16#100 ->
	    [<<2,0>>,Val];
	true ->
	    case binary:encode_unsigned(Val) of
		<<0:1,_/bitstring>>=Bin ->
		    case byte_size(Bin) of
			Sz when Sz < 128 ->
			    [Sz,Bin];
			Sz when Sz < 16384 ->
			    [<<2:2,Sz:14>>,Bin]
		    end;
		<<1:1,_/bitstring>>=Bin ->
		    case byte_size(Bin)+1 of
			Sz when Sz < 128 ->
			    [Sz,0,Bin];
			Sz when Sz < 16384 ->
			    [<<2:2,Sz:14,0:8>>,Bin]
		    end
	    end
    end;
encode_unconstrained_number(Val) ->
    Oct = enint(Val, []),
    Len = length(Oct),
    if
        Len < 128 ->
            [Len|Oct];
        Len < 16384 ->
            [<<2:2,Len:14>>|Oct]
    end.

%% bitstring_from_positions([Position]) -> BitString
%%  Given an unsorted list of bit positions (0..MAX), construct
%%  a BIT STRING. The rightmost bit will always be a one.

bitstring_from_positions([]) -> <<>>;
bitstring_from_positions([_|_]=L0) ->
    L1 = lists:sort(L0),
    L = diff(L1, -1),
    << <<1:(N+0)>> || N <- L >>.

%% bitstring_from_positions([Position], Lb) -> BitString
%%  Given an unsorted list of bit positions (0..MAX) and a lower bound
%%  for the number of bits, construct BIT STRING (zero-padded on the
%%  right side if needed).

bitstring_from_positions(L0, Lb) ->
    L1 = lists:sort(L0),
    L = diff(L1, -1, Lb-1),
    << <<B:(N+0)>> || {B,N} <- L >>.

%% to_bitstring(Val) -> BitString
%%    Val = BitString | {Unused,Binary} | [OneOrZero] | Integer
%%  Given one of the possible representations for a BIT STRING,
%%  return a bitstring (without adding or removing any zero bits
%%  at the right end).

to_bitstring({0,Bs}) when is_binary(Bs) ->
    Bs;
to_bitstring({Unused,Bs0}) when is_binary(Bs0) ->
    Sz = bit_size(Bs0) - Unused,
    <<Bs:Sz/bits,_/bits>> = Bs0,
    Bs;
to_bitstring(Bs) when is_bitstring(Bs) ->
    Bs;
to_bitstring(Int) when is_integer(Int), Int >= 0 ->
    L = int_to_bitlist(Int),
    << <<B:1>> || B <- L >>;
to_bitstring(L) when is_list(L) ->
    << <<B:1>> || B <- L >>.

%% to_bitstring(Val, Lb) -> BitString
%%    Val = BitString | {Unused,Binary} | [OneOrZero] | Integer
%%    Lb = Integer
%%  Given one of the possible representations for a BIT STRING
%%  and the lower bound for the number of bits,
%%  return a bitstring at least Lb bits long (padded with zeroes
%%  if needed).

to_bitstring({0,Bs}, Lb) when is_binary(Bs) ->
    case bit_size(Bs) of
	Sz when Sz < Lb ->
	    <<Bs/bits,0:(Lb-Sz)>>;
	_ ->
	    Bs
    end;
to_bitstring({Unused,Bs0}, Lb) when is_binary(Bs0) ->
    Sz = bit_size(Bs0) - Unused,
    if
	Sz < Lb ->
	    <<Bs0:Sz/bits,0:(Lb-Sz)>>;
	true ->
	    <<Bs:Sz/bits,_/bits>> = Bs0,
	    Bs
    end;
to_bitstring(Bs, Lb) when is_bitstring(Bs) ->
    adjust_size(Bs, Lb);
to_bitstring(Int, Lb) when is_integer(Int), Int >= 0 ->
    L = int_to_bitlist(Int),
    Bs = << <<B:1>> || B <- L >>,
    adjust_size(Bs, Lb);
to_bitstring(L, Lb) when is_list(L) ->
    Bs = << <<B:1>> || B <- L >>,
    adjust_size(Bs, Lb).

%% to_named_bitstring(Val) -> BitString
%%    Val = BitString | {Unused,Binary} | [OneOrZero] | Integer
%%  Given one of the possible representations for a BIT STRING,
%%  return a bitstring where any trailing zeroes have been stripped.

to_named_bitstring(Val) ->
    Bs = to_bitstring(Val),
    bs_drop_trailing_zeroes(Bs).

%% to_named_bitstring(Val, Lb) -> BitString
%%    Val = BitString | {Unused,Binary} | [OneOrZero] | Integer
%%    Lb = Integer
%%  Given one of the possible representations for a BIT STRING
%%  and the lower bound for the number of bits,
%%  return a bitstring that is at least Lb bits long. There will
%%  be zeroes at the right only if needed to reach the lower bound
%%  for the number of bits.

to_named_bitstring({0,Bs}, Lb) when is_binary(Bs) ->
    adjust_trailing_zeroes(Bs, Lb);
to_named_bitstring({Unused,Bs0}, Lb) when is_binary(Bs0) ->
    Sz = bit_size(Bs0) - Unused,
    <<Bs:Sz/bits,_/bits>> = Bs0,
    adjust_trailing_zeroes(Bs, Lb);
to_named_bitstring(Bs, Lb) when is_bitstring(Bs) ->
    adjust_trailing_zeroes(Bs, Lb);
to_named_bitstring(Val, Lb) ->
    %% Obsolete representations: list or integer. Optimize
    %% for correctness, not speed.
    adjust_trailing_zeroes(to_bitstring(Val), Lb).

is_default_bitstring(asn1_DEFAULT, _, _) ->
    true;
is_default_bitstring(Named, Named, _) ->
    true;
is_default_bitstring(Bs, _, Bs) ->
    true;
is_default_bitstring(Val, _, Def) when is_bitstring(Val) ->
    Sz = bit_size(Def),
    case Val of
	<<Def:Sz/bitstring,T/bitstring>> ->
	    NumZeroes = bit_size(T),
	    case T of
		<<0:NumZeroes>> -> true;
		_ -> false
	    end;
	_ ->
	    false
    end.

is_default_bitstring(asn1_DEFAULT, _, _, _, _) ->
    true;
is_default_bitstring({Unused,Bin}, V0, V1, V2, V3) when is_integer(Unused) ->
    %% Convert compact bitstring to a bitstring.
    Sz = bit_size(Bin) - Unused,
    <<Bs:Sz/bitstring,_:Unused>> = Bin,
    is_default_bitstring(Bs, V0, V1, V2, V3);
is_default_bitstring(Named, Named, _, _, _) ->
    true;
is_default_bitstring(Bs, _, Bs, _, _) ->
    true;
is_default_bitstring(List, _, _, List, _) ->
    true;
is_default_bitstring(Int, _, _, _, Int) ->
    true;
is_default_bitstring(Val, _, Def, _, _) when is_bitstring(Val) ->
    Sz = bit_size(Def),
    case Val of
	<<Def:Sz/bitstring,T/bitstring>> ->
	    NumZeroes = bit_size(T),
	    case T of
		<<0:NumZeroes>> -> true;
		_ -> false
	    end;
	_ ->
	    false
    end;
is_default_bitstring(Val, _, _, List, _) when is_list(Val) ->
    is_default_bitstring_list(List, Val);
is_default_bitstring(_, _, _, _, _) -> false.

extension_bitmap(Val, Pos, Limit) ->
    extension_bitmap(Val, Pos, Limit, 0).

open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) ->
    Bin.

legacy_open_type_to_binary({asn1_OPENTYPE,Bin}) when is_binary(Bin) ->
    Bin;
legacy_open_type_to_binary(Bin) when is_binary(Bin) ->
    Bin;
legacy_open_type_to_binary(List) when is_list(List) ->
    List.

%%%
%%% Internal functions.
%%%

decode_named_bit_string_1(Pos, [0|Bt], Names, Acc) ->
    decode_named_bit_string_1(Pos+1, Bt, Names, Acc);
decode_named_bit_string_1(Pos, [1|Bt], Names, Acc) ->
    case lists:keyfind(Pos, 2, Names) of
	{Name,_} ->
	    decode_named_bit_string_1(Pos+1, Bt, Names, [Name|Acc]);
	false ->
	    decode_named_bit_string_1(Pos+1, Bt, Names, [{bit,Pos}|Acc])
    end;
decode_named_bit_string_1(_Pos, [], _Names, Acc) ->
    lists:reverse(Acc).

decode_chars_16bit_1([H|T]) when H < 256 ->
    [H|decode_chars_16bit_1(T)];
decode_chars_16bit_1([H|T]) ->
    [{0,0,H bsr 8,H band 255}|decode_chars_16bit_1(T)];
decode_chars_16bit_1([]) -> [].

decode_big_chars_1([H|T]) when H < 256 ->
    [H|decode_big_chars_1(T)];
decode_big_chars_1([H|T]) ->
    [list_to_tuple(binary_to_list(<<H:32>>))|decode_big_chars_1(T)];
decode_big_chars_1([]) -> [].

dec_subidentifiers([H|T], Av, Al) when H >=16#80 ->
    dec_subidentifiers(T, (Av bsl 7) bor (H band 16#7F), Al);
dec_subidentifiers([H|T], Av, Al) ->
    dec_subidentifiers(T, 0, [(Av bsl 7) bor H|Al]);
dec_subidentifiers([], _Av, Al) ->
    lists:reverse(Al).

enc_char(C0, Lb, Tab) ->
    try	element(C0-Lb, Tab) of
	ill ->
	    illegal_char_error();
	C ->
	    C
    catch
	error:badarg ->
	    illegal_char_error()
    end.

enc_char_cm(C0, Lb, Limit) ->
    C = C0 - Lb,
    if
	0 =< C, C < Limit ->
	    C;
	true ->
	    illegal_char_error()
    end.

illegal_char_error() ->
    error({error,{asn1,"value forbidden by FROM constraint"}}).

encode_fragmented_1(Bin, Unit, N) ->
    SegSz = Unit * N * ?'16K',
    case Bin of
	<<B:SegSz/bitstring,T/bitstring>> ->
	    [<<3:2,N:6>>,B|encode_fragmented_1(T, Unit, N)];
	_ when N > 1 ->
	    encode_fragmented_1(Bin, Unit, N-1);
	_ ->
	    case bit_size(Bin) div Unit of
		Len when Len < 128 ->
		    [Len,Bin];
		Len when Len < 16384 ->
		    [<<2:2,Len:14>>,Bin]
	    end
    end.

%% E1 = 0|1|2 and (E2 < 40 when E1 = 0|1)
e_object_identifier([E1,E2|Tail]) when E1 >= 0, E1 < 2, E2 < 40; E1 =:= 2 ->
    Head = 40*E1 + E2,
    e_object_elements([Head|Tail], []);
e_object_identifier([_,_|_Tail]=Oid) ->
    exit({error,{asn1,{'illegal_value',Oid}}}).

e_object_elements([], Acc) ->
    lists:reverse(Acc);
e_object_elements([H|T], Acc) ->
    e_object_elements(T, [e_object_element(H)|Acc]).

e_object_element(Num) when Num < 128 ->
    [Num];
e_object_element(Num) ->
    [e_o_e(Num bsr 7)|[Num band 2#1111111]].

e_o_e(Num) when Num < 128 ->
    Num bor 2#10000000;
e_o_e(Num) ->
    [e_o_e(Num bsr 7)|[(Num band 2#1111111) bor 2#10000000]].

enint(-1, [B1|T]) when B1 > 127 ->
    [B1|T];
enint(N, Acc) ->
    enint(N bsr 8, [N band 16#ff|Acc]).

diff([H|T], Prev) ->
    [H-Prev|diff(T, H)];
diff([], _) -> [].

diff([H|T], Prev, Last) ->
    [{1,H-Prev}|diff(T, H, Last)];
diff([], Prev, Last) when Last >= Prev ->
    [{0,Last-Prev}];
diff([], _, _) -> [].

int_to_bitlist(0) -> [];
int_to_bitlist(Int) -> [Int band 1|int_to_bitlist(Int bsr 1)].

adjust_size(Bs, Lb) ->
    case bit_size(Bs) of
	Sz when Sz < Lb ->
	    <<Bs:Sz/bits,0:(Lb-Sz)>>;
	_ ->
	    Bs
    end.

adjust_trailing_zeroes(Bs0, Lb) ->
    case bit_size(Bs0) of
	Sz when Sz < Lb ->
	    %% Too short - pad with zeroes.
	    <<Bs0:Sz/bits,0:(Lb-Sz)>>;
	Lb ->
	    %% Exactly the right size - nothing to do.
	    Bs0;
	_ ->
	    %% Longer than the lower bound - drop trailing zeroes.
	    <<_:Lb/bits,Tail/bits>> = Bs0,
	    Sz = Lb + bit_size(bs_drop_trailing_zeroes(Tail)),
	    <<Bs:Sz/bits,_/bits>> = Bs0,
	    Bs
    end.

bs_drop_trailing_zeroes(Bs) ->
    bs_drop_trailing_zeroes(Bs, bit_size(Bs)).

bs_drop_trailing_zeroes(Bs, 0) ->
    Bs;
bs_drop_trailing_zeroes(Bs0, Sz0) when Sz0 < 8 ->
    <<Byte:Sz0>> = Bs0,
    Sz = Sz0 - ntz(Byte),
    <<Bs:Sz/bits,_/bits>> = Bs0,
    Bs;
bs_drop_trailing_zeroes(Bs0, Sz0) ->
    Sz1 = Sz0 - 8,
    <<Bs1:Sz1/bits,Byte:8>> = Bs0,
    case ntz(Byte) of
	8 ->
	    bs_drop_trailing_zeroes(Bs1, Sz1);
	Ntz ->
	    Sz = Sz0 - Ntz,
	    <<Bs:Sz/bits,_:Ntz/bits>> = Bs0,
	    Bs
    end.

%% ntz(Byte) -> Number of trailing zeroes.
ntz(Byte) ->
    %% The table was calculated like this:
    %%   NTZ = fun (B, N, NTZ) when B band 1 =:= 0 -> NTZ(B bsr 1, N+1, NTZ); (_, N, _) -> N end.
    %%   io:format("~w\n", [list_to_tuple([NTZ(B+256, 0, NTZ) || B <- lists:seq(0, 255)])]).
    T = {8,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
	 4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0},
    element(Byte+1, T).

is_default_bitstring_list([H|Def], [H|Val]) ->
    is_default_bitstring_list(Def, Val);
is_default_bitstring_list([], []) ->
    true;
is_default_bitstring_list([], [_|_]=Val) ->
    lists:all(fun(0) -> true;
		 (_) -> false
	      end, Val);
is_default_bitstring_list(_, _) -> false.

extension_bitmap(_Val, Pos, Limit, Acc) when Pos >= Limit ->
    Acc;
extension_bitmap(Val, Pos, Limit, Acc) ->
    Bit = case element(Pos, Val) of
	      asn1_NOVALUE -> 0;
	      _ -> 1
	  end,
    extension_bitmap(Val, Pos+1, Limit, (Acc bsl 1) bor Bit).