%%<copyright>
%% <year>2008-2008</year>
%% <holder>Ericsson AB, All Rights Reserved</holder>
%%</copyright>
%%<legalnotice>
%% 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.
%%
%% The Initial Developer of the Original Code is Ericsson AB.
%%</legalnotice>
%%

-module(pubkey_cert_records).

-include("public_key.hrl").

-export([decode_cert/2, encode_cert/1, encode_tbs_cert/1]).

-export([old_decode_cert/2, old_encode_cert/1]).  %% Debugging and testing new code.

%%====================================================================
%% Internal application API
%%====================================================================

decode_cert(DerCert, plain) ->
    'OTP-PUB-KEY':decode('Certificate', DerCert);
decode_cert(DerCert, otp) ->
    {ok, Cert} = 'OTP-PUB-KEY':decode('OTPCertificate', DerCert),
    {ok, decode_all_otp(Cert)}.

old_decode_cert(DerCert, otp) ->
    {ok, Cert} = 'OTP-PUB-KEY':decode('Certificate', DerCert),
    {ok, plain_to_otp(Cert)}.

old_encode_cert(Cert) ->
    PlainCert = otp_to_plain(Cert),
    {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', PlainCert),
    list_to_binary(EncCert).


encode_cert(Cert = #'Certificate'{}) ->
    {ok, EncCert} = 'OTP-PUB-KEY':encode('Certificate', Cert),
    list_to_binary(EncCert);
encode_cert(C = #'OTPCertificate'{tbsCertificate = TBS = 
				  #'OTPTBSCertificate'{
						     issuer=Issuer0,
						     subject=Subject0,
						     subjectPublicKeyInfo=Spki0,
						     extensions=Exts0}
				 }) ->
    Issuer  = transform(Issuer0,encode),
    Subject = transform(Subject0,encode),
    Spki = encode_supportedPublicKey(Spki0),
    Exts = encode_extensions(Exts0),
    %%    io:format("Extensions ~p~n",[Exts]),
    Cert = C#'OTPCertificate'{tbsCertificate=
			      TBS#'OTPTBSCertificate'{
				issuer=Issuer, subject=Subject,
				subjectPublicKeyInfo=Spki,
				extensions=Exts}},
    {ok, EncCert} = 'OTP-PUB-KEY':encode('OTPCertificate', Cert),
    list_to_binary(EncCert).

encode_tbs_cert(TBS = #'OTPTBSCertificate'{
		  issuer=Issuer0,
		  subject=Subject0,
		  subjectPublicKeyInfo=Spki0,
		  extensions=Exts0}) ->
    Issuer  = transform(Issuer0,encode),
    Subject = transform(Subject0,encode),
    Spki = encode_supportedPublicKey(Spki0),
    Exts = encode_extensions(Exts0),
    TBSCert = TBS#'OTPTBSCertificate'{issuer=Issuer,subject=Subject,
				      subjectPublicKeyInfo=Spki,extensions=Exts},
    {ok, EncTBSCert} = 'OTP-PUB-KEY':encode('OTPTBSCertificate', TBSCert),
    list_to_binary(EncTBSCert).

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------

decode_all_otp(C = #'OTPCertificate'{tbsCertificate = TBS = 
				     #'OTPTBSCertificate'{
							issuer=Issuer0,
							subject=Subject0,
							subjectPublicKeyInfo=Spki0,
							extensions=Exts0}
				    }) -> 
    Issuer  = transform(Issuer0,decode),
    Subject = transform(Subject0,decode),
    Spki = decode_supportedPublicKey(Spki0),
    Exts = decode_extensions(Exts0),
    %%    io:format("Extensions ~p~n",[Exts]),
    C#'OTPCertificate'{tbsCertificate=
		       TBS#'OTPTBSCertificate'{
			 issuer=Issuer, subject=Subject,
			 subjectPublicKeyInfo=Spki,extensions=Exts}}.


