%%
%% %CopyrightBegin%
%% 
%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
%% 
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%% 
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%% 
%% %CopyrightEnd%
%%

%%
%%----------------------------------------------------------------------
%% Purpose: Help functions for handling the SSL ciphers
%% 
%%----------------------------------------------------------------------

-module(ssl_cipher).

-include("ssl_internal.hrl").
-include("ssl_record.hrl").
-include("ssl_cipher.hrl").
-include("ssl_debug.hrl").

-export([security_parameters/2, suite_definition/1,
	 decipher/4, cipher/4, 
	 suite/1, suites/1,
	 openssl_suite/1, openssl_suite_name/1]).

-compile(inline).

%%--------------------------------------------------------------------
%% Function: security_parameters(CipherSuite, SecParams) -> 
%%                                              #security_parameters{}
%%
%% CipherSuite - as defined in ssl_cipher.hrl
%% SecParams - #security_parameters{}
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite> 
%%-------------------------------------------------------------------
security_parameters(CipherSuite, SecParams) ->
    { _, Cipher, Hash, Exportable} = suite_definition(CipherSuite),
    SecParams#security_parameters{
      cipher_suite = CipherSuite,
      bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher),
      cipher_type = type(Cipher),
      key_size = effective_key_bits(Cipher),
      expanded_key_material_length = expanded_key_material(Cipher),
      key_material_length = key_material(Cipher),
      iv_size = iv_size(Cipher),
      mac_algorithm = mac_algorithm(Hash),
      hash_size = hash_size(Hash),
      exportable = Exportable}.

%%--------------------------------------------------------------------
%% Function: cipher(Method, CipherState, Mac, Data) -> 
%%                                         {Encrypted, UpdateCipherState}
%%
%% Method - integer() (as defined in ssl_cipher.hrl)
%% CipherState, UpdatedCipherState - #cipher_state{}
%% Data, Encrypted - binary()
%%
%% Description: Encrypts the data and the mac using method, updating
%% the cipher state
%%-------------------------------------------------------------------
cipher(?NULL, CipherState, <<>>, Fragment) ->
    GenStreamCipherList = [Fragment, <<>>],
    {GenStreamCipherList, CipherState};
cipher(?RC4, CipherState, Mac, Fragment) ->
    State0 = case CipherState#cipher_state.state of
                 undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
                 S -> S
             end,
    GenStreamCipherList = [Fragment, Mac],

    ?DBG_HEX(GenStreamCipherList),
    ?DBG_HEX(State0),
    {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList),
    ?DBG_HEX(T),
    {T, CipherState#cipher_state{state = State1}};
cipher(?DES, CipherState, Mac, Fragment) ->
    block_cipher(fun(Key, IV, T) ->
			 crypto:des_cbc_encrypt(Key, IV, T)
		 end, block_size(des_cbc), CipherState, Mac, Fragment);
cipher(?DES40, CipherState, Mac, Fragment) ->
    block_cipher(fun(Key, IV, T) ->
			 crypto:des_cbc_encrypt(Key, IV, T)
		 end, block_size(des_cbc), CipherState, Mac, Fragment);
cipher(?'3DES', CipherState, Mac, Fragment) ->
    block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
			 crypto:des3_cbc_encrypt(K1, K2, K3, IV, T)
		 end, block_size(des_cbc), CipherState, Mac, Fragment);
cipher(?AES, CipherState, Mac, Fragment) ->
    block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
			 crypto:aes_cbc_128_encrypt(Key, IV, T);
		    (Key, IV, T) when byte_size(Key) =:= 32 ->
			 crypto:aes_cbc_256_encrypt(Key, IV, T)
		 end, block_size(aes_128_cbc), CipherState, Mac, Fragment);
%% cipher(?IDEA, CipherState, Mac, Fragment) ->
%%     block_cipher(fun(Key, IV, T) ->
%% 			 crypto:idea_cbc_encrypt(Key, IV, T)
%% 		 end, block_size(idea_cbc), CipherState, Mac, Fragment);
cipher(?RC2, CipherState, Mac, Fragment) ->
    block_cipher(fun(Key, IV, T) ->
			 crypto:rc2_40_cbc_encrypt(Key, IV, T)
		 end, block_size(rc2_cbc_40), CipherState, Mac, Fragment).

