%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 2012. 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% %% -module(asn1rtt_per). -export([setext/1, fixextensions/2, skipextensions/3, getbit/1, getchoice/3, set_choice/3,encode_integer/2, encode_small_number/1, encode_constrained_number/2, encode_length/1, encode_length/2, encode_bit_string/3, encode_object_identifier/1, decode_object_identifier/1, encode_relative_oid/1, decode_relative_oid/1, complete/1, encode_open_type/1, encode_GeneralString/2, decode_GeneralString/2, encode_GraphicString/2, decode_GraphicString/2, encode_TeletexString/2, decode_TeletexString/2, encode_VideotexString/2, decode_VideotexString/2, encode_ObjectDescriptor/2, decode_ObjectDescriptor/1, encode_UTF8String/1,decode_UTF8String/1, encode_octet_string/3, encode_known_multiplier_string/4, decode_known_multiplier_string/5, octets_to_complete/2]). -define('16K',16384). -define('32K',32768). -define('64K',65536). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% setext(true|false) -> CompleteList %% setext(false) -> [0]; setext(true) -> [1]. fixextensions({ext,ExtPos,ExtNum},Val) -> case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of 0 -> []; ExtBits -> [encode_small_length(ExtNum)|pre_complete_bits(ExtNum,ExtBits)] end. fixextensions(Pos,MaxPos,_,Acc) when Pos >= MaxPos -> Acc; fixextensions(Pos,ExtPos,Val,Acc) -> Bit = case catch(element(Pos+1,Val)) of asn1_NOVALUE -> 0; asn1_NOEXTVALUE -> 0; {'EXIT',_} -> 0; _ -> 1 end, fixextensions(Pos+1,ExtPos,Val,(Acc bsl 1)+Bit). skipextensions(Bytes0, Nr, ExtensionBitstr) when is_bitstring(ExtensionBitstr) -> Prev = Nr - 1, case ExtensionBitstr of <<_:Prev,1:1,_/bitstring>> -> {Len,Bytes1} = decode_length(Bytes0, undefined), <<_:Len/binary,Bytes2/bitstring>> = Bytes1, skipextensions(Bytes2, Nr+1, ExtensionBitstr); <<_:Prev,0:1,_/bitstring>> -> skipextensions(Bytes0, Nr+1, ExtensionBitstr); _ -> Bytes0 end. getchoice(Bytes, 1, 0) -> % only 1 alternative is not encoded {0,Bytes}; getchoice(Bytes, _, 1) -> decode_small_number(Bytes); getchoice(Bytes, NumChoices, 0) -> decode_constrained_number(Bytes, {0,NumChoices-1}). getbit(Buffer) -> <> = Buffer, {B,Rest}. getbits(Buffer, Num) when is_bitstring(Buffer) -> <> = Buffer, {Bs,Rest}. align(Bin) when is_binary(Bin) -> Bin; align(BitStr) when is_bitstring(BitStr) -> AlignBits = bit_size(BitStr) rem 8, <<_:AlignBits,Rest/binary>> = BitStr, Rest. %% First align buffer, then pick the first Num octets. %% Returns octets as an integer with bit significance as in buffer. getoctets(Buffer, Num) when is_binary(Buffer) -> <> = Buffer, {Val,RestBin}; getoctets(Buffer, Num) when is_bitstring(Buffer) -> AlignBits = bit_size(Buffer) rem 8, <<_:AlignBits,Val:Num/integer-unit:8,RestBin/binary>> = Buffer, {Val,RestBin}. %% First align buffer, then pick the first Num octets. %% Returns octets as a binary getoctets_as_bin(Bin,Num) when is_binary(Bin) -> <> = Bin, {Octets,RestBin}; getoctets_as_bin(Bin,Num) when is_bitstring(Bin) -> AlignBits = bit_size(Bin) rem 8, <<_:AlignBits,Val:Num/binary,RestBin/binary>> = Bin, {Val,RestBin}. %% same as above but returns octets as a List getoctets_as_list(Buffer,Num) -> {Bin,Buffer2} = getoctets_as_bin(Buffer,Num), {binary_to_list(Bin),Buffer2}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% set_choice(Alt,Choices,Altnum) -> ListofBitSettings %% Alt = atom() %% Altnum = integer() | {integer(),integer()}% number of alternatives %% Choices = [atom()] | {[atom()],[atom()]} %% When Choices is a tuple the first list is the Rootset and the %% second is the Extensions and then Altnum must also be a tuple with the %% lengths of the 2 lists %% set_choice(Alt,{L1,L2},{Len1,_Len2}) -> case set_choice_tag(Alt,L1) of N when is_integer(N), Len1 > 1 -> [0, % the value is in the root set encode_constrained_number({0,Len1-1},N)]; N when is_integer(N) -> [0]; % no encoding if only 0 or 1 alternative false -> [1, % extension value case set_choice_tag(Alt, L2) of N2 when is_integer(N2) -> encode_small_number(N2); false -> unknown_choice_alt end] end; set_choice(Alt, L, Len) -> case set_choice_tag(Alt, L) of N when is_integer(N), Len > 1 -> encode_constrained_number({0,Len-1},N); N when is_integer(N) -> []; % no encoding if only 0 or 1 alternative false -> [unknown_choice_alt] end. set_choice_tag(Alt,Choices) -> set_choice_tag(Alt,Choices,0). set_choice_tag(Alt,[Alt|_Rest],Tag) -> Tag; set_choice_tag(Alt,[_H|Rest],Tag) -> set_choice_tag(Alt,Rest,Tag+1); set_choice_tag(_Alt,[],_Tag) -> false. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_open_type(Constraint, Value) -> CompleteList %% Value = list of bytes of an already encoded value (the list must be flat) %% | binary %% Contraint = not used in this version %% encode_open_type(Val) when is_list(Val) -> Bin = list_to_binary(Val), case byte_size(Bin) of Size when Size > 255 -> [encode_length(Size),21,<>,Bin]; Size -> [encode_length(Size),20,Size,Bin] end; encode_open_type(Val) when is_binary(Val) -> case byte_size(Val) of Size when Size > 255 -> [encode_length(Size),21,<>,Val]; % octets implies align Size -> [encode_length(Size),20,Size,Val] end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_integer(Constraint, Value) -> CompleteList %% encode_integer([{Rc,_Ec}],Val) when is_tuple(Rc) -> try [0|encode_integer([Rc], Val)] catch _:{error,{asn1,_}} -> [1|encode_unconstrained_number(Val)] end; encode_integer([], Val) -> encode_unconstrained_number(Val); %% The constraint is the effective constraint, and in this case is a number encode_integer([{'SingleValue',V}], V) -> []; encode_integer([{'ValueRange',{Lb,Ub}=VR,Range,PreEnc}],Val) when Val >= Lb, Ub >= Val -> %% this case when NamedNumberList encode_constrained_number(VR, Range, PreEnc, Val); encode_integer([{'ValueRange',{Lb,'MAX'}}], Val) -> encode_semi_constrained_number(Lb, Val); encode_integer([{'ValueRange',{'MIN',_}}], Val) -> encode_unconstrained_number(Val); encode_integer([{'ValueRange',VR={_Lb,_Ub}}], Val) -> encode_constrained_number(VR, Val); encode_integer(_,Val) -> exit({error,{asn1,{illegal_value,Val}}}). %% X.691:10.6 Encoding of a normally small non-negative whole number %% Use this for encoding of CHOICE index if there is an extension marker in %% the CHOICE encode_small_number(Val) when Val < 64 -> [10,7,Val]; encode_small_number(Val) -> [1|encode_semi_constrained_number(0, Val)]. decode_small_number(Bytes) -> {Bit,Bytes2} = getbit(Bytes), case Bit of 0 -> getbits(Bytes2, 6); 1 -> decode_semi_constrained_number(Bytes2) end. %% X.691:10.7 Encoding of a semi-constrained whole number encode_semi_constrained_number(Lb, Val) -> Val2 = Val - Lb, Oct = eint_positive(Val2), Len = length(Oct), if Len < 128 -> [20,Len+1,Len|Oct]; Len < 256 -> [encode_length(Len),20,Len|Oct]; true -> [encode_length(Len),21,<>|Oct] end. decode_semi_constrained_number(Bytes) -> {Len,Bytes2} = decode_length(Bytes, undefined), getoctets(Bytes2, Len). encode_constrained_number({Lb,_Ub},_Range,{bits,N},Val) -> Val2 = Val-Lb, [10,N,Val2]; encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) when N < 256-> %% N is 8 or 16 (1 or 2 octets) Val2 = Val-Lb, [20,N,Val2]; encode_constrained_number({Lb,_Ub},_Range,{octets,N},Val) -> % N>255 %% N is 8 or 16 (1 or 2 octets) Val2 = Val-Lb, [21,<>,Val2]; encode_constrained_number({Lb,_Ub},Range,_,Val) -> Val2 = Val-Lb, if Range =< 16#1000000 -> % max 3 octets Octs = eint_positive(Val2), L = length(Octs), [encode_length({1,3},L),[20,L,Octs]]; Range =< 16#100000000 -> % max 4 octets Octs = eint_positive(Val2), L = length(Octs), [encode_length({1,4},L),[20,L,Octs]]; Range =< 16#10000000000 -> % max 5 octets Octs = eint_positive(Val2), L = length(Octs), [encode_length({1,5},L),[20,L,Octs]]; true -> exit({not_supported,{integer_range,Range}}) end. encode_constrained_number({Lb,Ub}, Val) when Val >= Lb, Ub >= Val -> Range = Ub - Lb + 1, Val2 = Val - Lb, if Range == 1 -> []; Range == 2 -> [Val2]; Range =< 4 -> [10,2,Val2]; Range =< 8 -> [10,3,Val2]; Range =< 16 -> [10,4,Val2]; Range =< 32 -> [10,5,Val2]; Range =< 64 -> [10,6,Val2]; Range =< 128 -> [10,7,Val2]; Range =< 255 -> [10,8,Val2]; Range =< 256 -> [20,1,Val2]; Range =< 65536 -> [20,2,<>]; Range =< (1 bsl (255*8)) -> Octs = binary:encode_unsigned(Val2), RangeOcts = binary:encode_unsigned(Range - 1), OctsLen = byte_size(Octs), RangeOctsLen = byte_size(RangeOcts), LengthBitsNeeded = minimum_bits(RangeOctsLen - 1), [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs]; true -> exit({not_supported,{integer_range,Range}}) end; encode_constrained_number({_,_},Val) -> exit({error,{asn1,{illegal_value,Val}}}). decode_constrained_number(Buffer,VR={Lb,Ub}) -> Range = Ub - Lb + 1, decode_constrained_number(Buffer,VR,Range). decode_constrained_number(Buffer,{Lb,_Ub},Range) -> % Val2 = Val - Lb, {Val,Remain} = if Range == 1 -> {0,Buffer}; Range == 2 -> getbits(Buffer,1); Range =< 4 -> getbits(Buffer,2); Range =< 8 -> getbits(Buffer,3); Range =< 16 -> getbits(Buffer,4); Range =< 32 -> getbits(Buffer,5); Range =< 64 -> getbits(Buffer,6); Range =< 128 -> getbits(Buffer,7); Range =< 255 -> getbits(Buffer,8); Range =< 256 -> getoctets(Buffer,1); Range =< 65536 -> getoctets(Buffer,2); Range =< (1 bsl (255*8)) -> OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), RangeOctLen = length(OList), {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), {Octs, RestBytes} = getoctets_as_bin(Bytes, Len), {binary:decode_unsigned(Octs), RestBytes}; true -> exit({not_supported,{integer_range,Range}}) end, {Val+Lb,Remain}. %% For some reason the minimum bits needed in the length field in %% the encoding of constrained whole numbers must always be at least 2? minimum_bits(N) when N < 4 -> 2; minimum_bits(N) when N < 8 -> 3; minimum_bits(N) when N < 16 -> 4; minimum_bits(N) when N < 32 -> 5; minimum_bits(N) when N < 64 -> 6; minimum_bits(N) when N < 128 -> 7; minimum_bits(_N) -> 8. %% X.691:10.8 Encoding of an unconstrained whole number encode_unconstrained_number(Val) -> Oct = if Val >= 0 -> eint(Val, []); true -> enint(Val, []) end, Len = length(Oct), if Len < 128 -> [20,Len + 1,Len|Oct]; Len < 256 -> [20,Len + 2,<<2:2,Len:14>>|Oct]; true -> [encode_length(Len),21,<>|Oct] end. %% used for positive Values which don't need a sign bit %% returns a list eint_positive(Val) -> case eint(Val,[]) of [0,B1|T] -> [B1|T]; T -> T end. eint(0, [B|Acc]) when B < 128 -> [B|Acc]; eint(N, Acc) -> eint(N bsr 8, [N band 16#ff| Acc]). enint(-1, [B1|T]) when B1 > 127 -> [B1|T]; enint(N, Acc) -> enint(N bsr 8, [N band 16#ff|Acc]). %% X.691:10.9 Encoding of a length determinant %%encode_small_length(undefined,Len) -> % null means no UpperBound %% encode_small_number(Len). %% X.691:10.9.3.5 %% X.691:10.9.3.7 encode_length(Len) -> % unconstrained if Len < 128 -> [20,1,Len]; Len < 16384 -> <<20,2,2:2,Len:14>>; true -> % should be able to endode length >= 16384 i.e. fragmented length exit({error,{asn1,{encode_length,{nyi,above_16k}}}}) end. encode_length(undefined, Len) -> % un-constrained encode_length(Len); encode_length({0,'MAX'},Len) -> encode_length(undefined,Len); encode_length({Lb,Ub}=Vr, Len) when Ub =< 65535 ,Lb >= 0 -> % constrained encode_constrained_number(Vr,Len); encode_length({Lb,_Ub}, Len) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 encode_length(Len); encode_length({{Lb,Ub}=Vr,Ext}, Len) when Ub =< 65535 ,Lb >= 0,Len= %% constrained extensible [0|encode_constrained_number(Vr,Len)]; encode_length({{Lb,_},Ext},Len) when is_list(Ext) -> [1|encode_semi_constrained_number(Lb, Len)]; encode_length(SingleValue, _Len) when is_integer(SingleValue) -> []. %% X.691 10.9.3.4 (only used for length of bitmap that prefixes extension %% additions in a sequence or set encode_small_length(Len) when Len =< 64 -> [10,7,Len-1]; encode_small_length(Len) -> [1,encode_length(Len)]. decode_length(Buffer, undefined) -> % un-constrained case align(Buffer) of <<0:1,Oct:7,Rest/binary>> -> {Oct,Rest}; <<2:2,Val:14,Rest/binary>> -> {Val,Rest}; <<3:2,_Val:14,_Rest/binary>> -> %% this case should be fixed exit({error,{asn1,{decode_length,{nyi,above_16k}}}}) end; decode_length(Buffer, {Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained decode_constrained_number(Buffer, {Lb,Ub}); decode_length(Buffer, {Lb,_Ub}) when is_integer(Lb), Lb >= 0 -> % Ub > 65535 decode_length(Buffer,undefined); decode_length(Buffer, {{Lb,Ub},Ext}) when is_list(Ext) -> case getbit(Buffer) of {0,Buffer2} -> decode_length(Buffer2, {Lb,Ub}); {1,Buffer2} -> decode_length(Buffer2, undefined) end; %When does this case occur with {_,_Lb,Ub} ?? % X.691:10.9.3.5 decode_length(Bin, {_,_Lb,_Ub}) -> % Unconstrained or large Ub NOTE! this case does not cover case when Ub > 65535 case Bin of <<0:1,Val:7,Rest/bitstring>> -> {Val,Rest}; _ -> case align(Bin) of <<2:2,Val:14,Rest/binary>> -> {Val,Rest}; <<3:2,_:14,_Rest/binary>> -> exit({error,{asn1,{decode_length,{nyi,length_above_64K}}}}) end end; decode_length(Buffer, SingleValue) when is_integer(SingleValue) -> {SingleValue,Buffer}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% bitstring NamedBitList %% Val can be of: %% - [identifiers] where only named identifers are set to one, %% the Constraint must then have some information of the %% bitlength. %% - [list of ones and zeroes] all bits %% - integer value representing the bitlist %% C is constraint Len, only valid when identifiers %% when the value is a list of {Unused,BinBits}, where %% Unused = integer(), %% BinBits = binary(). encode_bit_string(C, {Unused,BinBits}=Bin, NamedBitList) when is_integer(Unused), is_binary(BinBits) -> encode_bin_bit_string(C,Bin,NamedBitList); %% when the value is a list of named bits encode_bit_string(C, LoNB=[FirstVal | _RestVal], NamedBitList) when is_atom(FirstVal) -> ToSetPos = get_all_bitposes(LoNB, NamedBitList, []), BitList = make_and_set_list(ToSetPos,0), encode_bit_string(C,BitList,NamedBitList);% consider the constraint encode_bit_string(C, BL=[{bit,_} | _RestVal], NamedBitList) -> ToSetPos = get_all_bitposes(BL, NamedBitList, []), BitList = make_and_set_list(ToSetPos,0), encode_bit_string(C,BitList,NamedBitList); %% when the value is a list of ones and zeroes encode_bit_string(Int, BitListValue, _) when is_list(BitListValue),is_integer(Int),Int =< 16 -> %% The type is constrained by a single value size constraint %% range_check(Int,length(BitListValue)), [40,Int,length(BitListValue),BitListValue]; encode_bit_string(Int, BitListValue, _) when is_list(BitListValue),is_integer(Int), Int =< 255 -> %% The type is constrained by a single value size constraint %% range_check(Int,length(BitListValue)), [2,40,Int,length(BitListValue),BitListValue]; encode_bit_string(Int, BitListValue, _) when is_list(BitListValue),is_integer(Int), Int < ?'64K' -> {Code,DesiredLength,Length} = case length(BitListValue) of B1 when B1 > Int -> exit({error,{'BIT_STRING_length_greater_than_SIZE', Int,BitListValue}}); B1 when B1 =< 255,Int =< 255 -> {40,Int,B1}; B1 when B1 =< 255 -> {42,<>,B1}; B1 -> {43,<>,<>} end, %% The type is constrained by a single value size constraint [2,Code,DesiredLength,Length,BitListValue]; encode_bit_string(no, BitListValue,[]) when is_list(BitListValue) -> [encode_length(length(BitListValue)), 2|BitListValue]; encode_bit_string({{Fix,Fix},Ext}, BitListValue,[]) when is_integer(Fix), is_list(Ext) -> case length(BitListValue) of Len when Len =< Fix -> [0|encode_bit_string(Fix, BitListValue, [])]; _ -> [1|encode_bit_string(no, BitListValue, [])] end; encode_bit_string(C, BitListValue,[]) when is_list(BitListValue) -> [encode_length(C, length(BitListValue)), 2|BitListValue]; encode_bit_string(no, BitListValue,_NamedBitList) when is_list(BitListValue) -> %% this case with an unconstrained BIT STRING can be made more efficient %% if the complete driver can take a special code so the length field %% is encoded there. NewBitLVal = lists:reverse(lists:dropwhile(fun(0)->true;(1)->false end, lists:reverse(BitListValue))), [encode_length(length(NewBitLVal)),2|NewBitLVal]; encode_bit_string({{Fix,Fix},Ext}, BitListValue, NamedBitList) when is_integer(Fix), is_list(Ext) -> case length(BitListValue) of Len when Len =< Fix -> [0|encode_bit_string(Fix, BitListValue, NamedBitList)]; _ -> [1|encode_bit_string(no, BitListValue, NamedBitList)] end; encode_bit_string(C, BitListValue, _NamedBitList) when is_list(BitListValue) -> % C = {_,'MAX'} NewBitLVal = bit_string_trailing_zeros(BitListValue, C), [encode_length(C, length(NewBitLVal)),2|NewBitLVal]; %% when the value is an integer encode_bit_string(C, IntegerVal, NamedBitList) when is_integer(IntegerVal)-> BitList = int_to_bitlist(IntegerVal), encode_bit_string(C,BitList,NamedBitList). bit_string_trailing_zeros(BitList,C) when is_integer(C) -> bit_string_trailing_zeros1(BitList,C,C); bit_string_trailing_zeros(BitList,{Lb,Ub}) when is_integer(Lb) -> bit_string_trailing_zeros1(BitList,Lb,Ub); bit_string_trailing_zeros(BitList,{{Lb,Ub},_}) when is_integer(Lb) -> bit_string_trailing_zeros1(BitList,Lb,Ub); bit_string_trailing_zeros(BitList,_) -> BitList. bit_string_trailing_zeros1(BitList,Lb,Ub) -> case length(BitList) of Lb -> BitList; B when B < Lb -> BitList++lists:duplicate(Lb-B, 0); D -> F = fun(L,LB,LB,_,_)->lists:reverse(L); ([0|R],L1,LB,UB,Fun)->Fun(R,L1-1,LB,UB,Fun); (L,L1,_,UB,_)when L1 =< UB -> lists:reverse(L); (_,_L1,_,_,_) ->exit({error,{list_length_BIT_STRING, BitList}}) end, F(lists:reverse(BitList),D,Lb,Ub,F) end. %% encode_bin_bit_string/3, when value is a tuple of Unused and BinBits. %% Unused = integer(),i.e. number unused bits in least sign. byte of %% BinBits = binary(). encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList) when is_integer(C),C=<16 -> range_check(C, bit_size(BinBits) - Unused), [45,C,size(BinBits),BinBits]; encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList) when is_integer(C), C =< 255 -> range_check(C, bit_size(BinBits) - Unused), [2,45,C,size(BinBits),BinBits]; encode_bin_bit_string(C, {Unused,BinBits}, _NamedBitList) when is_integer(C), C =< 65535 -> range_check(C, bit_size(BinBits) - Unused), case byte_size(BinBits) of Size when Size =< 255 -> [2,46,<>,Size,BinBits]; Size -> [2,47,<>,<>,BinBits] end; encode_bin_bit_string(C,UnusedAndBin={_,_},NamedBitList) -> {Unused1,Bin1} = %% removes all trailing bits if NamedBitList is not empty remove_trailing_bin(NamedBitList,UnusedAndBin), case C of {Lb,Ub} when is_integer(Lb),is_integer(Ub) -> Size = byte_size(Bin1), [encode_length({Lb,Ub}, Size*8 - Unused1), 2,octets_unused_to_complete(Unused1,Size,Bin1)]; no -> Size = byte_size(Bin1), [encode_length(Size*8 - Unused1), 2|octets_unused_to_complete(Unused1, Size, Bin1)]; {{Fix,Fix},Ext} when is_integer(Fix),is_list(Ext) -> case byte_size(Bin1)*8 - Unused1 of Size when Size =< Fix -> [0|encode_bin_bit_string(Fix,UnusedAndBin,NamedBitList)]; _Size -> [1|encode_bin_bit_string(no,UnusedAndBin,NamedBitList)] end; Sc -> Size = byte_size(Bin1), [encode_length(Sc, Size*8 - Unused1), 2|octets_unused_to_complete(Unused1,Size,Bin1)] end. range_check(C,C) when is_integer(C) -> ok; range_check(C1,C2) when is_integer(C1) -> exit({error,{asn1,{bit_string_out_of_range,{C1,C2}}}}). remove_trailing_bin([], {Unused,Bin}) -> {Unused,Bin}; remove_trailing_bin(_NamedNumberList,{_Unused,<<>>}) -> {0,<<>>}; remove_trailing_bin(NamedNumberList, {_Unused,Bin}) -> Size = byte_size(Bin)-1, <> = Bin, %% clear the Unused bits to be sure Unused1 = trailingZeroesInNibble(LastByte band 15), Unused2 = case Unused1 of 4 -> 4 + trailingZeroesInNibble(LastByte bsr 4); _ -> Unused1 end, case Unused2 of 8 -> remove_trailing_bin(NamedNumberList,{0,Bfront}); _ -> {Unused2,Bin} end. trailingZeroesInNibble(0) -> 4; trailingZeroesInNibble(1) -> 0; trailingZeroesInNibble(2) -> 1; trailingZeroesInNibble(3) -> 0; trailingZeroesInNibble(4) -> 2; trailingZeroesInNibble(5) -> 0; trailingZeroesInNibble(6) -> 1; trailingZeroesInNibble(7) -> 0; trailingZeroesInNibble(8) -> 3; trailingZeroesInNibble(9) -> 0; trailingZeroesInNibble(10) -> 1; trailingZeroesInNibble(11) -> 0; trailingZeroesInNibble(12) -> %#1100 2; trailingZeroesInNibble(13) -> 0; trailingZeroesInNibble(14) -> 1; trailingZeroesInNibble(15) -> 0. %%%%%%%%%%%%%%% %% int_to_bitlist(Int) when is_integer(Int), Int > 0 -> [Int band 1 | int_to_bitlist(Int bsr 1)]; int_to_bitlist(0) -> []. %%%%%%%%%%%%%%%%%% %% get_all_bitposes([list of named bits to set], named_bit_db, []) -> %% [sorted_list_of_bitpositions_to_set] get_all_bitposes([{bit,ValPos}|Rest], NamedBitList, Ack) -> get_all_bitposes(Rest, NamedBitList, [ValPos | Ack ]); get_all_bitposes([Val | Rest], NamedBitList, Ack) -> case lists:keyfind(Val, 1, NamedBitList) of {_ValName, ValPos} -> get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]); false -> exit({error,{asn1, {bitstring_namedbit, Val}}}) end; get_all_bitposes([], _NamedBitList, Ack) -> lists:sort(Ack). %%%%%%%%%%%%%%%%%% %% make_and_set_list([list of positions to set to 1])-> %% returns list with all in SetPos set. %% in positioning in list the first element is 0, the second 1 etc.., but %% make_and_set_list([XPos|SetPos], XPos) -> [1 | make_and_set_list(SetPos, XPos + 1)]; make_and_set_list([Pos|SetPos], XPos) -> [0 | make_and_set_list([Pos | SetPos], XPos + 1)]; make_and_set_list([], _) -> []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% X.691:16 %% encode_octet_string(Constraint,ExtensionMarker,Val) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% encode_octet_string(_C, true, _Val) -> exit({error,{asn1,{'not_supported',extensionmarker}}}); encode_octet_string({_,_}=SZ, false, Val) -> Len = length(Val), try [encode_length(SZ, Len),2|octets_to_complete(Len, Val)] catch exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; encode_octet_string(SZ, false, Val) when is_list(SZ) -> Len = length(Val), try [encode_length({hd(SZ),lists:max(SZ)},Len),2| octets_to_complete(Len,Val)] catch exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; encode_octet_string(Sv, false, Val) when is_integer(Sv) -> encode_fragmented_octet_string(Val); encode_octet_string(no, false, Val) -> Len = length(Val), try [encode_length(Len),2|octets_to_complete(Len, Val)] catch exit:{error,{asn1,{encode_length,_}}} -> encode_fragmented_octet_string(Val) end; encode_octet_string(C, _, _) -> exit({error,{not_implemented,C}}). encode_fragmented_octet_string(Val) -> Bin = iolist_to_binary(Val), efos_1(Bin). efos_1(<>) -> [20,1,<<3:2,4:6>>, octets_to_complete(16#C000, B1), octets_to_complete(16#4000, B2)|efos_1(T)]; efos_1(<>) -> [20,1,<<3:2,3:6>>,octets_to_complete(16#C000, B)|efos_1(T)]; efos_1(<>) -> [20,1,<<3:2,2:6>>,octets_to_complete(16#8000, B)|efos_1(T)]; efos_1(<>) -> [20,1,<<3:2,1:6>>,octets_to_complete(16#4000, B)|efos_1(T)]; efos_1(<<>>) -> [20,1,0]; efos_1(<>) -> Len = byte_size(B), [encode_length(Len)|octets_to_complete(Len, B)]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Restricted char string types %% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString) %% X.691:26 and X.680:34-36 encode_restricted_string(Val) when is_list(Val)-> Len = length(Val), [encode_length(Len)|octets_to_complete(Len, Val)]. encode_known_multiplier_string(SizeC, NumBits, CharOutTab, Val) -> Result = chars_encode2(Val, NumBits, CharOutTab), case SizeC of Ub when is_integer(Ub), Ub*NumBits =< 16 -> Result; Ub when is_integer(Ub), Ub =<65535 -> % fixed length [2,Result]; {Ub,Lb} -> [encode_length({Ub,Lb},length(Val)),2,Result]; no -> [encode_length(length(Val)),2,Result] end. decode_restricted_string(Bytes,aligned) -> {Len,Bytes2} = decode_length(Bytes,undefined), getoctets_as_list(Bytes2,Len). decode_known_multiplier_string(StringType,SizeC,NumBits,CharInTab,Bytes) -> case SizeC of Ub when is_integer(Ub), Ub*NumBits =< 16 -> chars_decode(Bytes,NumBits,StringType,CharInTab,Ub); Ub when is_integer(Ub),Ub =<65535 -> % fixed length Bytes1 = align(Bytes), chars_decode(Bytes1,NumBits,StringType,CharInTab,Ub); Vl when is_list(Vl) -> {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,CharInTab,Len); no -> {Len,Bytes1} = decode_length(Bytes,undefined), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,CharInTab,Len); {Lb,Ub}-> {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,CharInTab,Len) end. encode_GeneralString(_C,Val) -> encode_restricted_string(Val). decode_GeneralString(Bytes,_C) -> decode_restricted_string(Bytes,aligned). encode_GraphicString(_C,Val) -> encode_restricted_string(Val). decode_GraphicString(Bytes,_C) -> decode_restricted_string(Bytes,aligned). encode_ObjectDescriptor(_C,Val) -> encode_restricted_string(Val). decode_ObjectDescriptor(Bytes) -> decode_restricted_string(Bytes,aligned). encode_TeletexString(_C,Val) -> % equivalent with T61String encode_restricted_string(Val). decode_TeletexString(Bytes,_C) -> decode_restricted_string(Bytes,aligned). encode_VideotexString(_C,Val) -> encode_restricted_string(Val). decode_VideotexString(Bytes,_C) -> decode_restricted_string(Bytes,aligned). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes} %% getBMPChars(<>, 0, Acc) -> {lists:reverse(Acc),T}; getBMPChars(<<0,O2,Bytes1/bitstring>>, Len, Acc) -> getBMPChars(Bytes1,Len-1,[O2|Acc]); getBMPChars(<>, Len, Acc) -> getBMPChars(Bytes1,Len-1,[{0,0,O1,O2}|Acc]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% chars_encode(C,StringType,Value) -> ValueList %% %% encodes chars according to the per rules taking the constraint %% PermittedAlphabet into account. %% %% This function only encodes the value part and NOT the length. chars_encode2([H|T],NumBits,T1={Min,Max,notab}) when H =< Max, H >= Min -> [pre_complete_bits(NumBits,H-Min)|chars_encode2(T,NumBits,T1)]; chars_encode2([H|T],NumBits,T1={Min,Max,Tab}) when H =< Max, H >= Min -> [pre_complete_bits(NumBits,exit_if_false(H,element(H-Min+1,Tab)))| chars_encode2(T,NumBits,T1)]; chars_encode2([{A,B,C,D}|T],NumBits,T1={Min,_Max,notab}) -> %% no value range check here (ought to be, but very expensive) [pre_complete_bits(NumBits, ((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min)| chars_encode2(T,NumBits,T1)]; chars_encode2([H={A,B,C,D}|T],NumBits,{Min,Max,Tab}) -> %% no value range check here (ought to be, but very expensive) [pre_complete_bits(NumBits,exit_if_false(H,element(((((((A bsl 8)+B) bsl 8)+C) bsl 8)+D)-Min,Tab)))|chars_encode2(T,NumBits,{Min,Max,notab})]; chars_encode2([H|_T],_NumBits,{_Min,_Max,_Tab}) -> exit({error,{asn1,{illegal_char_value,H}}}); chars_encode2([],_,_) -> []. exit_if_false(V,false)-> exit({error,{asn1,{"illegal value according to Permitted alphabet constraint",V}}}); exit_if_false(_,V) ->V. pre_complete_bits(NumBits,Val) when NumBits =< 8 -> [10,NumBits,Val]; pre_complete_bits(NumBits,Val) when NumBits =< 16 -> [10,NumBits-8,Val bsr 8,10,8,(Val band 255)]; pre_complete_bits(NumBits,Val) when NumBits =< 2040 -> % 255 * 8 Unused = (8 - (NumBits rem 8)) rem 8, Len = NumBits + Unused, [30,Unused,Len div 8,<<(Val bsl Unused):Len>>]. chars_decode(Bytes,_,'BMPString',_,Len) -> getBMPChars(Bytes,Len,[]); chars_decode(Bytes,NumBits,_StringType,CharInTab,Len) -> chars_decode2(Bytes,CharInTab,NumBits,Len). chars_decode2(Bytes,CharInTab,NumBits,Len) -> chars_decode2(Bytes,CharInTab,NumBits,Len,[]). chars_decode2(Bytes,_CharInTab,_NumBits,0,Acc) -> {lists:reverse(Acc),Bytes}; chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) when NumBits > 8 -> {Char,Bytes2} = getbits(Bytes,NumBits), Result = if Char < 256 -> Char; true -> list_to_tuple(binary_to_list(<>)) end, chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Result|Acc]); chars_decode2(Bytes,{Min,Max,notab},NumBits,Len,Acc) -> {Char,Bytes2} = getbits(Bytes,NumBits), chars_decode2(Bytes2,{Min,Max,notab},NumBits,Len -1,[Char+Min|Acc]); %% BMPString and UniversalString with PermittedAlphabet is currently not supported chars_decode2(Bytes,{Min,Max,CharInTab},NumBits,Len,Acc) -> {Char,Bytes2} = getbits(Bytes,NumBits), chars_decode2(Bytes2,{Min,Max,CharInTab},NumBits,Len -1,[element(Char+1,CharInTab)|Acc]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_UTF8String(Val) -> CompleteList %% Val -> <> %% CompleteList -> [apropriate codes and values for driver complete] %% encode_UTF8String(Val) when is_binary(Val) -> Sz = byte_size(Val), [encode_length(Sz),octets_to_complete(Sz, Val)]; encode_UTF8String(Val) -> encode_UTF8String(list_to_binary(Val)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% decode_UTF8String(Bytes) -> {Utf8Binary,RemainingBytes} %% Utf8Binary -> <> %% RemainingBytes -> <> decode_UTF8String(Bytes) -> {Len,Bytes2} = decode_length(Bytes, undefined), {_Bin,_Bytes3} = getoctets_as_bin(Bytes2, Len). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_object_identifier(Val) -> CompleteList %% encode_object_identifier({Name,Val}) -> CompleteList %% Val -> {Int1,Int2,...,IntN} % N >= 2 %% Name -> atom() %% Int1 -> integer(0..2) %% Int2 -> integer(0..39) when Int1 (0..1) else integer() %% Int3-N -> integer() %% CompleteList -> [{bits,8,Val}|{octets,Ol}|align|...] %% encode_object_identifier(Val) -> OctetList = e_object_identifier(Val), Octets = list_to_binary(OctetList), Sz = byte_size(Octets), [encode_length(Sz), octets_to_complete(Sz, Octets)]. e_object_identifier({'OBJECT IDENTIFIER',V}) -> e_object_identifier(V); e_object_identifier(V) when is_tuple(V) -> e_object_identifier(tuple_to_list(V)); %% 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, % weird e_object_elements([Head|Tail],[]); e_object_identifier(Oid=[_,_|_Tail]) -> 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]]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% decode_object_identifier(Bytes) -> {ObjId,RemainingBytes} %% ObjId -> {integer(),integer(),...} % at least 2 integers %% RemainingBytes -> [integer()] when integer() (0..255) decode_object_identifier(Bytes) -> {Len,Bytes2} = decode_length(Bytes,undefined), {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), [First|Rest] = dec_subidentifiers(Octs,0,[]), Idlist = if First < 40 -> [0,First|Rest]; First < 80 -> [1,First - 40|Rest]; true -> [2,First - 80|Rest] end, {list_to_tuple(Idlist),Bytes3}. dec_subidentifiers([H|T],Av,Al) when H >=16#80 -> dec_subidentifiers(T,(Av bsl 7) + (H band 16#7F),Al); dec_subidentifiers([H|T],Av,Al) -> dec_subidentifiers(T,0,[(Av bsl 7) + H |Al]); dec_subidentifiers([],_Av,Al) -> lists:reverse(Al). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_relative_oid(Val) -> CompleteList %% encode_relative_oid({Name,Val}) -> CompleteList encode_relative_oid(Val) when is_tuple(Val) -> encode_relative_oid(tuple_to_list(Val)); encode_relative_oid(Val) when is_list(Val) -> Octets = list_to_binary([e_object_element(X)||X <- Val]), Sz = byte_size(Octets), [encode_length(Sz)|octets_to_complete(Sz, Octets)]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% decode_relative_oid(Val) -> CompleteList %% decode_relative_oid({Name,Val}) -> CompleteList decode_relative_oid(Bytes) -> {Len,Bytes2} = decode_length(Bytes,undefined), {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), ObjVals = dec_subidentifiers(Octs,0,[]), {list_to_tuple(ObjVals),Bytes3}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% complete(InList) -> ByteList %% Takes a coded list with bits and bytes and converts it to a list of bytes %% Should be applied as the last step at encode of a complete ASN.1 type %% complete(L) -> asn1rt_nif:encode_per_complete(L). octets_to_complete(Len,Val) when Len < 256 -> [20,Len,Val]; octets_to_complete(Len,Val) -> [21,<>,Val]. octets_unused_to_complete(Unused,Len,Val) when Len < 256 -> [30,Unused,Len,Val]; octets_unused_to_complete(Unused,Len,Val) -> [31,Unused,<>,Val].