%%% SubjectPublicKey
supportedPublicKeyAlgorithms(?'rsaEncryption') -> 'RSAPublicKey';
supportedPublicKeyAlgorithms(?'id-dsa') -> 'DSAPublicKey';
supportedPublicKeyAlgorithms(?'dhpublicnumber') -> 'DHPublicKey';
supportedPublicKeyAlgorithms(?'id-keyExchangeAlgorithm') -> 'KEA-PublicKey';
supportedPublicKeyAlgorithms(?'id-ecPublicKey') -> 'ECPoint'.

decode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA =
						  #'PublicKeyAlgorithm'{algorithm=Algo},
						  subjectPublicKey = {0,SPK0}}) ->
    Type = supportedPublicKeyAlgorithms(Algo),
    {ok, SPK} = 'OTP-PUB-KEY':decode(Type, SPK0),
    #'OTPSubjectPublicKeyInfo'{subjectPublicKey = SPK, algorithm=PA}.

encode_supportedPublicKey(#'OTPSubjectPublicKeyInfo'{algorithm= PA =
						     #'PublicKeyAlgorithm'{algorithm=Algo},
						     subjectPublicKey = SPK0}) ->
    Type = supportedPublicKeyAlgorithms(Algo),
    {ok, SPK} = 'OTP-PUB-KEY':encode(Type, SPK0),
    #'OTPSubjectPublicKeyInfo'{subjectPublicKey = {0,list_to_binary(SPK)}, algorithm=PA}.

%%% Extensions

extension_id(?'id-ce-authorityKeyIdentifier') ->  'AuthorityKeyIdentifier';
extension_id(?'id-ce-subjectKeyIdentifier') ->    'SubjectKeyIdentifier';
extension_id(?'id-ce-keyUsage') -> 	          'KeyUsage';
extension_id(?'id-ce-privateKeyUsagePeriod') ->   'PrivateKeyUsagePeriod';
extension_id(?'id-ce-certificatePolicies') -> 	  'CertificatePolicies';
extension_id(?'id-ce-policyMappings') -> 	  'PolicyMappings';
extension_id(?'id-ce-subjectAltName') -> 	  'SubjectAltName'; 	        
extension_id(?'id-ce-issuerAltName') -> 	  'IssuerAltName'; 	        
extension_id(?'id-ce-subjectDirectoryAttributes') -> 	  'SubjectDirectoryAttributes';
extension_id(?'id-ce-basicConstraints' ) -> 	  'BasicConstraints';	        
extension_id(?'id-ce-nameConstraints') -> 	  'NameConstraints'; 	        
extension_id(?'id-ce-policyConstraints') -> 	  'PolicyConstraints'; 	
extension_id(?'id-ce-cRLDistributionPoints') ->   'CRLDistributionPoints'; 	
extension_id(?'id-ce-extKeyUsage') -> 	          'ExtKeyUsageSyntax'; 	        
extension_id(?'id-ce-inhibitAnyPolicy') -> 	  'InhibitAnyPolicy'; 	        
extension_id(?'id-ce-freshestCRL') -> 	          'FreshestCRL';
%% Missing in public_key doc 
extension_id(?'id-pe-authorityInfoAccess') -> 	  'AuthorityInfoAccessSyntax';
extension_id(?'id-pe-subjectInfoAccess') -> 	  'SubjectInfoAccessSyntax';
extension_id(?'id-ce-cRLNumber') -> 	          'CRLNumber';
extension_id(?'id-ce-issuingDistributionPoint') -> 'IssuingDistributionPoint';
extension_id(?'id-ce-deltaCRLIndicator') -> 	   'BaseCRLNumber';
extension_id(?'id-ce-cRLReasons') -> 	          'CRLReason';
extension_id(?'id-ce-certificateIssuer') -> 	  'CertificateIssuer';
extension_id(?'id-ce-holdInstructionCode') -> 	  'HoldInstructionCode';
extension_id(?'id-ce-invalidityDate') -> 	  'InvalidityDate';
extension_id(_) ->
    undefined.
     

decode_extensions(asn1_NOVALUE) ->
    asn1_NOVALUE;