block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, 
	     Mac, Fragment) ->
    TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1,
    {PaddingLength, Padding} = get_padding(TotSz, BlockSz),
    L = [Fragment, Mac, PaddingLength, Padding],
    ?DBG_HEX(Key),
    ?DBG_HEX(IV),
    ?DBG_HEX(L),
    T = Fun(Key, IV, L),
    ?DBG_HEX(T),
    NextIV = next_iv(T, IV),
    {T, CS0#cipher_state{iv=NextIV}}.

%%--------------------------------------------------------------------
%% Function: decipher(Method, CipherState, Mac, Data) -> 
%%                                           {Decrypted, UpdateCipherState}
%%
%% Method - integer() (as defined in ssl_cipher.hrl)
%% CipherState, UpdatedCipherState - #cipher_state{}
%% Data, Encrypted - binary()
%%
%% Description: Decrypts the data and the mac using method, updating
%% the cipher state
%%-------------------------------------------------------------------
decipher(?NULL, _HashSz, CipherState, Fragment) ->
    {Fragment, <<>>, CipherState};
decipher(?RC4, HashSz, CipherState, Fragment) ->
    ?DBG_TERM(CipherState#cipher_state.key),
    State0 = case CipherState#cipher_state.state of
                 undefined -> crypto:rc4_set_key(CipherState#cipher_state.key);
                 S -> S
             end,
    ?DBG_HEX(State0),
    ?DBG_HEX(Fragment),
    {State1, T} = crypto:rc4_encrypt_with_state(State0, Fragment),
    ?DBG_HEX(T),
    GSC = generic_stream_cipher_from_bin(T, HashSz),
    #generic_stream_cipher{content=Content, mac=Mac} = GSC,
    {Content, Mac, CipherState#cipher_state{state=State1}};
decipher(?DES, HashSz, CipherState, Fragment) ->
    block_decipher(fun(Key, IV, T) ->
			   crypto:des_cbc_decrypt(Key, IV, T)
		   end, CipherState, HashSz, Fragment);
decipher(?DES40, HashSz, CipherState, Fragment) ->
    block_decipher(fun(Key, IV, T) ->
			   crypto:des_cbc_decrypt(Key, IV, T)
		   end, CipherState, HashSz, Fragment);
decipher(?'3DES', HashSz, CipherState, Fragment) ->
    block_decipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) ->
			   crypto:des3_cbc_decrypt(K1, K2, K3, IV, T)
		   end, CipherState, HashSz, Fragment);
decipher(?AES, HashSz, CipherState, Fragment) ->
    block_decipher(fun(Key, IV, T) when byte_size(Key) =:= 16 ->
			   crypto:aes_cbc_128_decrypt(Key, IV, T);
		      (Key, IV, T) when byte_size(Key) =:= 32 ->
			   crypto:aes_cbc_256_decrypt(Key, IV, T)
		   end, CipherState, HashSz, Fragment);
%% decipher(?IDEA, HashSz, CipherState, Fragment) ->
%%     block_decipher(fun(Key, IV, T) ->
%%  			   crypto:idea_cbc_decrypt(Key, IV, T)
%%  		   end, CipherState, HashSz, Fragment);
decipher(?RC2, HashSz, CipherState, Fragment) ->
    block_decipher(fun(Key, IV, T) ->
			   crypto:rc2_40_cbc_decrypt(Key, IV, T)
		   end, CipherState, HashSz, Fragment).

block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, 
	       HashSz, Fragment) ->
    ?DBG_HEX(Key),
    ?DBG_HEX(IV),
    ?DBG_HEX(Fragment),
    T = Fun(Key, IV, Fragment),
    ?DBG_HEX(T),
    GBC = generic_block_cipher_from_bin(T, HashSz),
    ok = check_padding(GBC),  %% TODO kolla också...
    Content = GBC#generic_block_cipher.content,
    Mac = GBC#generic_block_cipher.mac,
    CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)},
    {Content, Mac, CipherState1}.

