%% ``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 via the world wide web 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. %% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings %% AB. All Rights Reserved.'' %% %% $Id: asn1rt_per.erl,v 1.1 2008/12/17 09:53:31 mikpe Exp $ %% -module(asn1rt_per). %% encoding / decoding of PER aligned -include("asn1_records.hrl"). -export([dec_fixup/3, cindex/3, list_to_record/2]). -export([setchoiceext/1, setext/1, fixoptionals/2, fixextensions/2, setoptionals/1, getext/1, getextension/2, skipextensions/3, getbit/1, getchoice/3 ]). -export([getoptionals/3, set_choice/3, encode_integer/2, encode_integer/3 ]). -export([decode_integer/2, decode_integer/3, encode_boolean/1, decode_boolean/1, encode_length/2, decode_length/1, decode_length/2, encode_small_length/1, decode_small_length/1]). -export([encode_enumerated/3, decode_enumerated/3, encode_bit_string/3, decode_bit_string/3 ]). -export([encode_octet_string/2, decode_octet_string/2, encode_restricted_string/4, encode_restricted_string/5, decode_restricted_string/4, decode_restricted_string/5, encode_null/1, decode_null/1, encode_object_identifier/1, decode_object_identifier/1, complete/1]). -export([encode_open_type/2, decode_open_type/2]). -export([encode_UniversalString/2, decode_UniversalString/2, encode_PrintableString/2, decode_PrintableString/2, 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_VisibleString/2, decode_VisibleString/2, encode_BMPString/2, decode_BMPString/2, encode_IA5String/2, decode_IA5String/2, encode_NumericString/2, decode_NumericString/2 ]). dec_fixup(Terms,Cnames,RemBytes) -> dec_fixup(Terms,Cnames,RemBytes,[]). dec_fixup([novalue|T],[_Hc|Tc],RemBytes,Acc) -> dec_fixup(T,Tc,RemBytes,Acc); dec_fixup([{_Name,novalue}|T],[_Hc|Tc],RemBytes,Acc) -> dec_fixup(T,Tc,RemBytes,Acc); dec_fixup([H|T],[Hc|Tc],RemBytes,Acc) -> dec_fixup(T,Tc,RemBytes,[{Hc,H}|Acc]); dec_fixup([],_Cnames,RemBytes,Acc) -> {lists:reverse(Acc),RemBytes}. cindex(Ix,Val,Cname) -> case element(Ix,Val) of {Cname,Val2} -> Val2; X -> X end. % converts a list to a record if necessary list_to_record(Name,List) when list(List) -> list_to_tuple([Name|List]); list_to_record(_Name,Tuple) when tuple(Tuple) -> Tuple. %%-------------------------------------------------------- %% setchoiceext(InRootSet) -> [{bit,X}] %% X is set to 1 when InRootSet==false %% X is set to 0 when InRootSet==true %% setchoiceext(true) -> [{debug,choiceext},{bit,0}]; setchoiceext(false) -> [{debug,choiceext},{bit,1}]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% setext(true|false) -> CompleteList %% setext(true) -> [{debug,ext},{bit,1}]; setext(false) -> [{debug,ext},{bit,0}]. fixoptionals(OptList,Val) when tuple(Val) -> fixoptionals(OptList,Val,[]); fixoptionals(OptList,Val) when list(Val) -> fixoptionals(OptList,Val,1,[],[]). fixoptionals([],Val,Acc) -> % return {Val,Opt} {Val,lists:reverse(Acc)}; fixoptionals([{_,Pos}|Ot],Val,Acc) -> case element(Pos+1,Val) of asn1_NOVALUE -> fixoptionals(Ot,Val,[0|Acc]); asn1_DEFAULT -> fixoptionals(Ot,Val,[0|Acc]); _ -> fixoptionals(Ot,Val,[1|Acc]) end. %setoptionals(OptList,Val) -> % Vlist = tuple_to_list(Val), % setoptionals(OptList,Vlist,1,[]). fixoptionals([{Name,Pos}|Ot],[{Name,Val}|Vt],_Opt,Acc1,Acc2) -> fixoptionals(Ot,Vt,Pos+1,[1|Acc1],[{Name,Val}|Acc2]); fixoptionals([{_Name,Pos}|Ot],V,Pos,Acc1,Acc2) -> fixoptionals(Ot,V,Pos+1,[0|Acc1],[asn1_NOVALUE|Acc2]); fixoptionals(O,[Vh|Vt],Pos,Acc1,Acc2) -> fixoptionals(O,Vt,Pos+1,Acc1,[Vh|Acc2]); fixoptionals([],[Vh|Vt],Pos,Acc1,Acc2) -> fixoptionals([],Vt,Pos+1,Acc1,[Vh|Acc2]); fixoptionals([],[],_,Acc1,Acc2) -> % return {Val,Opt} {list_to_tuple([asn1_RECORDNAME|lists:reverse(Acc2)]),lists:reverse(Acc1)}. setoptionals([H|T]) -> [{bit,H}|setoptionals(T)]; setoptionals([]) -> [{debug,optionals}]. getext(Bytes) when tuple(Bytes) -> getbit(Bytes); getext(Bytes) when list(Bytes) -> getbit({0,Bytes}). getextension(0, Bytes) -> {{},Bytes}; getextension(1, Bytes) -> {Len,Bytes2} = decode_small_length(Bytes), {Blist, Bytes3} = getbits_as_list(Len,Bytes2), {list_to_tuple(Blist),Bytes3}. fixextensions({ext,ExtPos,ExtNum},Val) -> case fixextensions(ExtPos,ExtNum+ExtPos,Val,0) of 0 -> []; ExtBits -> [encode_small_length(ExtNum),{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(Bytes,Nr,ExtensionBitPattern) -> case (catch element(Nr,ExtensionBitPattern)) of 1 -> {_,Bytes2} = decode_open_type(Bytes,[]), skipextensions(Bytes2, Nr+1, ExtensionBitPattern); 0 -> skipextensions(Bytes, Nr+1, ExtensionBitPattern); {'EXIT',_} -> % badarg, no more extensions Bytes end. getchoice(Bytes,1,0) -> % only 1 alternative is not encoded {0,Bytes}; getchoice(Bytes,_NumChoices,1) -> decode_small_number(Bytes); getchoice(Bytes,NumChoices,0) -> decode_integer(Bytes,[{'ValueRange',{0,NumChoices-1}}]). getoptionals(Bytes,L,NumComp) when list(L) -> {Blist,Bytes1} = getbits_as_list(length(L),Bytes), {list_to_tuple(comptuple(Blist,L,NumComp,1)),Bytes1}. comptuple([Bh|Bt],[{_Name,Nr}|T],NumComp,Nr) -> [Bh|comptuple(Bt,T,NumComp-1,Nr+1)]; comptuple(Bl,[{Name,Tnr}|Tl],NumComp,Nr) -> [0|comptuple(Bl,[{Name,Tnr}|Tl],NumComp-1,Nr+1)]; comptuple(_B,_L,0,_Nr) -> []; comptuple(B,O,N,Nr) -> [0|comptuple(B,O,N-1,Nr+1)]. getbits_as_list(Num,Bytes) -> getbits_as_list(Num,Bytes,[]). getbits_as_list(0,Bytes,Acc) -> {lists:reverse(Acc),Bytes}; getbits_as_list(Num,Bytes,Acc) -> {Bit,NewBytes} = getbit(Bytes), getbits_as_list(Num-1,NewBytes,[Bit|Acc]). getbit(Bytes) -> % io:format("getbit:~p~n",[Bytes]), getbit1(Bytes). getbit1({7,[H|T]}) -> {H band 1,{0,T}}; getbit1({Pos,[H|T]}) -> {(H bsr (7-Pos)) band 1,{(Pos+1) rem 8,[H|T]}}; getbit1(Bytes) when list(Bytes) -> getbit1({0,Bytes}). %% This could be optimized getbits(Buffer,Num) -> % io:format("getbits:Buffer = ~p~nNum=~p~n",[Buffer,Num]), getbits(Buffer,Num,0). getbits(Buffer,0,Acc) -> {Acc,Buffer}; getbits(Buffer,Num,Acc) -> {B,NewBuffer} = getbit(Buffer), getbits(NewBuffer,Num-1,B + (Acc bsl 1)). getoctet(Bytes) when list(Bytes) -> getoctet({0,Bytes}); getoctet(Bytes) -> % io:format("getoctet:Buffer = ~p~n",[Bytes]), getoctet1(Bytes). getoctet1({0,[H|T]}) -> {H,{0,T}}; getoctet1({_Pos,[_,H|T]}) -> {H,{0,T}}. align({0,L}) -> {0,L}; align({_Pos,[_H|T]}) -> {0,T}; align(Bytes) -> {0,Bytes}. getoctets(Buffer,Num) -> % io:format("getoctets:Buffer = ~p~nNum = ~p~n",[Buffer,Num]), getoctets(Buffer,Num,0). getoctets(Buffer,0,Acc) -> {Acc,Buffer}; getoctets(Buffer,Num,Acc) -> {Oct,NewBuffer} = getoctet(Buffer), getoctets(NewBuffer,Num-1,(Acc bsl 8)+Oct). getoctets_as_list(Buffer,Num) -> getoctets_as_list(Buffer,Num,[]). getoctets_as_list(Buffer,0,Acc) -> {lists:reverse(Acc),Buffer}; getoctets_as_list(Buffer,Num,Acc) -> {Oct,NewBuffer} = getoctet(Buffer), getoctets_as_list(NewBuffer,Num-1,[Oct|Acc]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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 integer(N), Len1 > 1 -> [{bit,0}, % the value is in the root set encode_integer([{'ValueRange',{0,Len1-1}}],N)]; N when integer(N) -> [{bit,0}]; % no encoding if only 0 or 1 alternative false -> [{bit,1}, % extension value case set_choice_tag(Alt,L2) of N2 when 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 integer(N), Len > 1 -> encode_integer([{'ValueRange',{0,Len-1}}],N); N when 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(_,[],_) -> 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(_Constraint, Val) when list(Val) -> [encode_length(undefined,length(Val)),align, {octets,Val}]; encode_open_type(_Constraint, Val) when binary(Val) -> [encode_length(undefined,size(Val)),align, {octets,binary_to_list(Val)}]. %% the binary_to_list is not optimal but compatible with the current solution %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% decode_open_type(Buffer,Constraint) -> Value %% Constraint is not used in this version %% Buffer = [byte] with PER encoded data %% Value = [byte] with decoded data (which must be decoded again as some type) %% decode_open_type(Bytes, _Constraint) -> {Len,Bytes2} = decode_length(Bytes,undefined), Bytes3 = align(Bytes2), getoctets_as_list(Bytes3,Len). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% encode_integer(Constraint,Value,NamedNumberList) -> CompleteList %% encode_integer(Constraint,Value) -> CompleteList %% encode_integer(Constraint,{Name,Value}) -> CompleteList %% %% encode_integer(C,V,NamedNumberList) when atom(V) -> case lists:keysearch(V,1,NamedNumberList) of {value,{_,NewV}} -> encode_integer(C,NewV); _ -> exit({error,{asn1,{namednumber,V}}}) end; encode_integer(C,V,_NamedNumberList) when integer(V) -> encode_integer(C,V). encode_integer(C,{Name,Val}) when atom(Name) -> encode_integer(C,Val); encode_integer({Rc,_Ec},Val) -> case (catch encode_integer(Rc,Val)) of {'EXIT',{error,{asn1,_}}} -> [{bit,1},encode_unconstrained_number(Val)]; Encoded -> [{bit,0},Encoded] end; encode_integer(C,Val ) when list(C) -> case get_constraint(C,'SingleValue') of no -> encode_integer1(C,Val); V when integer(V),V == Val -> []; % a type restricted to a single value encodes to nothing V when list(V) -> case lists:member(Val,V) of true -> encode_integer1(C,Val); _ -> exit({error,{asn1,{illegal_value,Val}}}) end; _ -> exit({error,{asn1,{illegal_value,Val}}}) end. encode_integer1(C, Val) -> case VR = get_constraint(C,'ValueRange') of no -> encode_unconstrained_number(Val); {Lb,'MAX'} -> encode_semi_constrained_number(Lb,Val); %% positive with range {Lb,Ub} when Val >= Lb, Ub >= Val -> encode_constrained_number(VR,Val) end. decode_integer(Buffer,Range,NamedNumberList) -> {Val,Buffer2} = decode_integer(Buffer,Range), case lists:keysearch(Val,2,NamedNumberList) of {value,{NewVal,_}} -> {NewVal,Buffer2}; _ -> {Val,Buffer2} end. decode_integer(Buffer,{Rc,_Ec}) -> {Ext,Buffer2} = getext(Buffer), case Ext of 0 -> decode_integer(Buffer2,Rc); 1 -> decode_unconstrained_number(Buffer2) end; decode_integer(Buffer,undefined) -> decode_unconstrained_number(Buffer); decode_integer(Buffer,C) -> case get_constraint(C,'SingleValue') of V when integer(V) -> {V,Buffer}; V when list(V) -> {Val,Buffer2} = decode_integer1(Buffer,C), case lists:member(Val,V) of true -> {Val,Buffer2}; _ -> exit({error,{asn1,{illegal_value,Val}}}) end; _ -> decode_integer1(Buffer,C) end. decode_integer1(Buffer,C) -> case VR = get_constraint(C,'ValueRange') of no -> decode_unconstrained_number(Buffer); {Lb, 'MAX'} -> decode_semi_constrained_number(Buffer,Lb); {_,_} -> decode_constrained_number(Buffer,VR) end. % 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({Name,Val}) when atom(Name) -> encode_small_number(Val); encode_small_number(Val) when Val =< 63 -> [{bit,0},{bits,6,Val}]; encode_small_number(Val) -> [{bit,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,{0,'MAX'}) end. % X.691:10.7 Encoding of a semi-constrained whole number %% might be an optimization encode_semi_constrained_number(0,Val) -> encode_semi_constrained_number(C,{Name,Val}) when atom(Name) -> encode_semi_constrained_number(C,Val); encode_semi_constrained_number({Lb,'MAX'},Val) -> encode_semi_constrained_number(Lb,Val); encode_semi_constrained_number(Lb,Val) -> Val2 = Val - Lb, Octs = eint_positive(Val2), [encode_length(undefined,length(Octs)),{octets,Octs}]. decode_semi_constrained_number(Bytes,{Lb,_}) -> decode_semi_constrained_number(Bytes,Lb); decode_semi_constrained_number(Bytes,Lb) -> {Len,Bytes2} = decode_length(Bytes,undefined), {V,Bytes3} = getoctets(Bytes2,Len), {V+Lb,Bytes3}. encode_constrained_number(Range,{Name,Val}) when atom(Name) -> encode_constrained_number(Range,Val); encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val -> Range = Ub - Lb + 1, Val2 = Val - Lb, if Range == 2 -> {bits,1,Val2}; Range =< 4 -> {bits,2,Val2}; Range =< 8 -> {bits,3,Val2}; Range =< 16 -> {bits,4,Val2}; Range =< 32 -> {bits,5,Val2}; Range =< 64 -> {bits,6,Val2}; Range =< 128 -> {bits,7,Val2}; Range =< 255 -> {bits,8,Val2}; Range =< 256 -> {octets,1,Val2}; Range =< 65536 -> {octets,2,Val2}; Range =< 16#1000000 -> Octs = eint_positive(Val2), [encode_length({1,3},length(Octs)),{octets,Octs}]; Range =< 16#100000000 -> Octs = eint_positive(Val2), [encode_length({1,4},length(Octs)),{octets,Octs}]; Range =< 16#10000000000 -> Octs = eint_positive(Val2), [encode_length({1,5},length(Octs)),{octets,Octs}]; true -> exit({not_supported,{integer_range,Range}}) end. decode_constrained_number(Buffer,{Lb,Ub}) -> Range = Ub - Lb + 1, % Val2 = Val - Lb, {Val,Remain} = if 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 =< 16#1000000 -> {Len,Bytes2} = decode_length(Buffer,{1,3}), {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), {dec_pos_integer(Octs),Bytes3}; Range =< 16#100000000 -> {Len,Bytes2} = decode_length(Buffer,{1,4}), {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), {dec_pos_integer(Octs),Bytes3}; Range =< 16#10000000000 -> {Len,Bytes2} = decode_length(Buffer,{1,5}), {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), {dec_pos_integer(Octs),Bytes3}; true -> exit({not_supported,{integer_range,Range}}) end, {Val+Lb,Remain}. % X.691:10.8 Encoding of an unconstrained whole number encode_unconstrained_number(Val) when Val >= 0 -> Oct = eint(Val,[]), [{debug,unconstrained_number}, encode_length({0,'MAX'},length(Oct)), {octets,Oct}]; encode_unconstrained_number(Val) -> % negative Oct = enint(Val,[]), [{debug,unconstrained_number}, encode_length({0,'MAX'},length(Oct)), {octets,Oct}]. %% used for positive Values which don't need a sign bit 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]). %% used for signed positive values %eint(Val, Ack) -> % X = Val band 255, % Next = Val bsr 8, % if % Next == 0, X >= 127 -> % [0,X|Ack]; % Next == 0 -> % [X|Ack]; % true -> % eint(Next,[X|Ack]) % end. %%% used for signed negative values %enint(Val, Acc) -> % NumOctets = if % -Val < 16#80 -> 1; % -Val < 16#8000 ->2; % -Val < 16#800000 ->3; % -Val < 16#80000000 ->4; % -Val < 16#8000000000 ->5; % -Val < 16#800000000000 ->6; % -Val < 16#80000000000000 ->7; % -Val < 16#8000000000000000 ->8; % -Val < 16#800000000000000000 ->9 % end, % enint(Val,Acc,NumOctets). %enint(Val, Acc,0) -> % Acc; %enint(Val, Acc,NumOctets) -> % enint(Val bsr 8,[Val band 255|Acc],NumOctets-1). decode_unconstrained_number(Bytes) -> {Len,Bytes2} = decode_length(Bytes,undefined), {Ints,Bytes3} = getoctets_as_list(Bytes2,Len), {dec_integer(Ints),Bytes3}. dec_pos_integer(Ints) -> decpint(Ints, 8 * (length(Ints) - 1)). dec_integer(Ints) when hd(Ints) band 255 =< 127 -> %% Positive number decpint(Ints, 8 * (length(Ints) - 1)); dec_integer(Ints) -> %% Negative decnint(Ints, 8 * (length(Ints) - 1)). decpint([Byte|Tail], Shift) -> (Byte bsl Shift) bor decpint(Tail, Shift-8); decpint([], _) -> 0. decnint([Byte|Tail], Shift) -> (-128 + (Byte band 127) bsl Shift) bor decpint(Tail, Shift-8). minimum_octets(Val) -> minimum_octets(Val,[]). minimum_octets(Val,Acc) when Val > 0 -> minimum_octets((Val bsr 8),[Val band 16#FF|Acc]); minimum_octets(0,Acc) -> 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(undefined,Len) -> % un-constrained if Len < 128 -> {octet,Len band 16#7F}; Len < 16384 -> {octets,2,2#1000000000000000 bor Len}; true -> exit({error,{asn1,{encode_length,{nyi,above_16k}}}}) end; encode_length({0,'MAX'},Len) -> encode_length(undefined,Len); encode_length({Lb,Ub},Len) when Ub =< 65535 ,Lb >= 0 -> % constrained encode_constrained_number({Lb,Ub},Len); encode_length(SingleValue,_Len) when integer(SingleValue) -> []. encode_small_length(Len) when Len =< 64 -> [{bit,0},{bits,6,Len-1}]; encode_small_length(Len) -> [{bit,1},encode_length(undefined,Len)]. decode_small_length(Buffer) -> case getbit(Buffer) of {0,Remain} -> {Bits,Remain2} = getbits(Remain,6), {Bits+1,Remain2}; {1,Remain} -> decode_length(Remain,undefined) end. decode_length(Buffer) -> decode_length(Buffer,undefined). decode_length(Buffer,undefined) -> % un-constrained Buffer2 = align(Buffer), {Bits,_} = getbits(Buffer2,2), case Bits of 2 -> {Val,Bytes3} = getoctets(Buffer2,2), {(Val band 16#3FFF),Bytes3}; 3 -> exit({error,{asn1,{decode_length,{nyi,above_16k}}}}); _ -> {Val,Bytes3} = getoctet(Buffer2), {Val band 16#7F,Bytes3} end; decode_length(Buffer,{Lb,Ub}) when Ub =< 65535 ,Lb >= 0 -> % constrained decode_constrained_number(Buffer,{Lb,Ub}); % X.691:10.9.3.5 decode_length(Buffer,{_,_Lb,_Ub}) -> %when Len =< 127 -> % Unconstrained or large Ub case getbit(Buffer) of {0,Remain} -> getbits(Remain,7); {1,_Remain} -> {Val,Remain2} = getoctets(Buffer,2), {Val band 2#0111111111111111, Remain2} end; decode_length(Buffer,SingleValue) when integer(SingleValue) -> {SingleValue,Buffer}. % X.691:11 encode_boolean({Name,Val}) when atom(Name) -> encode_boolean(Val); encode_boolean(true) -> {bit,1}; encode_boolean(false) -> {bit,0}; encode_boolean(Val) -> exit({error,{asn1,{encode_boolean,Val}}}). decode_boolean(Buffer) -> %when record(Buffer,buffer) case getbit(Buffer) of {1,Remain} -> {true,Remain}; {0,Remain} -> {false,Remain} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% X.691:12 %% ENUMERATED %% %% encode_enumerated(C,Value,NamedNumberTup) -> CompleteList %% %% encode_enumerated(C,{Name,Value},NamedNumberList) when atom(Name),list(NamedNumberList) -> encode_enumerated(C,Value,NamedNumberList); %% ENUMERATED with extension mark encode_enumerated(_C,{asn1_enum,Value},{_Nlist1,Nlist2}) when Value >= length(Nlist2) -> [{bit,1},encode_small_number(Value)]; encode_enumerated(C,Value,{Nlist1,Nlist2}) -> case enum_search(Value,Nlist1,0) of NewV when integer(NewV) -> [{bit,0},encode_integer(C,NewV)]; false -> case enum_search(Value,Nlist2,0) of ExtV when integer(ExtV) -> [{bit,1},encode_small_number(ExtV)]; false -> exit({error,{asn1,{encode_enumerated,Value}}}) end end; encode_enumerated(C,Value,NamedNumberList) when list(NamedNumberList) -> case enum_search(Value,NamedNumberList,0) of NewV when integer(NewV) -> encode_integer(C,NewV); false -> exit({error,{asn1,{encode_enumerated,Value}}}) end. %% returns the ordinal number from 0 ,1 ... in the list where Name is found %% or false if not found %% enum_search(Name,[Name|_NamedNumberList],Acc) -> Acc; enum_search(Name,[_H|T],Acc) -> enum_search(Name,T,Acc+1); enum_search(_,[],_) -> false. % name not found !error %% ENUMERATED with extension marker decode_enumerated(Buffer,C,{Ntup1,Ntup2}) when tuple(Ntup1), tuple(Ntup2) -> {Ext,Buffer2} = getext(Buffer), case Ext of 0 -> % not an extension value {Val,Buffer3} = decode_integer(Buffer2,C), case catch (element(Val+1,Ntup1)) of NewVal when atom(NewVal) -> {NewVal,Buffer3}; _Error -> exit({error,{asn1,{decode_enumerated,{Val,[Ntup1,Ntup2]}}}}) end; 1 -> % this an extension value {Val,Buffer3} = decode_small_number(Buffer2), case catch (element(Val+1,Ntup2)) of NewVal when atom(NewVal) -> {NewVal,Buffer3}; _ -> {{asn1_enum,Val},Buffer3} end end; decode_enumerated(Buffer,C,NamedNumberTup) when tuple(NamedNumberTup) -> {Val,Buffer2} = decode_integer(Buffer,C), case catch (element(Val+1,NamedNumberTup)) of NewVal when atom(NewVal) -> {NewVal,Buffer2}; _Error -> exit({error,{asn1,{decode_enumerated,{Val,NamedNumberTup}}}}) end. %%=============================================================================== %%=============================================================================== %%=============================================================================== %% Bitstring value, ITU_T X.690 Chapter 8.5 %%=============================================================================== %%=============================================================================== %%=============================================================================== %%=============================================================================== %% encode bitstring value %%=============================================================================== %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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 named bits encode_bit_string(C, [FirstVal | RestVal], NamedBitList) when atom(FirstVal) -> ToSetPos = get_all_bitposes([FirstVal | RestVal], NamedBitList, []), BitList = make_and_set_list(ToSetPos,0), encode_bit_string(C,BitList,NamedBitList); encode_bit_string(C, [{bit,No} | RestVal], NamedBitList) -> ToSetPos = get_all_bitposes([{bit,No} | RestVal], 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(C, BitListValue, _NamedBitList) when list(BitListValue) -> %% first remove any trailing zeroes Bl1 = lists:dropwhile(fun(0)->true;(1)->false end,lists:reverse(BitListValue)), BitList = [{bit,X} || X <- lists:reverse(Bl1)], case get_constraint(C,'SizeConstraint') of 0 -> % fixed length []; % nothing to encode V when integer(V),V=<16 -> % fixed length 16 bits or less pad_list(V,BitList); V when integer(V) -> % fixed length more than 16 bits [align,pad_list(V,BitList)]; {Lb,Ub} when integer(Lb),integer(Ub) -> [encode_length({Lb,Ub},length(BitList)),align,BitList]; no -> [encode_length(undefined,length(BitList)),align,BitList] end; %% when the value is an integer encode_bit_string(C, IntegerVal, NamedBitList) -> BitList = int_to_bitlist(IntegerVal), encode_bit_string(C,BitList,NamedBitList). %%%%%%%%%%%%%%% %% The result is presented as a list of named bits (if possible) %% else as a list of 0 and 1. %% decode_bit_string(Buffer, C, NamedNumberList) -> case get_constraint(C,'SizeConstraint') of 0 -> % fixed length {[],Buffer}; % nothing to encode V when integer(V),V=<16 -> % fixed length 16 bits or less bit_list_to_named(Buffer,V,NamedNumberList); V when integer(V) -> % fixed length 16 bits or less Bytes2 = align(Buffer), bit_list_to_named(Bytes2,V,NamedNumberList); {Lb,Ub} when integer(Lb),integer(Ub) -> {Len,Bytes2} = decode_length(Buffer,{Lb,Ub}), Bytes3 = align(Bytes2), bit_list_to_named(Bytes3,Len,NamedNumberList); no -> {Len,Bytes2} = decode_length(Buffer,undefined), Bytes3 = align(Bytes2), bit_list_to_named(Bytes3,Len,NamedNumberList) end. %% if no named bits are declared we will return a %% BitList = [0 | 1] bit_list_to_named(Buffer,Len,[]) -> getbits_as_list(Len,Buffer); %% if there are named bits declared we will return a named %% BitList where the names are atoms and unnamed bits represented %% as {bit,Pos} %% BitList = [atom() | {bit,Pos}] %% Pos = integer() bit_list_to_named(Buffer,Len,NamedNumberList) -> {BitList,Rest} = getbits_as_list(Len,Buffer), {bit_list_to_named1(0,BitList,NamedNumberList,[]), Rest}. bit_list_to_named1(Pos,[0|Bt],Names,Acc) -> bit_list_to_named1(Pos+1,Bt,Names,Acc); bit_list_to_named1(Pos,[1|Bt],Names,Acc) -> case lists:keysearch(Pos,2,Names) of {value,{Name,_}} -> bit_list_to_named1(Pos+1,Bt,Names,[Name|Acc]); _ -> bit_list_to_named1(Pos+1,Bt,Names,[{bit,Pos}|Acc]) end; bit_list_to_named1(_Pos,[],_Names,Acc) -> lists:reverse(Acc). %%%%%%%%%%%%%%% %% int_to_bitlist(0) -> []; int_to_bitlist(Int) when integer(Int), Int >= 0 -> [Int band 1 | int_to_bitlist(Int bsr 1)]. %%%%%%%%%%%%%%%%%% %% 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:keysearch(Val, 1, NamedBitList) of {value, {_ValName, ValPos}} -> get_all_bitposes(Rest, NamedBitList, [ValPos | Ack]); _ -> 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([], _) -> []. %%%%%%%%%%%%%%%%% %% pad_list(N,BitList) -> PaddedList %% returns a padded (with trailing {bit,0} elements) list of length N %% if Bitlist contains more than N significant bits set an exit asn1_error %% is generated pad_list(0,BitList) -> case BitList of [] -> []; _ -> exit({error,{asn1,{range_error,{bit_string,BitList}}}}) end; pad_list(N,[Bh|Bt]) -> [Bh|pad_list(N-1,Bt)]; pad_list(N,[]) -> [{bit,0},pad_list(N-1,[])]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% X.691:16 %% encode_octet_string(Constraint,ExtensionMarker,Val) %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% encode_octet_string(C,{Name,Val}) when atom(Name) -> encode_octet_string(C,false,Val); encode_octet_string(C,Val) -> encode_octet_string(C,false,Val). encode_octet_string(_C,true,_Val) -> exit({error,{asn1,{'not_supported',extensionmarker}}}); encode_octet_string(C,false,Val) -> case get_constraint(C,'SizeConstraint') of 0 -> []; 1 -> [V] = Val, {bits,8,V}; 2 -> [V1,V2] = Val, [{bits,8,V1},{bits,8,V2}]; Sv when Sv =<65535, Sv == length(Val) -> % fixed length [align,{octets,Val}]; {Lb,Ub} -> [encode_length({Lb,Ub},length(Val)),align, {octets,Val}]; Sv when list(Sv) -> [encode_length({hd(Sv),lists:max(Sv)},length(Val)),align, {octets,Val}]; no -> [encode_length(undefined,length(Val)),align, {octets,Val}] end. decode_octet_string(Bytes,Range) -> decode_octet_string(Bytes,Range,false). decode_octet_string(Bytes,C,false) -> case get_constraint(C,'SizeConstraint') of 0 -> {[],Bytes}; 1 -> {B1,Bytes2} = getbits(Bytes,8), {[B1],Bytes2}; 2 -> {B1,Bytes2}= getbits(Bytes,8), {B2,Bytes3}= getbits(Bytes2,8), {[B1,B2],Bytes3}; {_,0} -> {[],Bytes}; Sv when integer(Sv), Sv =<65535 -> % fixed length Bytes2 = align(Bytes), getoctets_as_list(Bytes2,Sv); {Lb,Ub} -> {Len,Bytes2} = decode_length(Bytes,{Lb,Ub}), Bytes3 = align(Bytes2), getoctets_as_list(Bytes3,Len); Sv when list(Sv) -> {Len,Bytes2} = decode_length(Bytes,{hd(Sv),lists:max(Sv)}), Bytes3 = align(Bytes2), getoctets_as_list(Bytes3,Len); no -> {Len,Bytes2} = decode_length(Bytes,undefined), Bytes3 = align(Bytes2), getoctets_as_list(Bytes3,Len) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Restricted char string types %% (NumericString, PrintableString,VisibleString,IA5String,BMPString,UniversalString) %% X.691:26 and X.680:34-36 %%encode_restricted_string(aligned,'BMPString',Constraints,Extension,Val) encode_restricted_string(aligned,StringType,C,Val) -> encode_restricted_string(aligned,StringType,C,false,Val). encode_restricted_string(aligned,StringType,C,_Ext,{Name,Val}) when atom(Name) -> encode_restricted_string(aligned,StringType,C,false,Val); encode_restricted_string(aligned,StringType,C,_Ext,Val) -> Result = chars_encode(C,StringType,Val), NumBits = get_NumBits(C,StringType), case get_constraint(C,'SizeConstraint') of Ub when integer(Ub), Ub*NumBits =< 16 -> case {StringType,Result} of {'BMPString',{octets,Ol}} -> [{bits,8,Oct}||Oct <- Ol]; _ -> Result end; 0 -> []; Ub when integer(Ub),Ub =<65535 -> % fixed length [align,Result]; {Ub,Lb} -> [encode_length({Ub,Lb},length(Val)),align,Result]; Vl when list(Vl) -> [encode_length({lists:min(Vl),lists:max(Vl)},length(Val)),align,Result]; no -> [encode_length(undefined,length(Val)),align,Result] end. decode_restricted_string(Bytes,aligned,StringType,C) -> decode_restricted_string(Bytes,aligned,StringType,C,false). decode_restricted_string(Bytes,aligned,StringType,C,_Ext) -> NumBits = get_NumBits(C,StringType), case get_constraint(C,'SizeConstraint') of Ub when integer(Ub), Ub*NumBits =< 16 -> chars_decode(Bytes,NumBits,StringType,C,Ub); Ub when integer(Ub),Ub =<65535 -> % fixed length Bytes1 = align(Bytes), chars_decode(Bytes1,NumBits,StringType,C,Ub); 0 -> {[],Bytes}; Vl when list(Vl) -> {Len,Bytes1} = decode_length(Bytes,{hd(Vl),lists:max(Vl)}), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,C,Len); no -> {Len,Bytes1} = decode_length(Bytes,undefined), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,C,Len); {Lb,Ub}-> {Len,Bytes1} = decode_length(Bytes,{Lb,Ub}), Bytes2 = align(Bytes1), chars_decode(Bytes2,NumBits,StringType,C,Len) end. encode_BMPString(C,Val) -> encode_restricted_string(aligned,'BMPString',C,false,Val). decode_BMPString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'BMPString',C,false). encode_GeneralString(C,Val) -> encode_restricted_string(aligned,'GeneralString',C,false,Val). decode_GeneralString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'GeneralString',C,false). encode_GraphicString(C,Val) -> encode_restricted_string(aligned,'GraphicString',C,false,Val). decode_GraphicString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'GraphicString',C,false). encode_IA5String(C,Val) -> encode_restricted_string(aligned,'IA5String',C,false,Val). decode_IA5String(Bytes,C) -> decode_restricted_string(Bytes,aligned,'IA5String',C,false). encode_NumericString(C,Val) -> encode_restricted_string(aligned,'NumericString',C,false,Val). decode_NumericString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'NumericString',C,false). encode_PrintableString(C,Val) -> encode_restricted_string(aligned,'PrintableString',C,false,Val). decode_PrintableString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'PrintableString',C,false). encode_TeletexString(C,Val) -> % equivalent with T61String encode_restricted_string(aligned,'TeletexString',C,false,Val). decode_TeletexString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'TeletexString',C,false). encode_UniversalString(C,Val) -> encode_restricted_string(aligned,'UniversalString',C,false,Val). decode_UniversalString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'UniversalString',C,false). encode_VideotexString(C,Val) -> encode_restricted_string(aligned,'VideotexString',C,false,Val). decode_VideotexString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'VideotexString',C,false). encode_VisibleString(C,Val) -> % equivalent with ISO646String encode_restricted_string(aligned,'VisibleString',C,false,Val). decode_VisibleString(Bytes,C) -> decode_restricted_string(Bytes,aligned,'VisibleString',C,false). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% getBMPChars(Bytes,Len) ->{BMPcharList,RemainingBytes} %% getBMPChars(Bytes,1) -> {O1,Bytes2} = getbits(Bytes,8), {O2,Bytes3} = getbits(Bytes2,8), if O1 == 0 -> {[O2],Bytes3}; true -> {[{O1,O2}],Bytes3} end; getBMPChars(Bytes,Len) -> getBMPChars(Bytes,Len,[]). getBMPChars(Bytes,0,Acc) -> {lists:reverse(Acc),Bytes}; getBMPChars(Bytes,Len,Acc) -> {Octs,Bytes1} = getoctets_as_list(Bytes,2), case Octs of [0,O2] -> getBMPChars(Bytes1,Len-1,[O2|Acc]); [O1,O2]-> getBMPChars(Bytes1,Len-1,[{O1,O2}|Acc]) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% chars_encode(C,StringType,Value) -> ValueList %% %% encodes chars according to the per rules taking the constraint PermittedAlphabet %% into account. %% This function does only encode the value part and NOT the length chars_encode(C,StringType,Value) -> case {StringType,get_constraint(C,'PermittedAlphabet')} of {'UniversalString',{_,_Sv}} -> exit({error,{asn1,{'not implemented',"UniversalString with PermittedAlphabet constraint"}}}); {'BMPString',{_,_Sv}} -> exit({error,{asn1,{'not implemented',"BMPString with PermittedAlphabet constraint"}}}); _ -> {NumBits,CharOutTab} = {get_NumBits(C,StringType),get_CharOutTab(C,StringType)}, chars_encode2(Value,NumBits,CharOutTab) end. chars_encode2([H|T],NumBits,{Min,Max,notab}) when H =< Max, H >= Min -> [{bits,NumBits,H-Min}|chars_encode2(T,NumBits,{Min,Max,notab})]; chars_encode2([H|T],NumBits,{Min,Max,Tab}) when H =< Max, H >= Min -> [{bits,NumBits,element(H-Min+1,Tab)}|chars_encode2(T,NumBits,{Min,Max,Tab})]; chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,notab}) -> %% no value range check here (ought to be, but very expensive) [{bits,NumBits,(A*B*C*D)-Min}|chars_encode2(T,NumBits,{Min,Max,notab})]; chars_encode2([{A,B,C,D}|T],NumBits,{Min,Max,Tab}) -> %% no value range check here (ought to be, but very expensive) [{bits,NumBits,element((A*B*C*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([],_,_) -> []. get_NumBits(C,StringType) -> case get_constraint(C,'PermittedAlphabet') of {'SingleValue',Sv} -> charbits(length(Sv),aligned); no -> case StringType of 'GeneralString' -> exit({error,{asn1,{not implemented,'GeneralString'}}}); 'GraphicString' -> exit({error,{asn1,{not implemented,'GraphicString'}}}); 'TeletexString' -> exit({error,{asn1,{not implemented,'TeletexString'}}}); 'VideotexString' -> exit({error,{asn1,{not implemented,'VideotexString'}}}); 'IA5String' -> charbits(128,aligned); % 16#00..16#7F 'VisibleString' -> charbits(95,aligned); % 16#20..16#7E 'PrintableString' -> charbits(74,aligned); % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z 'NumericString' -> charbits(11,aligned); % $ ,"0123456789" 'UniversalString' -> 32; 'BMPString' -> 16 end end. %%Maybe used later %%get_MaxChar(C,StringType) -> %% case get_constraint(C,'PermittedAlphabet') of %% {'SingleValue',Sv} -> %% lists:nth(length(Sv),Sv); %% no -> %% case StringType of %% 'IA5String' -> %% 16#7F; % 16#00..16#7F %% 'VisibleString' -> %% 16#7E; % 16#20..16#7E %% 'PrintableString' -> %% $z; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z %% 'NumericString' -> %% $9; % $ ,"0123456789" %% 'UniversalString' -> %% 16#ffffffff; %% 'BMPString' -> %% 16#ffff %% end %% end. %%Maybe used later %%get_MinChar(C,StringType) -> %% case get_constraint(C,'PermittedAlphabet') of %% {'SingleValue',Sv} -> %% hd(Sv); %% no -> %% case StringType of %% 'IA5String' -> %% 16#00; % 16#00..16#7F %% 'VisibleString' -> %% 16#20; % 16#20..16#7E %% 'PrintableString' -> %% $\s; % [$\s,$',$(,$),$+,$,,$-,$.,$/,"0123456789",$:,$=,$?,$A..$Z,$a..$z %% 'NumericString' -> %% $\s; % $ ,"0123456789" %% 'UniversalString' -> %% 16#00; %% 'BMPString' -> %% 16#00 %% end %% end. get_CharOutTab(C,StringType) -> get_CharTab(C,StringType,out). get_CharInTab(C,StringType) -> get_CharTab(C,StringType,in). get_CharTab(C,StringType,InOut) -> case get_constraint(C,'PermittedAlphabet') of {'SingleValue',Sv} -> get_CharTab2(C,StringType,hd(Sv),lists:max(Sv),Sv,InOut); no -> case StringType of 'IA5String' -> {0,16#7F,notab}; 'VisibleString' -> get_CharTab2(C,StringType,16#20,16#7F,notab,InOut); 'PrintableString' -> Chars = lists:sort( " '()+,-./0123456789:=?ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"), get_CharTab2(C,StringType,hd(Chars),lists:max(Chars),Chars,InOut); 'NumericString' -> get_CharTab2(C,StringType,16#20,$9," 0123456789",InOut); 'UniversalString' -> {0,16#FFFFFFFF,notab}; 'BMPString' -> {0,16#FFFF,notab} end end. get_CharTab2(C,StringType,Min,Max,Chars,InOut) -> BitValMax = (1 bsl get_NumBits(C,StringType))-1, if Max =< BitValMax -> {0,Max,notab}; true -> case InOut of out -> {Min,Max,create_char_tab(Min,Chars)}; in -> {Min,Max,list_to_tuple(Chars)} end end. create_char_tab(Min,L) -> list_to_tuple(create_char_tab(Min,L,0)). create_char_tab(Min,[Min|T],V) -> [V|create_char_tab(Min+1,T,V+1)]; create_char_tab(_Min,[],_V) -> []; create_char_tab(Min,L,V) -> [false|create_char_tab(Min+1,L,V)]. %% This very inefficient and should be moved to compiletime charbits(NumOfChars,aligned) -> case charbits(NumOfChars) of 1 -> 1; 2 -> 2; B when B > 2, B =< 4 -> 4; B when B > 4, B =< 8 -> 8; B when B > 8, B =< 16 -> 16; B when B > 16, B =< 32 -> 32 end. charbits(NumOfChars) when NumOfChars =< 2 -> 1; charbits(NumOfChars) when NumOfChars =< 4 -> 2; charbits(NumOfChars) when NumOfChars =< 8 -> 3; charbits(NumOfChars) when NumOfChars =< 16 -> 4; charbits(NumOfChars) when NumOfChars =< 32 -> 5; charbits(NumOfChars) when NumOfChars =< 64 -> 6; charbits(NumOfChars) when NumOfChars =< 128 -> 7; charbits(NumOfChars) when NumOfChars =< 256 -> 8; charbits(NumOfChars) when NumOfChars =< 512 -> 9; charbits(NumOfChars) when NumOfChars =< 1024 -> 10; charbits(NumOfChars) when NumOfChars =< 2048 -> 11; charbits(NumOfChars) when NumOfChars =< 4096 -> 12; charbits(NumOfChars) when NumOfChars =< 8192 -> 13; charbits(NumOfChars) when NumOfChars =< 16384 -> 14; charbits(NumOfChars) when NumOfChars =< 32768 -> 15; charbits(NumOfChars) when NumOfChars =< 65536 -> 16; charbits(NumOfChars) when integer(NumOfChars) -> 16 + charbits1(NumOfChars bsr 16). charbits1(0) -> 0; charbits1(NumOfChars) -> 1 + charbits1(NumOfChars bsr 1). chars_decode(Bytes,_,'BMPString',C,Len) -> case get_constraint(C,'PermittedAlphabet') of no -> getBMPChars(Bytes,Len); _ -> exit({error,{asn1, {'not implemented', "BMPString with PermittedAlphabet constraint"}}}) end; chars_decode(Bytes,NumBits,StringType,C,Len) -> CharInTab = get_CharInTab(C,StringType), 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 = case minimum_octets(Char+Min) of [NewChar] -> NewChar; [C1,C2] -> {0,0,C1,C2}; [C1,C2,C3] -> {0,C1,C2,C3}; [C1,C2,C3,C4] -> {C1,C2,C3,C4} 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]). % X.691:17 encode_null({Name,Val}) when atom(Name) -> encode_null(Val); encode_null(_) -> []. % encodes to nothing decode_null(Bytes) -> {'NULL',Bytes}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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) -> Octets = e_object_identifier(Val,notag), [{debug,object_identifier},encode_length(undefined,length(Octets)),{octets,Octets}]. %% This code is copied from asn1_encode.erl (BER) and corrected and modified e_object_identifier({'OBJECT IDENTIFIER',V},DoTag) -> e_object_identifier(V,DoTag); e_object_identifier({Cname,V},DoTag) when atom(Cname),tuple(V) -> e_object_identifier(tuple_to_list(V),DoTag); e_object_identifier({Cname,V},DoTag) when atom(Cname),list(V) -> e_object_identifier(V,DoTag); e_object_identifier(V,DoTag) when tuple(V) -> e_object_identifier(tuple_to_list(V),DoTag); % E1 = 0|1|2 and (E2 < 40 when E1 = 0|1) e_object_identifier([E1,E2|Tail],_DoTag) when E1 =< 2 -> Head = 40*E1 + E2, % weird Res = e_object_elements([Head|Tail]), % dotag(DoTag,[6],elength(length(Res)+1),[Head|Res]), Res. e_object_elements([]) -> []; e_object_elements([H|T]) -> lists:append(e_object_element(H),e_object_elements(T)). e_object_element(Num) when Num < 128 -> [Num]; % must be changed to handle more than 2 octets e_object_element(Num) -> %% when Num < ??? Left = ((Num band 2#11111110000000) bsr 7) bor 2#10000000, Right = Num band 2#1111111 , [Left,Right]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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). get_constraint(C,Key) -> case lists:keysearch(Key,1,C) of false -> no; {value,{_,V}} -> V end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% 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(InList) when list(InList) -> complete(InList,[],0); complete(InList) -> complete([InList],[],0). complete([{debug,_}|T], Acc, Acclen) -> complete(T,Acc,Acclen); complete([H|T],Acc,Acclen) when list(H) -> complete(lists:concat([H,T]),Acc,Acclen); complete([{octets,N,Val}|T],Acc,Acclen) when N =< 4 ,integer(Val) -> Newval = case N of 1 -> Val4 = Val band 16#FF, [Val4]; 2 -> Val3 = (Val bsr 8) band 16#FF, Val4 = Val band 16#FF, [Val3,Val4]; 3 -> Val2 = (Val bsr 16) band 16#FF, Val3 = (Val bsr 8) band 16#FF, Val4 = Val band 16#FF, [Val2,Val3,Val4]; 4 -> Val1 = (Val bsr 24) band 16#FF, Val2 = (Val bsr 16) band 16#FF, Val3 = (Val bsr 8) band 16#FF, Val4 = Val band 16#FF, [Val1,Val2,Val3,Val4] end, complete([{octets,Newval}|T],Acc,Acclen); complete([{octets,Oct}|T],[],_Acclen) when list(Oct) -> complete(T,lists:reverse(Oct),0); complete([{octets,Oct}|T],[Hacc|Tacc],Acclen) when list(Oct) -> Rest = 8 - Acclen, if Rest == 8 -> complete(T,lists:concat([lists:reverse(Oct),[Hacc|Tacc]]),0); true -> complete(T,lists:concat([lists:reverse(Oct),[Hacc bsl Rest|Tacc]]),0) end; complete([{bit,Val}|T], Acc, Acclen) -> complete([{bits,1,Val}|T],Acc,Acclen); complete([{octet,Val}|T], Acc, Acclen) -> complete([{octets,1,Val}|T],Acc,Acclen); complete([{bits,N,Val}|T], Acc, 0) when N =< 8 -> complete(T,[Val|Acc],N); complete([{bits,N,Val}|T], [Hacc|Tacc], Acclen) when N =< 8 -> Rest = 8 - Acclen, if Rest >= N -> complete(T,[(Hacc bsl N) + Val|Tacc],(Acclen+N) rem 8); true -> Diff = N - Rest, NewHacc = (Hacc bsl Rest) + (Val bsr Diff), Mask = element(Diff,{1,3,7,15,31,63,127,255}), complete(T,[(Val band Mask),NewHacc|Tacc],(Acclen+N) rem 8) end; complete([{bits,N,Val}|T], Acc, Acclen) -> % N > 8 complete([{bits,N-8,Val bsr 8},{bits,8,Val band 255}|T],Acc,Acclen); complete([align|T],Acc,0) -> complete(T,Acc,0); complete([align|T],[Hacc|Tacc],Acclen) -> Rest = 8 - Acclen, complete(T,[Hacc bsl Rest|Tacc],0); complete([{octets,_N,Val}|T],Acc,Acclen) when list(Val) -> % no security check here complete([{octets,Val}|T],Acc,Acclen); complete([],Acc,0) -> lists:reverse(Acc); complete([],[Hacc|Tacc],Acclen) when Acclen > 0-> Rest = 8 - Acclen, NewHacc = Hacc bsl Rest, lists:reverse([NewHacc|Tacc]).