decode_extensions(Exts) ->
    lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) ->
		      case extension_id(Id) of
			  undefined -> Ext;
			  Type ->
			      {ok, Value} = 'OTP-PUB-KEY':decode(Type, list_to_binary(Value0)),
			      Ext#'Extension'{extnValue=transform(Value,decode)}
		      end
	      end, Exts).

encode_extensions(asn1_NOVALUE) ->
    asn1_NOVALUE;

encode_extensions(Exts) ->
    lists:map(fun(Ext = #'Extension'{extnID=Id, extnValue=Value0}) ->
		      case extension_id(Id) of
			  undefined -> Ext;			  
			  Type ->
			      Value1 = transform(Value0,encode),
			      {ok, Value} = 'OTP-PUB-KEY':encode(Type, Value1),
			      Ext#'Extension'{extnValue=list_to_binary(Value)}
		      end
	      end, Exts).

transform(#'AttributeTypeAndValue'{type=Id,value=Value0} = ATAV, Func) ->
    Type = attribute_type(Id),
    {ok, Value} = 'OTP-PUB-KEY':Func(Type, Value0),
    ATAV#'AttributeTypeAndValue'{value=Value};
transform(AKI = #'AuthorityKeyIdentifier'{authorityCertIssuer=ACI},Func) ->
    AKI#'AuthorityKeyIdentifier'{authorityCertIssuer=transform(ACI,Func)};
transform(List = [{directoryName, _}],Func) ->
    [{directoryName, transform(Value,Func)} || {directoryName, Value} <- List];
transform({directoryName, Value},Func) ->
    {directoryName, transform(Value,Func)};
transform({rdnSequence, SeqList},Func) when is_list(SeqList) ->
    {rdnSequence, 
     lists:map(fun(Seq) -> 
		       lists:map(fun(Element) -> transform(Element,Func) end, Seq)
	       end, SeqList)};
%% transform(List = [{rdnSequence, _}|_],Func)  ->
%%     lists:map(fun(Element) -> transform(Element,Func) end, List);
transform(#'NameConstraints'{permittedSubtrees=Permitted, excludedSubtrees=Excluded}, Func) ->
    Res = #'NameConstraints'{permittedSubtrees=transform_sub_tree(Permitted,Func),
			     excludedSubtrees=transform_sub_tree(Excluded,Func)},
%%    io:format("~p~n",[Res]),
    Res;
transform(Other,_) ->
    Other.
transform_sub_tree(asn1_NOVALUE,_) -> asn1_NOVALUE;
transform_sub_tree(TreeList,Func) ->
    [Tree#'GeneralSubtree'{base=transform(Name,Func)} || 
	Tree = #'GeneralSubtree'{base=Name} <- TreeList].

attribute_type(?'id-at-name') -> 'X520name';
attribute_type(?'id-at-surname') -> 'X520name';
attribute_type(?'id-at-givenName') -> 'X520name';
attribute_type(?'id-at-initials') -> 'X520name';
attribute_type(?'id-at-generationQualifier') -> 'X520name';
attribute_type(?'id-at-commonName') -> 'X520CommonName';
attribute_type(?'id-at-localityName') -> 'X520LocalityName';
attribute_type(?'id-at-stateOrProvinceName') -> 'X520StateOrProvinceName';
attribute_type(?'id-at-organizationName') -> 'X520OrganizationName';
attribute_type(?'id-at-organizationalUnitName') -> 'X520OrganizationalUnitName';
attribute_type(?'id-at-title') -> 'X520Title';
attribute_type(?'id-at-dnQualifier') -> 'X520dnQualifier';
attribute_type(?'id-at-countryName') -> 'X520countryName';
attribute_type(?'id-at-serialNumber') -> 'X520SerialNumber';
attribute_type(?'id-at-pseudonym') -> 'X520Pseudonym';
attribute_type(?'id-domainComponent') -> 'DomainComponent';
attribute_type(?'id-emailAddress') -> 'EmailAddress'.

%%% Old code transforms

plain_to_otp(#'Certificate'{tbsCertificate = TBSCert,
			    signatureAlgorithm = SigAlg,
			    signature = Signature} = Cert) ->
    Cert#'Certificate'{tbsCertificate = plain_to_otp(TBSCert), 
		       signatureAlgorithm = plain_to_otp(SigAlg),
		       signature = plain_to_otp(Signature)};

plain_to_otp(#'TBSCertificate'{signature = Signature,
			       issuer = Issuer,
			       subject = Subject,
			       subjectPublicKeyInfo = SPubKeyInfo,
			       extensions = Extensions} = TBSCert) ->
    
    TBSCert#'TBSCertificate'{signature = plain_to_otp(Signature),
			     issuer = plain_to_otp(Issuer),
			     subject = 
			     plain_to_otp(Subject),
			     subjectPublicKeyInfo = 
			     plain_to_otp(SPubKeyInfo),
			     extensions = 
			     plain_to_otp_extensions(Extensions)
			    };