%%--------------------------------------------------------------------
%% Function: suites(Version) -> [Suite]
%%
%% Version = version()
%% Suite = binary() from ssl_cipher.hrl
%%
%% Description: Returns a list of supported cipher suites.
%%--------------------------------------------------------------------
suites({3, 0}) ->
    ssl_ssl3:suites();
suites({3, N}) when N == 1; N == 2 ->
    ssl_tls1:suites().

%%--------------------------------------------------------------------
%% Function: suite_definition(CipherSuite) -> 
%%                                {KeyExchange, Cipher, Hash, Exportable}
%%                                             
%%
%% CipherSuite - as defined in ssl_cipher.hrl
%% KeyExchange - rsa | dh_dss | dh_rsa | dh_anon | dhe_dss | dhe_rsa
%%               krb5 | *_export (old ssl)
%% Cipher      - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc'
%%               des40_cbc | dh_dss | aes_128_cbc | aes_256_cbc |
%%               rc2_cbc_40 | rc4_40 
%% Hash        - null | md5 | sha
%% Exportable  - export | no_export | ignore(?)
%%
%% Description: Returns a security parameters record where the
%% cipher values has been updated according to <CipherSuite> 
%% Note: since idea is unsupported on the openssl version used by
%% crypto (as of OTP R12B), we've commented away the idea stuff
%%-------------------------------------------------------------------
%% TLS v1.1 suites
suite_definition(?TLS_NULL_WITH_NULL_NULL) ->
    {null, null, null, ignore};
suite_definition(?TLS_RSA_WITH_NULL_MD5) ->
    {rsa, null, md5, ignore};
suite_definition(?TLS_RSA_WITH_NULL_SHA) ->
    {rsa, null, sha, ignore};			
suite_definition(?TLS_RSA_WITH_RC4_128_MD5) ->	% ok
    {rsa, rc4_128, md5, no_export};
suite_definition(?TLS_RSA_WITH_RC4_128_SHA) ->	% ok
    {rsa, rc4_128, sha, no_export};
%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> % unsupported
%%     {rsa, idea_cbc, sha, no_export};
suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) ->	% ok
    {rsa, des_cbc, sha, no_export}; 
suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
    {rsa, '3des_ede_cbc', sha, no_export}; 
suite_definition(?TLS_DH_DSS_WITH_DES_CBC_SHA) ->
    {dh_dss, des_cbc, sha, no_export};
suite_definition(?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA) ->
    {dh_dss, '3des_ede_cbc', sha, no_export};
suite_definition(?TLS_DH_RSA_WITH_DES_CBC_SHA) ->
    {dh_rsa, des_cbc, sha, no_export};
suite_definition(?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA) ->
    {dh_rsa, '3des_ede_cbc', sha, no_export};
suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) ->
    {dhe_dss, des_cbc, sha, no_export};
suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
    {dhe_dss, '3des_ede_cbc', sha, no_export};
suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
    {dhe_rsa, des_cbc, sha, no_export};
suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
    {dhe_rsa, '3des_ede_cbc', sha, no_export}; 
suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) ->
    {dh_anon, rc4_128, md5, no_export};
suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) ->
    {dh_anon, des40_cbc, sha, no_export};
suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) ->
    {dh_anon, '3des_ede_cbc', sha, no_export};

%%% TSL V1.1 AES suites
suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> % ok
    {rsa, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DH_DSS_WITH_AES_128_CBC_SHA) ->
    {dh_dss, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DH_RSA_WITH_AES_128_CBC_SHA) ->
    {dh_rsa, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
    {dhe_dss, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
    {dhe_rsa, aes_128_cbc, sha, ignore};
suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) ->
    {dh_anon, aes_128_cbc, sha, ignore};
suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> % ok
    {rsa, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DH_DSS_WITH_AES_256_CBC_SHA) ->
    {dh_dss, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DH_RSA_WITH_AES_256_CBC_SHA) ->
    {dh_rsa, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
    {dhe_dss, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
    {dhe_rsa, aes_256_cbc, sha, ignore};
suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) ->
    {dh_anon, aes_256_cbc, sha, ignore};

%% TSL V1.1 KRB SUITES
suite_definition(?TLS_KRB5_WITH_DES_CBC_SHA) ->
    {krb5, des_cbc, sha, ignore};
suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_SHA) ->
    {krb5, '3des_ede_cbc', sha, ignore};
suite_definition(?TLS_KRB5_WITH_RC4_128_SHA) ->
    {krb5, rc4_128, sha, ignore};
%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_SHA) ->
%%     {krb5, idea_cbc, sha, ignore};
suite_definition(?TLS_KRB5_WITH_DES_CBC_MD5) ->
    {krb5, des_cbc, md5, ignore};