plain_to_otp(#'AlgorithmIdentifier'{algorithm = Algorithm, 
				    parameters = Params}) ->
    SignAlgAny = 
	#'SignatureAlgorithm-Any'{algorithm = Algorithm, 
				  parameters = Params},
    {ok, AnyEnc} = 'OTP-PUB-KEY':encode('SignatureAlgorithm-Any', 
					SignAlgAny),
    {ok, SignAlg} = 'OTP-PUB-KEY':decode('SignatureAlgorithm', 
					list_to_binary(AnyEnc)),
    SignAlg;

plain_to_otp({rdnSequence, SeqList}) when is_list(SeqList) ->
    {rdnSequence, 
     lists:map(fun(Seq) -> 
		       lists:map(fun(Element) -> 
					 plain_to_otp(Element)
				 end,
				 Seq)
	       end, SeqList)};

plain_to_otp(#'AttributeTypeAndValue'{} = ATAV) ->
    {ok, ATAVEnc} = 
	'OTP-PUB-KEY':encode('AttributeTypeAndValue', ATAV),
    {ok, ATAVDec} = 'OTP-PUB-KEY':decode('OTPAttributeTypeAndValue', 
				      list_to_binary(ATAVEnc)),
    #'AttributeTypeAndValue'{type = ATAVDec#'OTPAttributeTypeAndValue'.type,
			     value =  
			     ATAVDec#'OTPAttributeTypeAndValue'.value};

plain_to_otp(#'SubjectPublicKeyInfo'{algorithm = 
				     #'AlgorithmIdentifier'{algorithm 
							    = Algo,
							    parameters = 
							    Params},
				     subjectPublicKey = PublicKey}) ->
    
    AnyAlgo = #'PublicKeyAlgorithm'{algorithm = Algo, 
				    parameters = Params},
    {0, AnyKey} = PublicKey,
    AnyDec = #'OTPSubjectPublicKeyInfo-Any'{algorithm = AnyAlgo,
					      subjectPublicKey = AnyKey},
    {ok, AnyEnc} = 
	'OTP-PUB-KEY':encode('OTPSubjectPublicKeyInfo-Any', AnyDec),
    {ok, InfoDec} = 'OTP-PUB-KEY':decode('OTPOLDSubjectPublicKeyInfo', 
					 list_to_binary(AnyEnc)),

    AlgorithmDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.algorithm,
    AlgoDec = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.algo, 
    NewParams = AlgorithmDec#'OTPOLDSubjectPublicKeyInfo_algorithm'.parameters,
    PublicKeyDec = InfoDec#'OTPOLDSubjectPublicKeyInfo'.subjectPublicKey,
    NewAlgorithmDec = 
	#'SubjectPublicKeyInfoAlgorithm'{algorithm = AlgoDec, 
					  parameters = NewParams},
    #'SubjectPublicKeyInfo'{algorithm = NewAlgorithmDec,
			    subjectPublicKey = PublicKeyDec
			   };

plain_to_otp(#'Extension'{extnID = ExtID, 
			  critical = Critical,
			  extnValue = Value}) 
  when  ExtID == ?'id-ce-authorityKeyIdentifier';
	ExtID == ?'id-ce-subjectKeyIdentifier';
	ExtID == ?'id-ce-keyUsage';
	ExtID == ?'id-ce-privateKeyUsagePeriod';
	ExtID == ?'id-ce-certificatePolicies';
	ExtID == ?'id-ce-policyMappings';
	ExtID == ?'id-ce-subjectAltName';
	ExtID == ?'id-ce-issuerAltName';
	ExtID == ?'id-ce-subjectDirectoryAttributes';
	ExtID == ?'id-ce-basicConstraints';
	ExtID == ?'id-ce-nameConstraints';
	ExtID == ?'id-ce-policyConstraints';
	ExtID == ?'id-ce-extKeyUsage';
	ExtID == ?'id-ce-cRLDistributionPoints';
	ExtID == ?'id-ce-inhibitAnyPolicy';
	ExtID == ?'id-ce-freshestCRL' ->    
    ExtAny = #'Extension-Any'{extnID = ExtID,
			      critical = Critical,
			      extnValue = Value},
    {ok, AnyEnc} = 'OTP-PUB-KEY':encode('Extension-Any', ExtAny),
    {ok, ExtDec} =  'OTP-PUB-KEY':decode('OTPExtension', 
				      list_to_binary(AnyEnc)),
    
    ExtValue = plain_to_otp_extension_value(ExtID, 
					    ExtDec#'OTPExtension'.extnValue),
    #'Extension'{extnID = ExtID,
		 critical = ExtDec#'OTPExtension'.critical,
		 extnValue = ExtValue};

plain_to_otp(#'Extension'{} = Ext) ->
    Ext;

plain_to_otp(#'AuthorityKeyIdentifier'{} = Ext) ->
    CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
    Ext#'AuthorityKeyIdentifier'{authorityCertIssuer = 
				 plain_to_otp(CertIssuer)};


plain_to_otp([{directoryName, Value}]) ->
    [{directoryName, plain_to_otp(Value)}];

plain_to_otp(Value) ->
    Value.

otp_to_plain(#'Certificate'{tbsCertificate = TBSCert,
			    signatureAlgorithm = SigAlg,
			    signature = Signature} = Cert) ->
    Cert#'Certificate'{tbsCertificate = otp_to_plain(TBSCert), 
		       signatureAlgorithm = 
		       otp_to_plain(SigAlg),
		       signature = otp_to_plain(Signature)};

otp_to_plain(#'TBSCertificate'{signature = Signature,
			       issuer = Issuer,
			       subject = Subject,
			       subjectPublicKeyInfo = SPubKeyInfo,
			       extensions = Extensions} = TBSCert) ->
    
    TBSCert#'TBSCertificate'{signature = otp_to_plain(Signature),
			     issuer = otp_to_plain(Issuer),
			     subject = 
			     otp_to_plain(Subject),
			     subjectPublicKeyInfo = 
			     otp_to_plain(SPubKeyInfo),
			     extensions = otp_to_plain_extensions(Extensions)
			    };

otp_to_plain(#'SignatureAlgorithm'{} = SignAlg) ->
    {ok, EncSignAlg} = 'OTP-PUB-KEY':encode('SignatureAlgorithm', SignAlg),
    {ok, #'SignatureAlgorithm-Any'{algorithm = Algorithm, 
				   parameters = Params}} = 
	'OTP-PUB-KEY':decode('SignatureAlgorithm-Any', 
			     list_to_binary(EncSignAlg)),
    #'AlgorithmIdentifier'{algorithm = Algorithm, 
			   parameters = Params};

otp_to_plain({rdnSequence, SeqList}) when is_list(SeqList) ->
    {rdnSequence, 
     lists:map(fun(Seq) -> 
		       lists:map(fun(Element) -> 
					 otp_to_plain(Element)
				 end,
				 Seq)
	       end, SeqList)};

otp_to_plain(#'AttributeTypeAndValue'{type = Type, value = Value}) ->
    {ok, ATAVEnc} = 
	'OTP-PUB-KEY':encode('OTPAttributeTypeAndValue', 
			     #'OTPAttributeTypeAndValue'{type = Type, 
							 value = Value}),
    {ok, ATAVDec} = 'OTP-PUB-KEY':decode('AttributeTypeAndValue', 
					 list_to_binary(ATAVEnc)),
    ATAVDec;