suite_definition(?TLS_KRB5_WITH_3DES_EDE_CBC_MD5) ->
    {krb5, '3des_ede_cbc', md5, ignore};
suite_definition(?TLS_KRB5_WITH_RC4_128_MD5) ->
    {krb5, rc4_128, md5, ignore};
%% suite_definition(?TLS_KRB5_WITH_IDEA_CBC_MD5) ->
%%     {krb5, idea_cbc, md5, ignore};

suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
    {rsa, rc4_56, md5, export};
suite_definition(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
    {rsa, rc2_cbc_56, md5, export};
suite_definition(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
    {rsa, des_cbc, sha, export};
suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
    {dhe_dss, des_cbc, sha, export};
suite_definition(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
    {rsa, rc4_56, sha, export};
suite_definition(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
    {dhe_dss, rc4_56, sha, export};
suite_definition(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
    {dhe_dss, rc4_128, sha, export};

%% Export suites  TLS 1.0 OR SSLv3-only servers.  
suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA) ->
    {krb5_export, des40_cbc, sha, export};
suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA) ->
    {krb5_export, rc2_cbc_40, sha, export};
suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_SHA) ->
    {krb5_export, des40_cbc, sha, export};
suite_definition(?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5) ->
    {krb5_export, des40_cbc, md5, export};
suite_definition(?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5) ->
    {krb5_export, rc2_cbc_40, md5, export};
suite_definition(?TLS_KRB5_EXPORT_WITH_RC4_40_MD5) ->
    {krb5_export, rc2_cbc_40, md5, export};
suite_definition(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> % ok
    {rsa, rc4_40, md5, export};	
suite_definition(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) -> % ok
    {rsa, rc2_cbc_40, md5, export};
suite_definition(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
    {rsa, des40_cbc, sha, export};
suite_definition(?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
    {dh_dss, des40_cbc, sha, export};
suite_definition(?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
    {dh_rsa, des40_cbc, sha, export};
suite_definition(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
    {dhe_dss, des40_cbc, sha, export};
suite_definition(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
    {dhe_rsa, des40_cbc, sha, export};
suite_definition(?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5) ->
    {dh_anon, rc4_40, md5, export};
suite_definition(?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA) ->
    {dh_anon, des40_cbc, sha, export}.

%% TLS v1.1 suites
suite({rsa, null, md5, ignore}) ->
    ?TLS_RSA_WITH_NULL_MD5;
suite({rsa, null, sha, ignore}) ->
    ?TLS_RSA_WITH_NULL_SHA;
suite({rsa, rc4_128, md5, no_export}) ->
    ?TLS_RSA_WITH_RC4_128_MD5;
suite({rsa, rc4_128, sha, no_export}) ->
    ?TLS_RSA_WITH_RC4_128_SHA;
%% suite({rsa, idea_cbc, sha, no_export}) -> 
%%     ?TLS_RSA_WITH_IDEA_CBC_SHA;
suite({rsa, des_cbc, sha, no_export}) ->
    ?TLS_RSA_WITH_DES_CBC_SHA; 
suite({rsa, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_RSA_WITH_3DES_EDE_CBC_SHA; 
suite({dh_dss, des_cbc, sha, no_export}) ->
    ?TLS_DH_DSS_WITH_DES_CBC_SHA;
suite({dh_dss, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA;
suite({dh_rsa, des_cbc, sha, no_export}) ->
    ?TLS_DH_RSA_WITH_DES_CBC_SHA;
suite({dh_rsa, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA;
suite({dhe_dss, des_cbc, sha, no_export}) ->
    ?TLS_DHE_DSS_WITH_DES_CBC_SHA;
suite({dhe_dss, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
suite({dhe_rsa, des_cbc, sha, no_export}) ->
    ?TLS_DHE_RSA_WITH_DES_CBC_SHA;
suite({dhe_rsa, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA; 
suite({dh_anon, rc4_128, md5, no_export}) ->
    ?TLS_DH_anon_WITH_RC4_128_MD5;
suite({dh_anon, des40_cbc, sha, no_export}) ->
    ?TLS_DH_anon_WITH_DES_CBC_SHA;
suite({dh_anon, '3des_ede_cbc', sha, no_export}) ->
    ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;

%%% TSL V1.1 AES suites
suite({rsa, aes_128_cbc, sha, ignore}) ->
    ?TLS_RSA_WITH_AES_128_CBC_SHA; 
suite({dh_dss, aes_128_cbc, sha, ignore}) ->
    ?TLS_DH_DSS_WITH_AES_128_CBC_SHA;
suite({dh_rsa, aes_128_cbc, sha, ignore}) ->
     ?TLS_DH_RSA_WITH_AES_128_CBC_SHA;
suite({dhe_dss, aes_128_cbc, sha, ignore}) ->
    ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; 
suite({dhe_rsa, aes_128_cbc, sha, ignore}) ->
    ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
suite({dh_anon, aes_128_cbc, sha, ignore}) ->
    ?TLS_DH_anon_WITH_AES_128_CBC_SHA;
suite({rsa, aes_256_cbc, sha, ignore}) ->
    ?TLS_RSA_WITH_AES_256_CBC_SHA;
suite({dh_dss, aes_256_cbc, sha, ignore}) ->
    ?TLS_DH_DSS_WITH_AES_256_CBC_SHA;
suite({dh_rsa, aes_256_cbc, sha, ignore}) ->
    ?TLS_DH_RSA_WITH_AES_256_CBC_SHA;
suite({dhe_dss, aes_256_cbc, sha, ignore}) ->
    ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
suite({dhe_rsa, aes_256_cbc, sha, ignore}) ->
    ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
suite({dh_anon, aes_256_cbc, sha, ignore}) ->
    ?TLS_DH_anon_WITH_AES_256_CBC_SHA;

%% TSL V1.1 KRB SUITES
suite({krb5, des_cbc, sha, ignore}) ->
    ?TLS_KRB5_WITH_DES_CBC_SHA;
suite({krb5_cbc, '3des_ede_cbc', sha, ignore}) ->
    ?TLS_KRB5_WITH_3DES_EDE_CBC_SHA;
suite({krb5, rc4_128, sha, ignore}) ->
     ?TLS_KRB5_WITH_RC4_128_SHA;
%% suite({krb5_cbc, idea_cbc, sha, ignore}) ->
%%     ?TLS_KRB5_WITH_IDEA_CBC_SHA;
suite({krb5_cbc, md5, ignore}) ->
    ?TLS_KRB5_WITH_DES_CBC_MD5;
suite({krb5_ede_cbc, des_cbc, md5, ignore}) ->
    ?TLS_KRB5_WITH_3DES_EDE_CBC_MD5;
suite({krb5_128, rc4_128, md5, ignore}) ->
    ?TLS_KRB5_WITH_RC4_128_MD5;
%% suite({krb5, idea_cbc, md5, ignore}) ->
%%     ?TLS_KRB5_WITH_IDEA_CBC_MD5;

%% Export suites  TLS 1.0 OR SSLv3-only servers.  
suite({rsa, rc4_40, md5, export}) ->	
    ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;
suite({rsa, rc2_cbc_40, md5, export}) ->
    ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
suite({rsa, des40_cbc, sha, export}) ->
    ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
suite({rsa, rc4_56, md5, export}) ->
    ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
suite({rsa, rc2_cbc_56, md5, export}) -> 
    ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
suite({rsa, des_cbc, sha, export}) ->
    ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
suite({dhe_dss, des_cbc, sha, export}) ->
    ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
suite({rsa, rc4_56, sha, export}) ->
    ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
suite({dhe_dss, rc4_56, sha, export}) ->
    ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
suite({dhe_dss, rc4_128, sha, export}) ->
    ?TLS_DHE_DSS_WITH_RC4_128_SHA;
suite({krb5_export, des40_cbc, sha, export}) ->
    ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA;
suite({krb5_export, rc2_cbc_40, sha, export}) ->
    ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA;
suite({krb5_export, rc4_cbc_40, sha, export}) ->
    ?TLS_KRB5_EXPORT_WITH_RC4_40_SHA;
suite({krb5_export, des40_cbc, md5, export}) ->
     ?TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5;
suite({krb5_export, rc2_cbc_40, md5, export}) ->
    ?TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5;
suite({krb5_export, rc4_cbc_40, md5, export}) ->
     ?TLS_KRB5_EXPORT_WITH_RC4_40_MD5;
suite({rsa_export, rc4_cbc_40, md5, export}) ->
    ?TLS_RSA_EXPORT_WITH_RC4_40_MD5;	
suite({rsa_export, rc2_cbc_40, md5, export}) ->
    ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
suite({rsa_export, des40_cbc, sha, export}) ->
    ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
suite({dh_dss_export, des40_cbc, sha, export}) ->
    ?TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA;
suite({dh_rsa_export, des40_cbc, sha, export}) ->
    ?TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA;
suite({dhe_dss_export, des40_cbc, sha, export}) ->
    ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
suite({dhe_rsa_export, des40_cbc, sha, export}) ->
    ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
suite({dh_anon_export, rc4_40, md5, export}) ->
    ?TLS_DH_anon_EXPORT_WITH_RC4_40_MD5;
suite({dh_anon_export, des40_cbc, sha, export}) ->
    ?TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA.


%% translate constants <-> openssl-strings
%% TODO: Is there a pattern in the nameing
%% that is useable to make a nicer function defention?

openssl_suite("DHE-RSA-AES256-SHA") ->
    ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("DHE-DSS-AES256-SHA") ->
    ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA;
openssl_suite("AES256-SHA") ->
    ?TLS_RSA_WITH_AES_256_CBC_SHA;
openssl_suite("EDH-RSA-DES-CBC3-SHA") ->
    ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
openssl_suite("EDH-DSS-DES-CBC3-SHA") ->
    ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA;
openssl_suite("DES-CBC3-SHA") ->
    ?TLS_RSA_WITH_3DES_EDE_CBC_SHA;
openssl_suite("DHE-RSA-AES128-SHA") ->
    ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
openssl_suite("DHE-DSS-AES128-SHA") ->
    ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA;
openssl_suite("AES128-SHA") ->
    ?TLS_RSA_WITH_AES_128_CBC_SHA;
%% TODO: Do we want to support this?
%% openssl_suite("DHE-DSS-RC4-SHA") ->
%%     ?TLS_DHE_DSS_WITH_RC4_128_SHA;
%%openssl_suite("IDEA-CBC-SHA") ->
%%    ?TLS_RSA_WITH_IDEA_CBC_SHA;
openssl_suite("RC4-SHA") ->
    ?TLS_RSA_WITH_RC4_128_SHA;
openssl_suite("RC4-MD5") -> 
    ?TLS_RSA_WITH_RC4_128_MD5;
%% TODO: Do we want to support this?
openssl_suite("EXP1024-RC4-MD5") ->
    ?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5;
openssl_suite("EXP1024-RC2-CBC-MD5") ->
    ?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5;
openssl_suite("EXP1024-DES-CBC-SHA") ->
    ?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA;
openssl_suite("EXP1024-DHE-DSS-DES-CBC-SHA") ->
    ?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA;
openssl_suite("EXP1024-RC4-SHA") ->
    ?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA;
openssl_suite("EXP1024-DHE-DSS-RC4-SHA") ->
    ?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA;
openssl_suite("DHE-DSS-RC4-SHA") ->
    ?TLS_DHE_DSS_WITH_RC4_128_SHA;

openssl_suite("EDH-RSA-DES-CBC-SHA") ->
    ?TLS_DHE_RSA_WITH_DES_CBC_SHA;
openssl_suite("DES-CBC-SHA") ->
    ?TLS_RSA_WITH_DES_CBC_SHA;
openssl_suite("EXP-EDH-RSA-DES-CBC-SHA") ->
    ?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA;
openssl_suite("EXP-EDH-DSS-DES-CBC-SHA") ->
    ?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA;
openssl_suite("EXP-DES-CBC-SHA") ->
    ?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA;
openssl_suite("EXP-RC2-CBC-MD5") ->
    ?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5;
openssl_suite("EXP-RC4-MD5") -> 
    ?TLS_RSA_EXPORT_WITH_RC4_40_MD5.

openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) ->
    "DHE-RSA-AES256-SHA";
openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) ->
    "DHE-DSS-AES256-SHA";
openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA) ->
    "AES256-SHA";
openssl_suite_name(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) ->
    "EDH-RSA-DES-CBC3-SHA";
openssl_suite_name(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) ->
    "EDH-DSS-DES-CBC3-SHA";
openssl_suite_name(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) ->
    "DES-CBC3-SHA";
openssl_suite_name( ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) ->
    "DHE-RSA-AES128-SHA";
openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) ->
    "DHE-DSS-AES128-SHA";
openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) ->
    "AES128-SHA";
%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) ->
%%     "IDEA-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) ->
    "RC4-SHA";
openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) -> 
    "RC4-MD5";
openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) ->
    "EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) ->
    "DES-CBC-SHA";
openssl_suite_name(?TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
    "EXP-EDH-RSA-DES-CBC-SHA";
openssl_suite_name(?TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA) ->
    "EXP-EDH-DSS-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_EXPORT_WITH_DES40_CBC_SHA) ->
    "EXP-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5) ->
    "EXP-RC2-CBC-MD5";
openssl_suite_name(?TLS_RSA_EXPORT_WITH_RC4_40_MD5) -> 
    "EXP-RC4-MD5";

openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_MD5) ->
    "EXP1024-RC4-MD5";
openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC2_CBC_56_MD5) ->
    "EXP1024-RC2-CBC-MD5";
openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_DES_CBC_SHA) ->
    "EXP1024-DES-CBC-SHA";
openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_DES_CBC_SHA) ->
    "EXP1024-DHE-DSS-DES-CBC-SHA";
openssl_suite_name(?TLS_RSA_EXPORT1024_WITH_RC4_56_SHA) ->
    "EXP1024-RC4-SHA";
openssl_suite_name(?TLS_DHE_DSS_EXPORT1024_WITH_RC4_56_SHA) ->
    "EXP1024-DHE-DSS-RC4-SHA";
openssl_suite_name(?TLS_DHE_DSS_WITH_RC4_128_SHA) ->
    "DHE-DSS-RC4-SHA";

%% No oppenssl name
openssl_suite_name(Cipher) ->
    suite_definition(Cipher).

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

bulk_cipher_algorithm(null) ->
    ?NULL;
%% Not supported yet
%% bulk_cipher_algorithm(idea_cbc) ->
%%     ?IDEA;
bulk_cipher_algorithm(Cipher) when Cipher == rc2_cbc_40;
				   Cipher == rc2_cbc_56 ->
    ?RC2;
bulk_cipher_algorithm(Cipher) when Cipher == rc4_40;
				   Cipher == rc4_56;
				   Cipher == rc4_128 ->
    ?RC4;
bulk_cipher_algorithm(des40_cbc) ->
    ?DES40;
bulk_cipher_algorithm(des_cbc) ->
    ?DES;
bulk_cipher_algorithm('3des_ede_cbc') ->
    ?'3DES';
bulk_cipher_algorithm(Cipher) when Cipher == aes_128_cbc;
				   Cipher == aes_256_cbc ->
    ?AES.

type(Cipher) when Cipher == null;
		  Cipher == rc4_40;
		  Cipher == rc4_56;
		  Cipher == rc4_128 ->
    ?STREAM;

type(Cipher) when Cipher == idea_cbc;
		  Cipher == rc2_cbc_40;
		  Cipher == rc2_cbc_56;
		  Cipher == des40_cbc;
		  Cipher == des_cbc;
		  Cipher == '3des_ede_cbc';
		  Cipher == aes_128_cbc;
		  Cipher == aes_256_cbc ->
    ?BLOCK.

key_material(null) ->
    0;
key_material(Cipher) when Cipher == idea_cbc;
 			  Cipher == rc4_128 ->
    16;
key_material(Cipher) when Cipher == rc2_cbc_56;
			  Cipher == rc4_56 ->
    7;
key_material(Cipher) when Cipher == rc2_cbc_40;
 			  Cipher == rc4_40;
 			  Cipher == des40_cbc ->
    5;
key_material(des_cbc) ->
    8;
key_material('3des_ede_cbc') ->
    24;
key_material(aes_128_cbc) ->
    16;
key_material(aes_256_cbc) ->
    32.

expanded_key_material(null) ->
    0;
expanded_key_material(Cipher) when Cipher == idea_cbc;
 				   Cipher == rc2_cbc_40;
 				   Cipher == rc2_cbc_56;
 				   Cipher == rc4_40;
				   Cipher == rc4_56;
 				   Cipher == rc4_128 ->
    16;
expanded_key_material(Cipher) when Cipher == des_cbc;
 				   Cipher == des40_cbc ->
    8;
expanded_key_material('3des_ede_cbc') ->
    24;
expanded_key_material(Cipher) when Cipher == aes_128_cbc;
 				   Cipher == aes_256_cbc ->
    unknown.  


effective_key_bits(null) ->
    0;
effective_key_bits(Cipher) when Cipher == rc2_cbc_40;
				Cipher == rc4_40;
				Cipher == des40_cbc ->
    40;
effective_key_bits(Cipher) when Cipher == rc2_cbc_56;
				Cipher == rc4_56;
				Cipher == des_cbc ->
    56;
effective_key_bits(Cipher) when Cipher == idea_cbc;
				Cipher == rc4_128;
				Cipher == aes_128_cbc ->
    128;
effective_key_bits('3des_ede_cbc') ->
    168;
effective_key_bits(aes_256_cbc) ->
    256.

iv_size(Cipher) when Cipher == null;
		     Cipher == rc4_40;
		     Cipher == rc4_56;
		     Cipher == rc4_128 ->
    0;
iv_size(Cipher) ->
    block_size(Cipher).

block_size(Cipher) when Cipher == idea_cbc;
			Cipher == rc2_cbc_40;
			Cipher == rc2_cbc_56;
			Cipher == des40_cbc;
			Cipher == des_cbc;
			Cipher == '3des_ede_cbc' -> 
    8;

block_size(Cipher) when Cipher == aes_128_cbc;
			Cipher == aes_256_cbc ->
    16.

mac_algorithm(null) ->
    ?NULL;
mac_algorithm(md5) ->
    ?MD5;
mac_algorithm(sha) ->
    ?SHA.

hash_size(null) ->
    0;
hash_size(md5) ->
    16;
hash_size(sha) ->
    20.

generic_block_cipher_from_bin(T, HashSize) ->
    Sz1 = byte_size(T) - 1,
    <<_:Sz1/binary, ?BYTE(PadLength)>> = T,
    CompressedLength = byte_size(T) - PadLength - 1 - HashSize,
    <<Content:CompressedLength/binary, Mac:HashSize/binary,
     Padding:PadLength/binary, ?BYTE(PadLength)>> = T,
    #generic_block_cipher{content=Content, mac=Mac,
			  padding=Padding, padding_length=PadLength}.

generic_stream_cipher_from_bin(T, HashSz) ->
    Sz = byte_size(T),
    CompressedLength = Sz - HashSz,
    <<Content:CompressedLength/binary, Mac:HashSz/binary>> = T,
    #generic_stream_cipher{content=Content,
			   mac=Mac}.

check_padding(_GBC) ->
    ok.

get_padding(Length, BlockSize) ->
    get_padding_aux(BlockSize, Length rem BlockSize).

get_padding_aux(_, 0) ->
    {0, <<>>};
get_padding_aux(BlockSize, PadLength) ->
    N = BlockSize - PadLength,
    {N, list_to_binary(lists:duplicate(N, N))}.

next_iv(Bin, IV) ->
    BinSz = byte_size(Bin),
    IVSz = byte_size(IV),
    FirstPart = BinSz - IVSz,
    <<_:FirstPart/binary, NextIV:IVSz/binary>> = Bin,
    NextIV.