otp_to_plain(#'SubjectPublicKeyInfo'{algorithm = 
				     #'SubjectPublicKeyInfoAlgorithm'{
				       algorithm = Algo,
				       parameters = 
				       Params},
				     subjectPublicKey = PublicKey}) ->
    
    OtpAlgo = #'OTPOLDSubjectPublicKeyInfo_algorithm'{algo = Algo,
						   parameters = Params},
    OtpDec = #'OTPOLDSubjectPublicKeyInfo'{algorithm = OtpAlgo,
					    subjectPublicKey = PublicKey},
    {ok, OtpEnc} = 
	'OTP-PUB-KEY':encode('OTPOLDSubjectPublicKeyInfo', OtpDec),
    
    {ok, AnyDec} = 'OTP-PUB-KEY':decode('OTPSubjectPublicKeyInfo-Any', 
					list_to_binary(OtpEnc)),
    
    #'OTPSubjectPublicKeyInfo-Any'{algorithm = #'PublicKeyAlgorithm'{
				     algorithm = NewAlgo,
				     parameters = NewParams},
				   subjectPublicKey = Bin} = AnyDec,
    
    #'SubjectPublicKeyInfo'{algorithm = 
			    #'AlgorithmIdentifier'{
			      algorithm = NewAlgo,
			      parameters = plain_key_params(NewParams)}, 
			    subjectPublicKey = 
			    {0, Bin}
			   };

otp_to_plain(#'Extension'{extnID = ExtID, 
			  extnValue = Value} = Ext) ->
    ExtValue = 
	otp_to_plain_extension_value(ExtID, Value),
    
    Ext#'Extension'{extnValue = ExtValue};

otp_to_plain(#'AuthorityKeyIdentifier'{} = Ext) ->
    CertIssuer = Ext#'AuthorityKeyIdentifier'.authorityCertIssuer,
    Ext#'AuthorityKeyIdentifier'{authorityCertIssuer = 
				 otp_to_plain(CertIssuer)};

otp_to_plain([{directoryName, Value}]) ->
    [{directoryName, otp_to_plain(Value)}];

otp_to_plain(Value) ->
    Value.

plain_key_params('NULL') ->
   <<5,0>>;
plain_key_params(Value) ->
    Value.

plain_to_otp_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
    plain_to_otp(Value);
plain_to_otp_extension_value(_, Value) ->
    Value.

plain_to_otp_extensions(Exts) when is_list(Exts) ->
    lists:map(fun(Ext) -> plain_to_otp(Ext) end, Exts).

otp_to_plain_extension_value(?'id-ce-authorityKeyIdentifier', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('AuthorityKeyIdentifier',
				     otp_to_plain(Value)),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-subjectKeyIdentifier', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectKeyIdentifier', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-keyUsage', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('KeyUsage', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-privateKeyUsagePeriod', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('PrivateKeyUsagePeriod', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-certificatePolicies', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('CertificatePolicies', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-policyMappings', Value) ->
    {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyMappings', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-subjectAltName', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectAltName', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-issuerAltName', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('IssuerAltName', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-subjectDirectoryAttributes', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('SubjectDirectoryAttributes', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-basicConstraints', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('BasicConstraints', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-nameConstraints', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('NameConstraints', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-policyConstraints', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('PolicyConstraints', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-extKeyUsage', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('ExtKeyUsage', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-cRLDistributionPoints', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('CRLDistributionPoints', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-inhibitAnyPolicy', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('InhibitAnyPolicy', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(?'id-ce-freshestCRL', Value) ->
 {ok, Enc} = 'OTP-PUB-KEY':encode('FreshestCRL', Value),
    otp_to_plain_extension_value_format(Enc);
otp_to_plain_extension_value(_Id, Value) ->
    Value.

otp_to_plain_extension_value_format(Value) -> 
    list_to_binary(Value).

otp_to_plain_extensions(Exts) when is_list(Exts) ->
    lists:map(fun(Ext) ->
		      otp_to_plain(Ext)
	      end, Exts).
