diff options
author | Ingela Anderton Andin <[email protected]> | 2010-06-29 11:21:41 +0200 |
---|---|---|
committer | Ingela Anderton Andin <[email protected]> | 2010-06-29 11:21:41 +0200 |
commit | 5ef0b06ddbaa48499394c30d56fc81e7162abf50 (patch) | |
tree | 09960a4dd828d06b05a48e656657cf7949bda4b0 /lib/ssl/src | |
parent | 249613f10368dc694edb0a962d6025e19940b5af (diff) | |
parent | 80e96ac836975c537ebbb7588cce9390775e20db (diff) | |
download | otp-5ef0b06ddbaa48499394c30d56fc81e7162abf50.tar.gz otp-5ef0b06ddbaa48499394c30d56fc81e7162abf50.tar.bz2 otp-5ef0b06ddbaa48499394c30d56fc81e7162abf50.zip |
Merge branch 'ia/ssl/new-doc' into dev
Diffstat (limited to 'lib/ssl/src')
-rw-r--r-- | lib/ssl/src/ssl.erl | 109 | ||||
-rw-r--r-- | lib/ssl/src/ssl_alert.erl | 25 | ||||
-rw-r--r-- | lib/ssl/src/ssl_app.erl | 12 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 53 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate_db.erl | 30 | ||||
-rw-r--r-- | lib/ssl/src/ssl_cipher.erl | 73 | ||||
-rw-r--r-- | lib/ssl/src/ssl_cipher.hrl | 8 | ||||
-rw-r--r-- | lib/ssl/src/ssl_connection.erl | 261 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 135 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 24 | ||||
-rw-r--r-- | lib/ssl/src/ssl_manager.erl | 98 | ||||
-rw-r--r-- | lib/ssl/src/ssl_record.erl | 116 | ||||
-rw-r--r-- | lib/ssl/src/ssl_session.erl | 32 | ||||
-rw-r--r-- | lib/ssl/src/ssl_session_cache.erl | 45 | ||||
-rw-r--r-- | lib/ssl/src/ssl_ssl3.erl | 14 | ||||
-rw-r--r-- | lib/ssl/src/ssl_tls1.erl | 16 |
16 files changed, 542 insertions, 509 deletions
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 35d27713b8..df4cd7c84d 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -34,10 +34,12 @@ %% Should be deprecated as soon as old ssl is removed %%-deprecated({pid, 1, next_major_release}). +-deprecated({peercert, 2, next_major_release}). -include("ssl_int.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_cipher.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -49,11 +51,11 @@ }). %%-------------------------------------------------------------------- -%% Function: start([, Type]) -> ok +-spec start() -> ok. +-spec start(permanent | transient | temporary) -> ok. %% -%% Type = permanent | transient | temporary -%% -%% Description: Starts the ssl application. Default type +%% Description: Utility function that starts the ssl, +%% crypto and public_key applications. Default type %% is temporary. see application(3) %%-------------------------------------------------------------------- start() -> @@ -67,7 +69,7 @@ start(Type) -> application:start(ssl, Type). %%-------------------------------------------------------------------- -%% Function: stop() -> ok +-spec stop() -> ok. %% %% Description: Stops the ssl application. %%-------------------------------------------------------------------- @@ -75,7 +77,8 @@ stop() -> application:stop(ssl). %%-------------------------------------------------------------------- -%% Function: connect(Address, Port, Options[, Timeout]) -> {ok, Socket} +-spec connect(host() | port(), port_num(), list()) -> {ok, #sslsocket{}}. +-spec connect(host() | port(), port_num(), list(), timeout()) -> {ok, #sslsocket{}}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -101,13 +104,13 @@ connect(Socket, SslOptions0, Timeout) when is_port(Socket) -> {error, Reason} end; -connect(Address, Port, Options) -> - connect(Address, Port, Options, infinity). +connect(Host, Port, Options) -> + connect(Host, Port, Options, infinity). -connect(Address, Port, Options0, Timeout) -> +connect(Host, Port, Options0, Timeout) -> case proplists:get_value(ssl_imp, Options0, new) of new -> - new_connect(Address, Port, Options0, Timeout); + new_connect(Host, Port, Options0, Timeout); old -> %% Allow the option reuseaddr to be present %% so that new and old ssl can be run by the same @@ -115,13 +118,14 @@ connect(Address, Port, Options0, Timeout) -> %% that hardcodes reuseaddr to true in its portprogram. Options1 = proplists:delete(reuseaddr, Options0), Options = proplists:delete(ssl_imp, Options1), - old_connect(Address, Port, Options, Timeout); + old_connect(Host, Port, Options, Timeout); Value -> {error, {eoptions, {ssl_imp, Value}}} end. %%-------------------------------------------------------------------- -%% Function: listen(Port, Options) -> {ok, ListenSock} | {error, Reason} +-spec listen(port_num(), list()) ->{ok, #sslsocket{}} | {error, reason()}. + %% %% Description: Creates a ssl listen socket. %%-------------------------------------------------------------------- @@ -144,7 +148,8 @@ listen(Port, Options0) -> end. %%-------------------------------------------------------------------- -%% Function: transport_accept(ListenSocket[, Timeout]) -> {ok, Socket}. +-spec transport_accept(#sslsocket{}) -> {ok, #sslsocket{}}. +-spec transport_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}}. %% %% Description: Performs transport accept on a ssl listen socket %%-------------------------------------------------------------------- @@ -182,8 +187,8 @@ transport_accept(#sslsocket{} = ListenSocket, Timeout) -> ssl_broker:transport_accept(Pid, ListenSocket, Timeout). %%-------------------------------------------------------------------- -%% Function: ssl_accept(ListenSocket[, Timeout]) -> {ok, Socket} | -%% {error, Reason} +-spec ssl_accept(#sslsocket{}) -> {ok, #sslsocket{}} | {error, reason()}. +-spec ssl_accept(#sslsocket{}, timeout()) -> {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -217,7 +222,7 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(#sslsocket{}) -> term(). %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -230,7 +235,7 @@ close(Socket = #sslsocket{}) -> ssl_broker:close(Socket). %%-------------------------------------------------------------------- -%% Function: send(Socket, Data) -> ok | {error, Reason} +-spec send(#sslsocket{}, iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -242,7 +247,8 @@ send(#sslsocket{} = Socket, Data) -> ssl_broker:send(Socket, Data). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length [,Timeout]) -> {ok, Data} | {error, reason} +-spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. +-spec recv(#sslsocket{}, integer(), timeout()) -> {ok, binary()| list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- @@ -256,8 +262,8 @@ recv(Socket = #sslsocket{}, Length, Timeout) -> ssl_broker:recv(Socket, Length, Timeout). %%-------------------------------------------------------------------- -%% Function: controlling_process(Socket, NewOwner) -> ok | {error, Reason} -%% +-spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. +%% %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- @@ -270,11 +276,8 @@ controlling_process(Socket, NewOwner) when is_pid(NewOwner) -> ssl_broker:controlling_process(Socket, NewOwner). %%-------------------------------------------------------------------- -%% Function: connection_info(Socket) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} -%% Protocol = sslv3 | tlsv1 | tlsv1.1 -%% CipherSuite = {KeyExchange, Chipher, Hash, Exportable} -%% +-spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | + {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -286,9 +289,9 @@ connection_info(#sslsocket{} = Socket) -> ssl_broker:connection_info(Socket). %%-------------------------------------------------------------------- -%% Function: peercert(Socket[, Opts]) -> {ok, Cert} | {error, Reason} +-spec peercert(#sslsocket{}) ->{ok, der_cert()} | {error, reason()}. %% -%% Description: +%% Description: Returns the peercert. %%-------------------------------------------------------------------- peercert(Socket) -> peercert(Socket, []). @@ -342,9 +345,9 @@ select_part(plain, {ok, Cert}, Opts) -> end. %%-------------------------------------------------------------------- -%% Function: peername(Socket) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% -%% Description: +%% Description: same as inet:peername/1. %%-------------------------------------------------------------------- peername(#sslsocket{fd = new_ssl, pid = Pid}) -> ssl_connection:peername(Pid); @@ -354,9 +357,10 @@ peername(#sslsocket{} = Socket) -> ssl_broker:peername(Socket). %%-------------------------------------------------------------------- -%% Function: cipher_suites() -> -%% -%% Description: +-spec cipher_suites() -> [erl_cipher_suite()]. +-spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. + +%% Description: Returns all supported cipher suites. %%-------------------------------------------------------------------- cipher_suites() -> cipher_suites(erlang). @@ -370,7 +374,7 @@ cipher_suites(openssl) -> [ssl_cipher:openssl_suite_name(S) || S <- ssl_cipher:suites(Version)]. %%-------------------------------------------------------------------- -%% Function: getopts(Socket, OptTags) -> {ok, Options} | {error, Reason} +-spec getopts(#sslsocket{}, [atom()]) -> {ok, [{atom(), term()}]}| {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -383,7 +387,7 @@ getopts(#sslsocket{} = Socket, Options) -> ssl_broker:getopts(Socket, Options). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec setopts(#sslsocket{}, [{atom(), term()}]) -> ok | {error, reason()}. %% %% Description: %%-------------------------------------------------------------------- @@ -398,8 +402,8 @@ setopts(#sslsocket{} = Socket, Options) -> ssl_broker:setopts(Socket, Options). %%--------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} -%% +-spec shutdown(#sslsocket{}, read | write | read_write) -> ok | {error, reason()}. +%% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}, fd = new_ssl}, How) -> @@ -408,8 +412,8 @@ shutdown(#sslsocket{pid = Pid, fd = new_ssl}, How) -> ssl_connection:shutdown(Pid, How). %%-------------------------------------------------------------------- -%% Function: sockname(Socket) -> {ok, {Address, Port}} | {error, Reason} -%% +-spec sockname(#sslsocket{}) -> {ok, {tuple(), port_num()}} | {error, reason()}. +%% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}) -> @@ -423,9 +427,9 @@ sockname(#sslsocket{} = Socket) -> ssl_broker:sockname(Socket). %%--------------------------------------------------------------- -%% Function: seed(Data) -> ok | {error, edata} +-spec seed(term()) ->term(). %% -%% Description: +%% Description: Only used by old ssl. %%-------------------------------------------------------------------- %% TODO: crypto:seed ? seed(Data) -> @@ -433,20 +437,17 @@ seed(Data) -> ssl_server:seed(Data). %%--------------------------------------------------------------- -%% Function: session_id(Socket) -> {ok, PropList} | {error, Reason} +-spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. %% -%% Description: +%% Description: Returns list of session info currently [{session_id, session_id(), +%% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:session_info(Pid). %%--------------------------------------------------------------- -%% Function: versions() -> [{SslAppVer, SupportedSslVer, AvailableSslVsn}] -%% -%% SslAppVer = string() - t.ex: ssl-4.0 -%% SupportedSslVer = [SslVer] -%% AvailableSslVsn = [SSLVer] -%% SSLVer = sslv3 | tlsv1 | 'tlsv1.1' +-spec versions() -> [{{ssl_app, string()}, {supported, [tls_version()]}, + {available, [tls_version()]}}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -456,7 +457,11 @@ versions() -> AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS, [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. - +%%--------------------------------------------------------------- +-spec renegotiate(#sslsocket{}) -> ok | {error, reason()}. +%% +%% Description: +%%-------------------------------------------------------------------- renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). @@ -660,7 +665,7 @@ validate_option(secure_renegotiate, Value) when Value == true; Value == false -> Value; validate_option(renegotiate_at, Value) when is_integer(Value) -> - min(Value, ?DEFAULT_RENEGOTIATE_AT); + erlang:min(Value, ?DEFAULT_RENEGOTIATE_AT); validate_option(debug, Value) when is_list(Value); Value == true -> Value; @@ -890,10 +895,6 @@ version() -> end, {ok, {SSLVsn, CompVsn, LibVsn}}. -min(N,M) when N < M -> - N; -min(_, M) -> - M. %% Only used to remove exit messages from old ssl %% First is a nonsense clause to provide some diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index db9a883654..eb1228afa4 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -32,6 +32,17 @@ -export([alert_txt/1, reason_code/2]). +%%==================================================================== +%% Internal application API +%%==================================================================== +%%-------------------------------------------------------------------- +-spec reason_code(#alert{}, client | server) -> closed | esslconnect | + esslaccept | string(). +%% +%% Description: Returns the error reason that will be returned to the +%% user. +%%-------------------------------------------------------------------- + reason_code(#alert{description = ?CLOSE_NOTIFY}, _) -> closed; reason_code(#alert{description = ?HANDSHAKE_FAILURE}, client) -> @@ -41,10 +52,19 @@ reason_code(#alert{description = ?HANDSHAKE_FAILURE}, server) -> reason_code(#alert{description = Description}, _) -> description_txt(Description). +%%-------------------------------------------------------------------- +-spec alert_txt(#alert{}) -> string(). +%% +%% Description: Returns the error string for given alert. +%%-------------------------------------------------------------------- + alert_txt(#alert{level = Level, description = Description, where = {Mod,Line}}) -> Mod ++ ":" ++ integer_to_list(Line) ++ ":" ++ level_txt(Level) ++" "++ description_txt(Description). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- level_txt(?WARNING) -> "Warning:"; level_txt(?FATAL) -> @@ -96,8 +116,3 @@ description_txt(?USER_CANCELED) -> "user canceled"; description_txt(?NO_RENEGOTIATION) -> "no renegotiation". - - - - - diff --git a/lib/ssl/src/ssl_app.erl b/lib/ssl/src/ssl_app.erl index 6ca1c42631..d9a354086d 100644 --- a/lib/ssl/src/ssl_app.erl +++ b/lib/ssl/src/ssl_app.erl @@ -27,14 +27,16 @@ -export([start/2, stop/1]). -%% start/2(Type, StartArgs) -> {ok, Pid} | {ok, Pid, State} | -%% {error, Reason} -%% +%%-------------------------------------------------------------------- +-spec start(normal | {takeover, node()} | {failover, node()}, list()) -> + {ok, pid()} | {ok, pid(), term()} | {error, term()}. +%%-------------------------------------------------------------------- start(_Type, _StartArgs) -> ssl_sup:start_link(). -%% stop(State) -> void() -%% +%-------------------------------------------------------------------- +-spec stop(term())-> ok. +%%-------------------------------------------------------------------- stop(_State) -> ok. diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 9aa31ae8a4..8a79f75725 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -46,6 +46,14 @@ %% Internal application API %%==================================================================== +%%-------------------------------------------------------------------- +-spec trusted_cert_and_path([der_cert()], certdb_ref(), boolean()) -> + {der_cert(), [der_cert()], list()}. +%% +%% Description: Extracts the root cert (if not presents tries to +%% look it up, if not found {bad_cert, unknown_ca} will be added verification +%% errors. Returns {RootCert, Path, VerifyErrors} +%%-------------------------------------------------------------------- trusted_cert_and_path(CertChain, CertDbRef, Verify) -> [Cert | RestPath] = lists:reverse(CertChain), {ok, OtpCert} = public_key:pkix_decode_cert(Cert, otp), @@ -84,19 +92,31 @@ trusted_cert_and_path(CertChain, CertDbRef, Verify) -> end end. - +%%-------------------------------------------------------------------- +-spec certificate_chain(undefined | binary(), certdb_ref()) -> + {error, no_cert} | [der_cert()]. +%% +%% Description: Return the certificate chain to send to peer. +%%-------------------------------------------------------------------- certificate_chain(undefined, _CertsDbRef) -> {error, no_cert}; certificate_chain(OwnCert, CertsDbRef) -> {ok, ErlCert} = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertsDbRef, [OwnCert]). - +%%-------------------------------------------------------------------- +-spec file_to_certificats(string()) -> [der_cert()]. +%% +%% Description: Return list of DER encoded certificates. +%%-------------------------------------------------------------------- file_to_certificats(File) -> {ok, List} = ssl_manager:cache_pem_file(File), [Bin || {cert, Bin, not_encrypted} <- List]. - - -%% Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- +-spec validate_extensions([#'Extension'{}], term(), [#'Extension'{}], + boolean(), list(), client | server) -> {[#'Extension'{}], term(), list()}. +%% +%% Description: Validates ssl/tls specific extensions +%%-------------------------------------------------------------------- validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) -> {UnknownExtensions, ValidationState, AccErr}; @@ -119,21 +139,42 @@ validate_extensions([Extension | Rest], ValidationState, UnknownExtensions, validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions], Verify, AccErr, Role). +%%-------------------------------------------------------------------- +-spec is_valid_key_usage(list(), term()) -> boolean(). +%% +%% Description: Checks if Use is a valid key usage. +%%-------------------------------------------------------------------- is_valid_key_usage(KeyUse, Use) -> lists:member(Use, KeyUse). - select_extension(_, []) -> +%%-------------------------------------------------------------------- +-spec select_extension(term(), list()) -> undefined | #'Extension'{}. +%% +%% Description: Selects the extension identified by Id if present in +%% a list of extensions. +%%-------------------------------------------------------------------- +select_extension(_, []) -> undefined; select_extension(Id, [#'Extension'{extnID = Id} = Extension | _]) -> Extension; select_extension(Id, [_ | Extensions]) -> select_extension(Id, Extensions). +%%-------------------------------------------------------------------- +-spec extensions_list(asn1_NOVALUE | list()) -> list(). +%% +%% Description: Handles that +%%-------------------------------------------------------------------- extensions_list(asn1_NOVALUE) -> []; extensions_list(Extensions) -> Extensions. +%%-------------------------------------------------------------------- +-spec signature_type(term()) -> rsa | dsa . +%% +%% Description: +%%-------------------------------------------------------------------- signature_type(RSA) when RSA == ?sha1WithRSAEncryption; RSA == ?md5WithRSAEncryption -> rsa; diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index b8c3c6f6b7..e953821057 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -22,7 +22,7 @@ %%---------------------------------------------------------------------- -module(ssl_certificate_db). - +-include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). -export([create/0, remove/1, add_trusted_certs/3, @@ -34,8 +34,7 @@ %%==================================================================== %%-------------------------------------------------------------------- -%% Function: create() -> Db -%% Db = term() - Reference to the crated database +-spec create() -> certdb_ref(). %% %% Description: Creates a new certificate db. %% Note: lookup_trusted_cert/3 may be called from any process but only @@ -47,8 +46,7 @@ create() -> ets:new(ssl_pid_to_file, [bag, private])]. %%-------------------------------------------------------------------- -%% Function: delete(Db) -> _ -%% Db = Database refererence as returned by create/0 +-spec remove(certdb_ref()) -> term(). %% %% Description: Removes database db %%-------------------------------------------------------------------- @@ -56,11 +54,10 @@ remove(Dbs) -> lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). %%-------------------------------------------------------------------- -%% Function: lookup_trusted_cert(Ref, SerialNumber, Issuer) -> {BinCert,DecodedCert} -%% Ref = ref() +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> {der_cert(), #'OTPCertificate'{}}. + %% SerialNumber = integer() %% Issuer = {rdnSequence, IssuerAttrs} -%% BinCert = binary() %% %% Description: Retrives the trusted certificate identified by %% <SerialNumber, Issuer>. Ref is used as it is specified @@ -78,11 +75,7 @@ lookup_cached_certs(File) -> ets:lookup(certificate_db_name(), {file, File}). %%-------------------------------------------------------------------- -%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref} -%% Pid = pid() -%% File = string() -%% Db = Database refererence as returned by create/0 -%% Ref = ref() +-spec add_trusted_certs(pid(), string(), certdb_ref()) -> {ok, certdb_ref()}. %% %% Description: Adds the trusted certificates from file <File> to the %% runtime database. Returns Ref that should be handed to lookup_trusted_cert @@ -103,7 +96,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> {ok, Ref}. %%-------------------------------------------------------------------- -%% Function: cache_pem_file(Pid, File, Db) -> FileContent +-spec cache_pem_file(pid(), string(), certdb_ref()) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- @@ -114,7 +107,8 @@ cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) -> Res. %%-------------------------------------------------------------------- -%% Function: remove_trusted_certs(Pid, Db) -> _ +-spec remove_trusted_certs(pid(), certdb_ref()) -> term(). + %% %% Description: Removes trusted certs originating from %% the file associated to Pid from the runtime database. @@ -144,11 +138,9 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> end. %%-------------------------------------------------------------------- -%% Function: issuer_candidate() -> {Key, Candidate} | no_more_candidates +-spec issuer_candidate(no_candidate | cert_key()) -> + {cert_key(), der_cert()} | no_more_candidates. %% -%% Candidate -%% -%% %% Description: If a certificat does not define its issuer through %% the extension 'ce-authorityKeyIdentifier' we can %% try to find the issuer in the database over known diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 2a71df8ee1..a6e80047c2 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -40,11 +40,8 @@ -compile(inline). %%-------------------------------------------------------------------- -%% Function: security_parameters(CipherSuite, SecParams) -> -%% #security_parameters{} -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% SecParams - #security_parameters{} +-spec security_parameters(erl_cipher_suite(), #security_parameters{}) -> + #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> @@ -63,15 +60,11 @@ security_parameters(CipherSuite, SecParams) -> hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- -%% Function: cipher(Method, CipherState, Mac, Data) -> -%% {Encrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> + {binary(), #cipher_state{}}. %% -%% Description: Encrypts the data and the mac using method, updating -%% the cipher state +%% Description: Encrypts the data and the MAC using chipher described +%% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- cipher(?NULL, CipherState, <<>>, Fragment) -> GenStreamCipherList = [Fragment, <<>>], @@ -125,15 +118,11 @@ block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- -%% Function: decipher(Method, CipherState, Mac, Data, Version) -> -%% {Decrypted, UpdateCipherState} -%% -%% Method - integer() (as defined in ssl_cipher.hrl) -%% CipherState, UpdatedCipherState - #cipher_state{} -%% Data, Encrypted - binary() +-spec decipher(cipher_enum(), integer(), #cipher_state{}, binary(), tls_version()) -> + {binary(), #cipher_state{}}. %% -%% Description: Decrypts the data and the mac using method, updating -%% the cipher state +%% Description: Decrypts the data and the MAC using cipher described +%% by cipher_enum() and updating the cipher state. %%------------------------------------------------------------------- decipher(?NULL, _HashSz, CipherState, Fragment, _) -> {Fragment, <<>>, CipherState}; @@ -192,10 +181,7 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, end. %%-------------------------------------------------------------------- -%% Function: suites(Version) -> [Suite] -%% -%% Version = version() -%% Suite = binary() from ssl_cipher.hrl +-spec suites(tls_version()) -> [cipher_suite()]. %% %% Description: Returns a list of supported cipher suites. %%-------------------------------------------------------------------- @@ -205,19 +191,9 @@ suites({3, N}) when N == 1; N == 2 -> ssl_tls1:suites(). %%-------------------------------------------------------------------- -%% Function: suite_definition(CipherSuite) -> -%% {KeyExchange, Cipher, Hash} -%% -%% -%% CipherSuite - as defined in ssl_cipher.hrl -%% KeyExchange - rsa | dh_anon | dhe_dss | dhe_rsa | kerb5 -%% -%% Cipher - null | rc4_128 | idea_cbc | des_cbc | '3des_ede_cbc' -%% des40_cbc | aes_128_cbc | aes_256_cbc -%% Hash - null | md5 | sha +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). %% -%% Description: Returns a security parameters tuple where the -%% cipher values has been updated according to <CipherSuite> +%% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. %% They should be supported or removed in the future. %%------------------------------------------------------------------- @@ -261,6 +237,12 @@ suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> {dhe_rsa, aes_256_cbc, sha}. +%%-------------------------------------------------------------------- +-spec suite(erl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- + %% TLS v1.1 suites %%suite({rsa, null, md5}) -> %% ?TLS_RSA_WITH_NULL_MD5; @@ -309,7 +291,11 @@ suite({dhe_rsa, aes_256_cbc, sha}) -> %% suite({dh_anon, aes_256_cbc, sha}) -> %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA. - +%%-------------------------------------------------------------------- +-spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). +%% +%% Description: Return TLS cipher suite definition. +%%-------------------------------------------------------------------- %% translate constants <-> openssl-strings openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; @@ -339,7 +325,11 @@ 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. - +%%-------------------------------------------------------------------- +-spec openssl_suite_name(cipher_suite()) -> openssl_cipher_suite(). +%% +%% Description: Return openssl cipher suite name. +%%------------------------------------------------------------------- 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) -> @@ -372,6 +362,11 @@ openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> openssl_suite_name(Cipher) -> suite_definition(Cipher). +%%-------------------------------------------------------------------- +-spec filter(undefined | binary(), [cipher_suite()]) -> [cipher_suite()]. +%% +%% Description: . +%%------------------------------------------------------------------- filter(undefined, Ciphers) -> Ciphers; filter(DerCert, Ciphers) -> diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 80fe527f45..19de709d9c 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -26,6 +26,14 @@ -ifndef(ssl_cipher). -define(ssl_cipher, true). +-type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' + | aes_128_cbc | aes_256_cbc. +-type hash() :: sha | md5. +-type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type cipher_suite() :: binary(). +-type cipher_enum() :: integer(). +-type openssl_cipher_suite() :: string(). + %%% SSL cipher protocol %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -define(CHANGE_CIPHER_SPEC_PROTO, 1). % _PROTO to not clash with % SSL record protocol diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 07fa101ed4..5b4b129e30 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -98,12 +98,17 @@ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME, base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}). +-type state_name() :: hello | abbreviated | certify | cipher | connection. +-type gen_fsm_state_return() :: {next_state, state_name(), #state{}} | + {next_state, state_name(), #state{}, timeout()} | + {stop, term(), #state{}}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: send(Pid, Data) -> ok | {error, Reason} +-spec send(pid(), iolist()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- @@ -112,15 +117,16 @@ send(Pid, Data) -> erlang:iolist_to_binary(Data)}, infinity). %%-------------------------------------------------------------------- -%% Function: recv(Socket, Length Timeout) -> {ok, Data} | {error, reason} +-spec recv(pid(), integer(), timeout()) -> + {ok, binary() | list()} | {error, reason()}. %% %% Description: Receives data when active = false %%-------------------------------------------------------------------- recv(Pid, Length, Timeout) -> sync_send_all_state_event(Pid, {recv, Length}, Timeout). %%-------------------------------------------------------------------- -%% Function: : connect(Host, Port, Socket, Options, -%% User, CbInfo, Timeout) -> {ok, Socket} +-spec connect(host(), port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Connect to a ssl server. %%-------------------------------------------------------------------- @@ -132,8 +138,8 @@ connect(Host, Port, Socket, Options, User, CbInfo, Timeout) -> {error, ssl_not_started} end. %%-------------------------------------------------------------------- -%% Function: accept(Port, Socket, Opts, User, -%% CbInfo, Timeout) -> {ok, Socket} | {error, Reason} +-spec ssl_accept(port_num(), port(), list(), pid(), tuple(), timeout()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Performs accept on a ssl listen socket. e.i. performs %% ssl handshake. @@ -147,7 +153,7 @@ ssl_accept(Port, Socket, Opts, User, CbInfo, Timeout) -> end. %%-------------------------------------------------------------------- -%% Function: handshake(SslSocket, Timeout) -> ok | {error, Reason} +-spec handshake(#sslsocket{}, timeout()) -> ok | {error, reason()}. %% %% Description: Starts ssl handshake. %%-------------------------------------------------------------------- @@ -159,7 +165,8 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> Error end. %-------------------------------------------------------------------- -%% Function: socket_control(Pid) -> {ok, SslSocket} | {error, Reason} +-spec socket_control(port(), pid(), atom()) -> + {ok, #sslsocket{}} | {error, reason()}. %% %% Description: Set the ssl process to own the accept socket %%-------------------------------------------------------------------- @@ -172,7 +179,7 @@ socket_control(Socket, Pid, CbModule) -> end. %%-------------------------------------------------------------------- -%% Function: close() -> ok +-spec close(pid()) -> ok | {error, reason()}. %% %% Description: Close a ssl connection %%-------------------------------------------------------------------- @@ -185,7 +192,7 @@ close(ConnectionPid) -> end. %%-------------------------------------------------------------------- -%% Function: shutdown(Socket, How) -> ok | {error, Reason} +-spec shutdown(pid(), atom()) -> ok | {error, reason()}. %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- @@ -193,7 +200,7 @@ shutdown(ConnectionPid, How) -> sync_send_all_state_event(ConnectionPid, {shutdown, How}). %%-------------------------------------------------------------------- -%% Function: new_user(ConnectionPid, User) -> ok | {error, Reason} +-spec new_user(pid(), pid()) -> ok | {error, reason()}. %% %% Description: Changes process that receives the messages when active = true %% or once. @@ -201,28 +208,28 @@ shutdown(ConnectionPid, How) -> new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). %%-------------------------------------------------------------------- -%% Function: sockname(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec sockname(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- sockname(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, sockname). %%-------------------------------------------------------------------- -%% Function: peername(ConnectionPid) -> {ok, {Address, Port}} | {error, Reason} +-spec peername(pid()) -> {ok, {tuple(), port_num()}} | {error, reason()}. %% %% Description: Same as inet:peername/1 %%-------------------------------------------------------------------- peername(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peername). %%-------------------------------------------------------------------- -%% Function: get_opts(ConnectionPid, OptTags) -> {ok, Options} | {error, Reason} +-spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% %% Description: Same as inet:getopts/2 %%-------------------------------------------------------------------- get_opts(ConnectionPid, OptTags) -> sync_send_all_state_event(ConnectionPid, {get_opts, OptTags}). %%-------------------------------------------------------------------- -%% Function: setopts(Socket, Options) -> ok | {error, Reason} +-spec set_opts(pid(), list()) -> ok | {error, reason()}. %% %% Description: Same as inet:setopts/2 %%-------------------------------------------------------------------- @@ -230,8 +237,7 @@ set_opts(ConnectionPid, Options) -> sync_send_all_state_event(ConnectionPid, {set_opts, Options}). %%-------------------------------------------------------------------- -%% Function: info(ConnectionPid) -> {ok, {Protocol, CipherSuite}} | -%% {error, Reason} +-spec info(pid()) -> {ok, {atom(), tuple()}} | {error, reason()}. %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- @@ -239,7 +245,7 @@ info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, info). %%-------------------------------------------------------------------- -%% Function: session_info(ConnectionPid) -> {ok, PropList} | {error, Reason} +-spec session_info(pid()) -> {ok, list()} | {error, reason()}. %% %% Description: Returns info about the ssl session %%-------------------------------------------------------------------- @@ -247,7 +253,7 @@ session_info(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, session_info). %%-------------------------------------------------------------------- -%% Function: peercert(ConnectionPid) -> {ok, Cert} | {error, Reason} +-spec peer_certificate(pid()) -> {ok, binary()} | {error, reason()}. %% %% Description: Returns the peer cert %%-------------------------------------------------------------------- @@ -255,7 +261,7 @@ peer_certificate(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, peer_certificate). %%-------------------------------------------------------------------- -%% Function: renegotiation(ConnectionPid) -> ok | {error, Reason} +-spec renegotiation(pid()) -> ok | {error, reason()}. %% %% Description: Starts a renegotiation of the ssl session. %%-------------------------------------------------------------------- @@ -267,7 +273,8 @@ renegotiation(ConnectionPid) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(atom(), host(), port_num(), port(), list(), pid(), tuple()) -> + {ok, pid()} | ignore | {error, reason()}. %% %% Description: Creates a gen_fsm process which calls Module:init/1 to %% initialize. To ensure a synchronized start-up procedure, this function @@ -281,10 +288,9 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> %% gen_fsm callbacks %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, StateName, State} | -%% {ok, StateName, State, Timeout} | -%% ignore | -%% {stop, StopReason} +-spec init(list()) -> {ok, state_name(), #state{}} + | {ok, state_name(), #state{}, timeout()} | + ignore | {stop, term()}. %% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or %% gen_fsm:start_link/3,4, this function is called by the new process to %% initialize. @@ -309,11 +315,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, end. %%-------------------------------------------------------------------- -%% Function: -%% state_name(Event, State) -> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +%% -spec state_name(event(), #state{}) -> gen_fsm_state_return() %% %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent @@ -322,6 +324,9 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %% the event. It is also called if a timeout occurs. %% %%-------------------------------------------------------------------- +-spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, transport_cb = Transport, socket = Socket, @@ -359,49 +364,30 @@ hello(#hello_request{}, #state{role = client} = State0) -> hello(#server_hello{cipher_suite = CipherSuite, compression_method = Compression} = Hello, - #state{session = Session0 = #session{session_id = OldId}, + #state{session = #session{session_id = OldId}, connection_states = ConnectionStates0, role = client, negotiated_version = ReqVersion, - host = Host, port = Port, renegotiation = {Renegotiation, _}, - ssl_options = SslOptions, - session_cache = Cache, - session_cache_cb = CacheCb} = State0) -> + ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of - {Version, NewId, ConnectionStates1} -> + {Version, NewId, ConnectionStates} -> {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), - State1 = State0#state{key_algorithm = KeyAlgorithm, - negotiated_version = Version, - connection_states = ConnectionStates1, - premaster_secret = PremasterSecret}, + State = State0#state{key_algorithm = KeyAlgorithm, + negotiated_version = Version, + connection_states = ConnectionStates, + premaster_secret = PremasterSecret}, case ssl_session:is_new(OldId, NewId) of true -> - Session = Session0#session{session_id = NewId, - cipher_suite = CipherSuite, - compression_method = Compression}, - {Record, State} = next_record(State1#state{session = Session}), - next_state(certify, Record, State); + handle_new_session(NewId, CipherSuite, Compression, State); false -> - Session = CacheCb:lookup(Cache, {{Host, Port}, NewId}), - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, client) of - {_, ConnectionStates2} -> - {Record, State} = - next_record(State1#state{ - connection_states = ConnectionStates2, - session = Session}), - next_state(abbreviated, Record, State); - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State1), - {stop, normal, State1} - end + handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end; #alert{} = Alert -> handle_own_alert(Alert, ReqVersion, hello, State0), @@ -431,7 +417,10 @@ hello(Hello = #client_hello{client_version = ClientVersion}, hello(Msg, State) -> handle_unexpected_message(Msg, hello, State). - +%%-------------------------------------------------------------------- +-spec abbreviated(#hello_request{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- abbreviated(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -477,6 +466,11 @@ abbreviated(#finished{verify_data = Data} = Finished, abbreviated(Msg, State) -> handle_unexpected_message(Msg, abbreviated, State). +%%-------------------------------------------------------------------- +-spec certify(#hello_request{} | #certificate{} | #server_key_exchange{} | + #certificate_request{} | #server_hello_done{} | #client_key_exchange{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- certify(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -646,6 +640,10 @@ certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{ certify(Msg, State) -> handle_unexpected_message(Msg, certify, State). +%%-------------------------------------------------------------------- +-spec cipher(#hello_request{} | #certificate_verify{} | #finished{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- cipher(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(hello, Record, State); @@ -676,31 +674,14 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0, - connection_states = ConnectionStates0} = State) -> + tls_handshake_hashes = Hashes0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), MasterSecret, Hashes0) of verified -> Session = register_session(Role, Host, Port, Session0), - case Role of - client -> - ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), - next_state_connection(cipher, ack_connection(State#state{session = Session, - connection_states = ConnectionStates})); - server -> - ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = - finalize_handshake(State#state{ - connection_states = ConnectionStates1, - session = Session}, cipher), - next_state_connection(cipher, ack_connection(State#state{connection_states = - ConnectionStates, - session = Session, - tls_handshake_hashes = - Hashes})) - end; + cipher_role(Role, Data, Session, State); #alert{} = Alert -> handle_own_alert(Alert, Version, cipher, State), {stop, normal, State} @@ -709,7 +690,10 @@ cipher(#finished{verify_data = Data} = Finished, cipher(Msg, State) -> handle_unexpected_message(Msg, cipher, State). - +%%-------------------------------------------------------------------- +-spec connection(#hello_request{} | #client_hello{} | term(), + #state{}) -> gen_fsm_state_return(). +%%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, ssl_options = SslOpts, @@ -736,30 +720,22 @@ connection(#client_hello{} = Hello, #state{role = server} = State) -> connection(Msg, State) -> handle_unexpected_message(Msg, connection, State). %%-------------------------------------------------------------------- -%% Function: -%% handle_event(Event, StateName, State) -> {next_state, NextStateName, -%% NextState} | -%% {next_state, NextStateName, -%% NextState, Timeout} | -%% {stop, Reason, NewState} +-spec handle_event(term(), state_name(), #state{}) -> gen_fsm_state_return(). +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle -%% the event. +%% the event. Not currently used! %%-------------------------------------------------------------------- handle_event(_Event, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_sync_event(Event, From, StateName, -%% State) -> {next_state, NextStateName, NextState} | -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {reply, Reply, NextStateName, NextState}| -%% {reply, Reply, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} | -%% {stop, Reason, Reply, NewState} +-spec handle_sync_event(term(), from(), state_name(), #state{}) -> + gen_fsm_state_return() | + {reply, reply(), state_name(), #state{}} | + {reply, reply(), state_name(), #state{}, timeout()} | + {stop, reason(), reply(), #state{}}. +%% %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle %% the event. @@ -936,11 +912,11 @@ handle_sync_event(peer_certificate, _, StateName, {reply, {ok, Cert}, StateName, State}. %%-------------------------------------------------------------------- -%% Function: -%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}| -%% {next_state, NextStateName, NextState, -%% Timeout} | -%% {stop, Reason, NewState} +-spec handle_info(msg(),state_name(), #state{}) -> + {next_state, state_name(), #state{}}| + {next_state, state_name(), #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: This function is called by a gen_fsm when it receives any %% other message than a synchronous or asynchronous event %% (or a system message). @@ -1000,7 +976,8 @@ handle_info(Msg, StateName, State) -> {next_state, StateName, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, StateName, State) -> void() +-spec terminate(reason(), state_name(), #state{}) -> term(). +%% %% Description:This function is called by a gen_fsm when it is about %% to terminate. It should be the opposite of Module:init/1 and do any %% necessary cleaning up. When it returns, the gen_fsm terminates with @@ -1027,7 +1004,8 @@ terminate(_Reason, _StateName, #state{transport_cb = Transport, Transport:close(Socket). %%-------------------------------------------------------------------- -%% Function: +-spec code_change(term(), state_name(), #state{}, list()) -> {ok, state_name(), #state{}}. +%% %% code_change(OldVsn, StateName, State, Extra) -> {ok, StateName, NewState} %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- @@ -1039,24 +1017,17 @@ code_change(_OldVsn, StateName, State, _Extra) -> %%-------------------------------------------------------------------- start_fsm(Role, Host, Port, Socket, Opts, User, {CbModule, _,_, _} = CbInfo, Timeout) -> - case ssl_connection_sup:start_child([Role, Host, Port, Socket, - Opts, User, CbInfo]) of - {ok, Pid} -> - case socket_control(Socket, Pid, CbModule) of - {ok, SslSocket} -> - case handshake(SslSocket, Timeout) of - ok -> - {ok, SslSocket}; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} - end; - {error, Reason} -> - {error, Reason} + try + {ok, Pid} = ssl_connection_sup:start_child([Role, Host, Port, Socket, + Opts, User, CbInfo]), + {ok, SslSocket} = socket_control(Socket, Pid, CbModule), + ok = handshake(SslSocket, Timeout), + {ok, SslSocket} + catch + error:{badmatch, {error, _} = Error} -> + Error end. - + ssl_init(SslOpts, Role) -> {ok, CertDbRef, CacheRef, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = @@ -1267,6 +1238,33 @@ do_server_hello(#server_hello{cipher_suite = CipherSuite, {stop, normal, State0} end. +handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> + Session = Session0#session{session_id = NewId, + cipher_suite = CipherSuite, + compression_method = Compression}, + {Record, State} = next_record(State0#state{session = Session}), + next_state(certify, Record, State). + +handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, + negotiated_version = Version, + host = Host, port = Port, + session_cache = Cache, + session_cache_cb = CacheCb} = State0) -> + Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, client) of + {_, ConnectionStates1} -> + {Record, State} = + next_record(State0#state{ + connection_states = ConnectionStates1, + session = Session}), + next_state(abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + + client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of @@ -1354,9 +1352,7 @@ key_exchange(#state{role = server, key_algorithm = Algo, transport_cb = Transport } = State) when Algo == dhe_dss; - Algo == dhe_dss_export; - Algo == dhe_rsa; - Algo == dhe_rsa_export -> + Algo == dhe_rsa -> Keys = public_key:gen_key(Params), ConnectionState = @@ -1402,9 +1398,7 @@ key_exchange(#state{role = client, socket = Socket, transport_cb = Transport, tls_handshake_hashes = Hashes0} = State) when Algorithm == dhe_dss; - Algorithm == dhe_dss_export; - Algorithm == dhe_rsa; - Algorithm == dhe_rsa_export -> + Algorithm == dhe_rsa -> Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), {BinMsg, ConnectionStates1, Hashes1} = encode_handshake(Msg, Version, ConnectionStates0, Hashes0), @@ -1545,6 +1539,21 @@ verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> public_key:verify_signature(Hash, none, Signed, PublicKey, PublicKeyParams). +cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates = ssl_record:set_server_verify_data(current_both, Data, ConnectionStates0), + next_state_connection(cipher, ack_connection(State#state{session = Session, + connection_states = ConnectionStates})); + +cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> + ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), + {ConnectionStates, Hashes} = + finalize_handshake(State#state{connection_states = ConnectionStates1, + session = Session}, cipher), + next_state_connection(cipher, ack_connection(State#state{connection_states = + ConnectionStates, + session = Session, + tls_handshake_hashes = + Hashes})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ?DBG_TERM(Alert), ssl_record:encode_alert_record(Alert, Version, ConnectionStates). diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index f7e3e392ec..3811906d77 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -42,16 +42,15 @@ encode_handshake/3, init_hashes/0, update_hashes/2, decrypt_premaster_secret/2]). +-type tls_handshake() :: #client_hello{} | #server_hello{} | #server_hello_done{} | +#certificate{} | #client_key_exchange{} | #finished{} | #certificate_verify{}. + %%==================================================================== %% Internal application API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: client_hello(Host, Port, ConnectionStates, SslOpts, Cert, Renegotiation) -> -%% #client_hello{} -%% Host -%% Port -%% ConnectionStates = #connection_states{} -%% SslOpts = #ssl_options{} +-spec client_hello(host(), port_num(), #connection_states{}, + #ssl_options{}, binary(), boolean()) -> #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- @@ -79,13 +78,8 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, }. %%-------------------------------------------------------------------- -%% Function: server_hello(SessionId, Version, -%% ConnectionStates, Renegotiation) -> #server_hello{} -%% SessionId -%% Version -%% ConnectionStates -%% Renegotiation -%% +-spec server_hello(session_id(), tls_version(), #connection_states{}, + boolean()) -> #server_hello{}. %% %% Description: Creates a server hello message. %%-------------------------------------------------------------------- @@ -103,7 +97,7 @@ server_hello(SessionId, Version, ConnectionStates, Renegotiation) -> }. %%-------------------------------------------------------------------- -%% Function: hello_request() -> #hello_request{} +-spec hello_request() -> #hello_request{}. %% %% Description: Creates a hello request message sent by server to %% trigger renegotiation. @@ -112,15 +106,12 @@ hello_request() -> #hello_request{}. %%-------------------------------------------------------------------- -%% Function: hello(Hello, Info, Renegotiation) -> -%% {Version, Id, NewConnectionStates} | -%% #alert{} -%% -%% Hello = #client_hello{} | #server_hello{} -%% Info = ConnectionStates | {Port, #ssl_options{}, Session, -%% Cahce, CahceCb, ConnectionStates} -%% ConnectionStates = #connection_states{} -%% Renegotiation = boolean() +-spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, + #connection_states{} | {port_num(), #session{}, cache_ref(), + atom(), #connection_states{}, binary()}, + boolean()) -> {tls_version(), session_id(), #connection_states{}}| + {tls_version(), {resumed | new, session_id()}, + #connection_states{}} | #alert{}. %% %% Description: Handles a recieved hello message %%-------------------------------------------------------------------- @@ -183,12 +174,9 @@ hello(#client_hello{client_version = ClientVersion, random = Random, end. %%-------------------------------------------------------------------- -%% Function: certify(Certs, CertDbRef, MaxPathLen) -> -%% {PeerCert, PublicKeyInfo} | #alert{} -%% -%% Certs = #certificate{} -%% CertDbRef = reference() -%% MaxPathLen = integer() | nolimit +-spec certify(#certificate{}, term(), integer() | nolimit, + verify_peer | verify_none, fun(), fun(), + client | server) -> {der_cert(), public_key_info()} | #alert{}. %% %% Description: Handles a certificate handshake message %%-------------------------------------------------------------------- @@ -244,10 +232,7 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef, end. %%-------------------------------------------------------------------- -%% Function: certificate(OwnCert, CertDbRef, Role) -> #certificate{} -%% -%% OwnCert = binary() -%% CertDbRef = term() as returned by ssl_certificate_db:create() +-spec certificate(der_cert(), term(), client | server) -> #certificate{}. %% %% Description: Creates a certificate message. %%-------------------------------------------------------------------- @@ -273,10 +258,10 @@ certificate(OwnCert, CertDbRef, server) -> end. %%-------------------------------------------------------------------- -%% Function: client_certificate_verify(Cert, ConnectionStates) -> -%% #certificate_verify{} | ignore -%% Cert = #'OTPcertificate'{} -%% ConnectionStates = #connection_states{} +-spec client_certificate_verify(undefined | der_cert(), binary(), + tls_version(), key_algo(), private_key(), + {binary(), binary()}) -> + #certificate_verify{} | ignore. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- @@ -298,10 +283,9 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm, end. %%-------------------------------------------------------------------- -%% Function: certificate_verify(Signature, PublicKeyInfo) -> valid | #alert{} -%% -%% Signature = binary() -%% PublicKeyInfo = {Algorithm, PublicKey, PublicKeyParams} +-spec certificate_verify(binary(), public_key_info(), tls_version(), + binary(), key_algo(), + {binary(), binary()}) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- @@ -325,8 +309,8 @@ certificate_verify(Signature, {_, PublicKey, PublicKeyParams}, Version, public_key:verify_signature(Hashes, sha, Signature, PublicKey, PublicKeyParams). %%-------------------------------------------------------------------- -%% Function: certificate_request(ConnectionStates, CertDbRef) -> -%% #certificate_request{} +-spec certificate_request(#connection_states{}, certdb_ref()) -> + #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- @@ -342,11 +326,12 @@ certificate_request(ConnectionStates, CertDbRef) -> }. %%-------------------------------------------------------------------- -%% Function: key_exchange(Role, Secret, Params) -> -%% #client_key_exchange{} | #server_key_exchange{} -%% -%% Secret - -%% Params - +-spec key_exchange(client | server, + {premaster_secret, binary(), public_key_info()} | + {dh, binary()} | + {dh, binary(), #'DHParameter'{}, key_algo(), + binary(), binary(), private_key()}) -> + #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- @@ -382,15 +367,9 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, signed_params = Signed}. %%-------------------------------------------------------------------- -%% Function: master_secret(Version, Session/PremasterSecret, -%% ConnectionStates, Role) -> -%% {MasterSecret, NewConnectionStates} | #alert{} -%% Version = #protocol_version{} -%% Session = #session{} (session contains master secret) -%% PremasterSecret = binary() -%% ConnectionStates = #connection_states{} -%% Role = client | server -%% +-spec master_secret(tls_version(), #session{} | binary(), #connection_states{}, + client | server) -> {binary(), #connection_states{}} | #alert{}. +%% %% Description: Sets or calculates the master secret and calculate keys, %% updating the pending connection states. The Mastersecret and the update %% connection states are returned or an alert if the calculation fails. @@ -427,9 +406,8 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- -%% Function: finished(Version, Role, MacSecret, Hashes) -> #finished{} -%% -%% ConnectionStates = #connection_states{} +-spec finished(tls_version(), client | server, binary(), {binary(), binary()}) -> + #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- @@ -438,15 +416,8 @@ finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes calc_finished(Version, Role, MasterSecret, Hashes)}. %%-------------------------------------------------------------------- -%% Function: verify_connection(Finished, Role, -%% MasterSecret, Hashes) -> verified | #alert{} -%% -%% Finished = #finished{} -%% Role = client | server - the role of the process that sent the finished -%% message. -%% MasterSecret = binary() -%% Hashes = binary() - {md5_hash, sha_hash} -%% +-spec verify_connection(tls_version(), #finished{}, client | server, binary(), + {binary(), binary()}) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. @@ -462,17 +433,18 @@ verify_connection(Version, #finished{verify_data = Data}, _E -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE) end. - +%%-------------------------------------------------------------------- +-spec server_hello_done() -> #server_hello_done{}. +%% +%% Description: Creates a server hello done message. +%%-------------------------------------------------------------------- server_hello_done() -> #server_hello_done{}. %%-------------------------------------------------------------------- -%% Function: encode_handshake(HandshakeRec) -> BinHandshake -%% HandshakeRec = #client_hello | #server_hello{} | server_hello_done | -%% #certificate{} | #client_key_exchange{} | #finished{} | -%% #client_certify_request{} +-spec encode_handshake(tls_handshake(), tls_version(), key_algo()) -> binary(). %% -%% encode a handshake packet to binary +%% Description: Encode a handshake packet to binary %%-------------------------------------------------------------------- encode_handshake(Package, Version, KeyAlg) -> SigAlg = sig_alg(KeyAlg), @@ -481,12 +453,11 @@ encode_handshake(Package, Version, KeyAlg) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- -%% Function: get_tls_handshake(Data, Buffer) -> Result -%% Result = {[#handshake{}], [Raw], NewBuffer} -%% Data = Buffer = NewBuffer = Raw = binary() +-spec get_tls_handshake(binary(), binary(), key_algo(), tls_version()) -> + {[tls_handshake()], [binary()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects -%% and returns it as a list of #handshake, also returns leftover +%% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- get_tls_handshake(Data, <<>>, KeyAlg, Version) -> @@ -495,6 +466,9 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) -> get_tls_handshake_aux(list_to_binary([Buffer, Data]), KeyAlg, Version, []). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, KeyAlg, Version, Acc) -> @@ -504,9 +478,6 @@ get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) -> {lists:reverse(Acc), Data}. -%%-------------------------------------------------------------------- -%%% Internal functions -%%-------------------------------------------------------------------- verify_bool(verify_peer) -> true; verify_bool(verify_none) -> diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index fdc0c33750..ddace02dea 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -23,6 +23,8 @@ -ifndef(ssl_internal). -define(ssl_internal, true). +-include_lib("public_key/include/public_key.hrl"). + %% basic binary constructors -define(BOOLEAN(X), X:8/unsigned-big-integer). -define(BYTE(X), X:8/unsigned-big-integer). @@ -88,6 +90,28 @@ active = true }). +-type reason() :: term(). +-type reply() :: term(). +-type msg() :: term(). +-type from() :: term(). +-type host() :: string() | tuple(). +-type port_num() :: integer(). +-type session_id() :: binary(). +-type tls_version() :: {integer(), integer()}. +-type tls_atom_version() :: sslv3 | tlsv1. +-type cache_ref() :: term(). +-type certdb_ref() :: term(). +-type key_algo() :: rsa | dhe_rsa | dhe_dss. +-type enum_algo() :: integer(). +-type public_key() :: #'RSAPublicKey'{} | integer(). +-type public_key_params() :: #'Dss-Parms'{} | term(). +-type public_key_info() :: {enum_algo(), public_key(), public_key_params()}. +-type der_cert() :: binary(). +-type private_key() :: #'RSAPrivateKey'{} | #'DSAPrivateKey'{}. +-type issuer() :: tuple(). +-type serialnumber() :: integer(). +-type cert_key() :: {reference(), integer(), issuer()}. + -endif. % -ifdef(ssl_internal). diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 19bdcfa1f5..af30f78dbf 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -24,8 +24,10 @@ -module(ssl_manager). -behaviour(gen_server). +-include("ssl_internal.hrl"). + %% Internal application API --export([start_link/0, start_link/1, +-export([start_link/1, connection_init/2, cache_pem_file/1, lookup_trusted_cert/3, issuer_candidate/1, client_session_id/3, server_session_id/3, register_session/2, register_session/3, invalidate_session/2, @@ -58,21 +60,25 @@ %% API %%==================================================================== %%-------------------------------------------------------------------- -%% Function: start_link() -> {ok,Pid} | ignore | {error,Error} +-spec start_link(list()) -> {ok, pid()} | ignore | {error, term()}. +%% %% Description: Starts the server %%-------------------------------------------------------------------- -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). start_link(Opts) -> gen_server:start_link({local, ?MODULE}, ?MODULE, [Opts], []). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec connection_init(string(), client | server) -> {ok, reference(), cache_ref()}. +%% +%% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init(TrustedcertsFile, Role) -> call({connection_init, TrustedcertsFile, Role}). - +%%-------------------------------------------------------------------- +-spec cache_pem_file(string()) -> {ok, term()}. +%% +%% Description: Cach a pem file and +%%-------------------------------------------------------------------- cache_pem_file(File) -> case ssl_certificate_db:lookup_cached_certs(File) of [{_,Content}] -> @@ -80,48 +86,51 @@ cache_pem_file(File) -> [] -> call({cache_pem, File}) end. - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec lookup_trusted_cert(reference(), serialnumber(), issuer()) -> + {der_cert(), #'OTPCertificate'{}}. +%% +%% Description: Lookup the trusted cert with Key = {reference(), serialnumber(), issuer()}. %%-------------------------------------------------------------------- lookup_trusted_cert(Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(Ref, SerialNumber, Issuer). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec issuer_candidate(cert_key()) -> {cert_key(), der_cert()} | no_more_candidates. +%% +%% Description: Return next issuer candidate. %%-------------------------------------------------------------------- issuer_candidate(PrevCandidateKey) -> ssl_certificate_db:issuer_candidate(PrevCandidateKey). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec client_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the client. %%-------------------------------------------------------------------- client_session_id(Host, Port, SslOpts) -> call({client_session_id, Host, Port, SslOpts}). - + %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec server_session_id(host(), port_num(), #ssl_options{}) -> session_id(). +%% +%% Description: Select a session id for the server. %%-------------------------------------------------------------------- server_session_id(Port, SuggestedSessionId, SslOpts) -> call({server_session_id, Port, SuggestedSessionId, SslOpts}). %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec register_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session available for reuse. %%-------------------------------------------------------------------- register_session(Host, Port, Session) -> cast({register_session, Host, Port, Session}). register_session(Port, Session) -> cast({register_session, Port, Session}). - %%-------------------------------------------------------------------- -%% Function: -%% Description: +-spec invalidate_session(host(), port_num(), #session{}) -> ok. +%% +%% Description: Make the session unavilable for reuse. %%-------------------------------------------------------------------- invalidate_session(Host, Port, Session) -> cast({invalidate_session, Host, Port, Session}). @@ -134,10 +143,9 @@ invalidate_session(Port, Session) -> %%==================================================================== %%-------------------------------------------------------------------- -%% Function: init(Args) -> {ok, State} | -%% {ok, State, Timeout} | -%% ignore | -%% {stop, Reason} +-spec init(list()) -> {ok, #state{}} | {ok, #state{}, timeout()} | + ignore | {stop, term()}. +%% %% Description: Initiates the server %%-------------------------------------------------------------------- init([Opts]) -> @@ -156,12 +164,13 @@ init([Opts]) -> session_validation_timer = Timer}}. %%-------------------------------------------------------------------- -%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} | -%% {reply, Reply, State, Timeout} | -%% {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, Reply, State} | -%% {stop, Reason, State} +-spec handle_call(msg(), from(), #state{}) -> {reply, reply(), #state{}} | + {reply, reply(), #state{}, timeout()} | + {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), reply(), #state{}} | + {stop, reason(), #state{}}. +%% %% Description: Handling call messages %%-------------------------------------------------------------------- handle_call({{connection_init, "", _Role}, Pid}, _From, @@ -207,9 +216,10 @@ handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) -> {reply, {error, Reason}, State} end. %%-------------------------------------------------------------------- -%% Function: handle_cast(Msg, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_cast(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling cast messages %%-------------------------------------------------------------------- handle_cast({register_session, Host, Port, Session}, @@ -243,9 +253,10 @@ handle_cast({invalidate_session, Port, #session{session_id = ID}}, {noreply, State}. %%-------------------------------------------------------------------- -%% Function: handle_info(Info, State) -> {noreply, State} | -%% {noreply, State, Timeout} | -%% {stop, Reason, State} +-spec handle_info(msg(), #state{}) -> {noreply, #state{}} | + {noreply, #state{}, timeout()} | + {stop, reason(), #state{}}. +%% %% Description: Handling all non call/cast messages %%-------------------------------------------------------------------- handle_info(validate_sessions, #state{session_cache_cb = CacheCb, @@ -278,7 +289,8 @@ handle_info(_Info, State) -> {noreply, State}. %%-------------------------------------------------------------------- -%% Function: terminate(Reason, State) -> void() +-spec terminate(reason(), #state{}) -> term(). +%% %% Description: This function is called by a gen_server when it is about to %% terminate. It should be the opposite of Module:init/1 and do any necessary %% cleaning up. When it returns, the gen_server terminates with Reason. @@ -294,7 +306,8 @@ terminate(_Reason, #state{certificate_db = Db, ok. %%-------------------------------------------------------------------- -%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState} +-spec code_change(term(), #state{}, list()) -> {ok, #state{}}. +%% %% Description: Convert process state when code is changed %%-------------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> @@ -339,4 +352,3 @@ session_validation({{{Host, Port}, _}, Session}, LifeTime) -> session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. - diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 6b7cffaa7d..90615c22a1 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -66,10 +66,9 @@ %%==================================================================== %% Internal application API %%==================================================================== + %%-------------------------------------------------------------------- -%% Function: init_connection_states(Role) -> #connection_states{} -%% Role = client | server -%% Random = binary() +-spec init_connection_states(client | server) -> #connection_states{}. %% %% Description: Creates a connection_states record with appropriate %% values for the initial SSL connection setup. @@ -85,9 +84,8 @@ init_connection_states(Role) -> }. %%-------------------------------------------------------------------- -%% Function: current_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec current_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the current conection state. @@ -100,9 +98,8 @@ current_connection_state(#connection_states{current_write = Current}, Current. %%-------------------------------------------------------------------- -%% Function: pending_connection_state(States, Type) -> #connection_state{} -%% States = #connection_states{} -%% Type = read | write +-spec pending_connection_state(#connection_states{}, read | write) -> + #connection_state{}. %% %% Description: Returns the instance of the connection_state record %% that is currently defined as the pending conection state. @@ -115,14 +112,11 @@ pending_connection_state(#connection_states{pending_write = Pending}, Pending. %%-------------------------------------------------------------------- -%% Function: update_security_params(Params, States) -> -%% #connection_states{} -%% Params = #security_parameters{} -%% States = #connection_states{} +-spec update_security_params(#security_parameters{}, #security_parameters{}, + #connection_states{}) -> #connection_states{}. %% %% Description: Creates a new instance of the connection_states record -%% where the pending states gets its security parameters -%% updated to <Params>. +%% where the pending states gets its security parameters updated. %%-------------------------------------------------------------------- update_security_params(ReadParams, WriteParams, States = #connection_states{pending_read = Read, @@ -135,14 +129,10 @@ update_security_params(ReadParams, WriteParams, States = WriteParams} }. %%-------------------------------------------------------------------- -%% Function: set_mac_secret(ClientWriteMacSecret, -%% ServerWriteMacSecret, Role, States) -> -%% #connection_states{} -%% MacSecret = binary() -%% States = #connection_states{} -%% Role = server | client +-spec set_mac_secret(binary(), binary(), client | server, + #connection_states{}) -> #connection_states{}. %% -%% update the mac_secret field in pending connection states +%% Description: update the mac_secret field in pending connection states %%-------------------------------------------------------------------- set_mac_secret(ClientWriteMacSecret, ServerWriteMacSecret, client, States) -> set_mac_secret(ServerWriteMacSecret, ClientWriteMacSecret, States); @@ -159,12 +149,9 @@ set_mac_secret(ReadMacSecret, WriteMacSecret, %%-------------------------------------------------------------------- -%% Function: set_master_secret(MasterSecret, States) -> -%% #connection_states{} -%% MacSecret = -%% States = #connection_states{} +-spec set_master_secret(binary(), #connection_state{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set master_secret in pending connection states %%-------------------------------------------------------------------- set_master_secret(MasterSecret, States = #connection_states{pending_read = Read, @@ -180,12 +167,9 @@ set_master_secret(MasterSecret, States#connection_states{pending_read = Read1, pending_write = Write1}. %%-------------------------------------------------------------------- -%% Function: set_renegotiation_flag(Flag, States) -> -%% #connection_states{} -%% Flag = boolean() -%% States = #connection_states{} +-spec set_renegotiation_flag(boolean(), #connection_states{}) -> #connection_states{}. %% -%% Set master_secret in pending connection states +%% Description: Set secure_renegotiation in pending connection states %%-------------------------------------------------------------------- set_renegotiation_flag(Flag, #connection_states{ current_read = CurrentRead0, @@ -203,13 +187,11 @@ set_renegotiation_flag(Flag, #connection_states{ pending_write = PendingWrite}. %%-------------------------------------------------------------------- -%% Function: set_client_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_client_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in connection states. +%% Description: Set verify data in connection states. %%-------------------------------------------------------------------- set_client_verify_data(current_read, Data, #connection_states{current_read = CurrentRead0, @@ -235,15 +217,12 @@ set_client_verify_data(current_both, Data, CurrentWrite = CurrentWrite0#connection_state{client_verify_data = Data}, ConnectionStates#connection_states{current_read = CurrentRead, current_write = CurrentWrite}. - %%-------------------------------------------------------------------- -%% Function: set_server_verify_data(State, Data, States) -> -%% #connection_states{} -%% State = atom() -%% Data = binary() -%% States = #connection_states{} +-spec set_server_verify_data(current_read | current_write | current_both, + binary(), #connection_states{})-> + #connection_states{}. %% -%% Set verify data in pending connection states. +%% Description: Set verify data in pending connection states. %%-------------------------------------------------------------------- set_server_verify_data(current_write, Data, #connection_states{pending_read = PendingRead0, @@ -273,10 +252,8 @@ set_server_verify_data(current_both, Data, current_write = CurrentWrite}. %%-------------------------------------------------------------------- -%% Function: activate_pending_connection_state(States, Type) -> -%% #connection_states{} -%% States = #connection_states{} -%% Type = read | write +-spec activate_pending_connection_state(#connection_states{}, read | write) -> + #connection_states{}. %% %% Description: Creates a new instance of the connection_states record %% where the pending state of <Type> has been activated. @@ -308,11 +285,9 @@ activate_pending_connection_state(States = }. %%-------------------------------------------------------------------- -%% Function: set_pending_cipher_state(States, ClientState, -%% ServerState, Role) -> -%% #connection_states{} -%% ClientState = ServerState = #cipher_state{} -%% States = #connection_states{} +-spec set_pending_cipher_state(#connection_states{}, #cipher_state{}, + #cipher_state{}, client | server) -> + #connection_states{}. %% %% Description: Set the cipher state in the specified pending connection state. %%-------------------------------------------------------------------- @@ -331,12 +306,10 @@ set_pending_cipher_state(#connection_states{pending_read = Read, pending_write = Write#connection_state{cipher_state = ClientState}}. %%-------------------------------------------------------------------- -%% Function: get_tls_record(Data, Buffer) -> Result -%% Result = {[#tls_compressed{}], NewBuffer} -%% Data = Buffer = NewBuffer = binary() -%% -%% Description: given old buffer and new data from TCP, packs up a records -%% and returns it as a list of #tls_compressed, also returns leftover +-spec get_tls_records(binary(), binary()) -> {[binary()], binary()}. +%% +%% Description: Given old buffer and new data from TCP, packs up a records +%% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- get_tls_records(Data, <<>>) -> @@ -399,8 +372,7 @@ get_tls_records_aux(Data, Acc) -> {lists:reverse(Acc), Data}. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version) -> #protocol_version{} -%% Version = atom() +-spec protocol_version(tls_atom_version()) -> tls_version(). %% %% Description: Creates a protocol version record from a version atom %% or vice versa. @@ -420,8 +392,7 @@ protocol_version({3, 1}) -> protocol_version({3, 0}) -> sslv3. %%-------------------------------------------------------------------- -%% Function: protocol_version(Version1, Version2) -> #protocol_version{} -%% Version1 = Version2 = #protocol_version{} +-spec lowest_protocol_version(tls_version(), tls_version()) -> tls_version(). %% %% Description: Lowes protocol version of two given versions %%-------------------------------------------------------------------- @@ -436,8 +407,7 @@ lowest_protocol_version(Version = {M,_}, lowest_protocol_version(_,Version) -> Version. %%-------------------------------------------------------------------- -%% Function: protocol_version(Versions) -> #protocol_version{} -%% Versions = [#protocol_version{}] +-spec highest_protocol_version([tls_version()]) -> tls_version(). %% %% Description: Highest protocol version present in a list %%-------------------------------------------------------------------- @@ -459,9 +429,8 @@ highest_protocol_version(_, [Version | Rest]) -> highest_protocol_version(Version, Rest). %%-------------------------------------------------------------------- -%% Function: supported_protocol_versions() -> Versions -%% Versions = [#protocol_version{}] -%% +-spec supported_protocol_versions() -> [tls_version()]. +%% %% Description: Protocol versions supported %%-------------------------------------------------------------------- supported_protocol_versions() -> @@ -487,8 +456,7 @@ supported_protocol_versions([_|_] = Vsns) -> Vsns. %%-------------------------------------------------------------------- -%% Function: is_acceptable_version(Version) -> true | false -%% Version = #protocol_version{} +-spec is_acceptable_version(tls_version()) -> boolean(). %% %% Description: ssl version 2 is not acceptable security risks are too big. %%-------------------------------------------------------------------- @@ -499,7 +467,7 @@ is_acceptable_version(_) -> false. %%-------------------------------------------------------------------- -%% Function: compressions() -> binary() +-spec compressions() -> binary(). %% %% Description: return a list of compressions supported (currently none) %%-------------------------------------------------------------------- @@ -507,8 +475,8 @@ compressions() -> [?byte(?NULL)]. %%-------------------------------------------------------------------- -%% Function: decode_cipher_text(CipherText, ConnectionStates0) -> -%% {Plain, ConnectionStates} +-spec decode_cipher_text(#ssl_tls{}, #connection_states{}) -> + {#ssl_tls{}, #connection_states{}}. %% %% Description: Decode cipher text %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index bcb10daf69..e9755cb0e1 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -32,11 +32,10 @@ -define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-type seconds() :: integer(). + %%-------------------------------------------------------------------- -%% Function: is_new(ClientSuggestedId, ServerDecidedId) -> true | false -%% -%% ClientSuggestedId = binary() -%% ServerDecidedId = binary() +-spec is_new(binary(), binary()) -> boolean(). %% %% Description: Checks if the session id decided by the server is a %% new or resumed sesion id. @@ -45,17 +44,11 @@ is_new(<<>>, _) -> true; is_new(SessionId, SessionId) -> false; -is_new(_, _) -> +is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- -%% Function: id(ClientInfo, Cache, CacheCb) -> SessionId -%% -%% ClientInfo = {HostIP, Port, SslOpts} -%% HostIP = ipadress() -%% Port = integer() -%% CacheCb = atom() -%% SessionId = binary() +-spec id({host(), port_num(), #ssl_options{}}, cache_ref(), atom()) -> binary(). %% %% Description: Should be called by the client side to get an id %% for the client hello message. @@ -69,14 +62,8 @@ id(ClientInfo, Cache, CacheCb) -> end. %%-------------------------------------------------------------------- -%% Function: id(Port, SuggestedSessionId, ReuseFun, CacheCb, -%% SecondLifeTime) -> SessionId -%% -%% Port = integer() -%% SuggestedSessionId = SessionId = binary() -%% ReuseFun = fun(SessionId, PeerCert, Compression, CipherSuite) -> -%% true | false -%% CacheCb = atom() +-spec id(port_num(), binary(), #ssl_options{}, cache_ref(), + atom(), seconds()) -> binary(). %% %% Description: Should be called by the server side to get an id %% for the server hello message. @@ -95,10 +82,7 @@ id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) end. %%-------------------------------------------------------------------- -%% Function: valid_session(Session, LifeTime) -> true | false -%% -%% Session = #session{} -%% LifeTime = integer() - seconds +-spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired %%-------------------------------------------------------------------- diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index 1f2d1fc7d3..823bf7acfa 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -22,13 +22,16 @@ -behaviour(ssl_session_cache_api). +-include("ssl_handshake.hrl"). +-include("ssl_internal.hrl"). + -export([init/1, terminate/1, lookup/2, update/3, delete/2, foldl/3, select_session/2]). +-type key() :: {{host(), port_num()}, session_id()} | {port_num(), session_id()}. + %%-------------------------------------------------------------------- -%% Function: init() -> Cache -%% -%% Cache - Reference to the cash (opaque) +-spec init(list()) -> cache_ref(). %% Returns reference to the cache (opaque) %% %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- @@ -36,9 +39,7 @@ init(_) -> ets:new(cache_name(), [set, protected]). %%-------------------------------------------------------------------- -%% Function: terminate(Cache) -> -%% -%% Cache - as returned by create/0 +-spec terminate(cache_ref()) -> any(). %% %% %% Description: Handles cache table at termination of ssl manager. %%-------------------------------------------------------------------- @@ -46,9 +47,7 @@ terminate(Cache) -> ets:delete(Cache). %%-------------------------------------------------------------------- -%% Function: lookup(Cache, Key) -> Session | undefined -%% Cache - as returned by create/0 -%% Session = #session{} +-spec lookup(cache_ref(), key()) -> #session{} | undefined. %% %% Description: Looks up a cach entry. Should be callable from any %% process. @@ -62,9 +61,7 @@ lookup(Cache, Key) -> end. %%-------------------------------------------------------------------- -%% Function: update(Cache, Key, Session) -> _ -%% Cache - as returned by create/0 -%% Session = #session{} +-spec update(cache_ref(), key(), #session{}) -> any(). %% %% Description: Caches a new session or updates a already cached one. %% Will only be called from the ssl_manager process. @@ -73,11 +70,7 @@ update(Cache, Key, Session) -> ets:insert(Cache, {Key, Session}). %%-------------------------------------------------------------------- -%% Function: delete(Cache, HostIP, Port, Id) -> _ -%% Cache - as returned by create/0 -%% HostIP = Host = string() | ipadress() -%% Port = integer() -%% Id = +-spec delete(cache_ref(), key()) -> any(). %% %% Description: Delets a cache entry. %% Will only be called from the ssl_manager process. @@ -86,28 +79,19 @@ delete(Cache, Key) -> ets:delete(Cache, Key). %%-------------------------------------------------------------------- -%% Function: foldl(Fun, Acc0, Cache) -> Acc -%% -%% Fun - fun() -%% Acc0 - term() -%% Cache - cache_ref() -%% +-spec foldl(fun(), term(), cache_ref()) -> term(). %% %% Description: Calls Fun(Elem, AccIn) on successive elements of the %% cache, starting with AccIn == Acc0. Fun/2 must return a new %% accumulator which is passed to the next call. The function returns -%% the final value of the accumulator. Acc0 is returned if the cache is -%% empty. -%% Should be callable from any process +%% the final value of the accumulator. Acc0 is returned if the cache +%% is empty.Should be callable from any process %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). %%-------------------------------------------------------------------- -%% Function: select_session(Cache, PartialKey) -> [Sessions] -%% -%% Cache - as returned by create/0 -%% PartialKey - opaque Key = {PartialKey, SessionId} +-spec select_session(cache_ref(), {host(), port_num()} | port_num()) -> [#session{}]. %% %% Description: Selects a session that could be reused. Should be callable %% from any process. @@ -119,6 +103,5 @@ select_session(Cache, PartialKey) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- - cache_name() -> ssl_otp_session_cache. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index 88b801d46b..375adf263a 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -38,6 +38,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(PremasterSecret), ?DBG_HEX(ClientRandom), @@ -57,6 +59,8 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> ?DBG_HEX(B), B. +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { @@ -75,6 +79,8 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), <<MD5/binary, SHA/binary>>. +-spec certificate_verify(key_algo(), binary(), {binary(), binary()}) -> binary(). + certificate_verify(Algorithm, MasterSecret, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> %% md5_hash @@ -94,6 +100,8 @@ certificate_verify(dhe_dss, MasterSecret, {_, SHAHash}) -> %% SHA(handshake_messages + master_secret + pad_1)); handshake_hash(?SHA, MasterSecret, undefined, SHAHash). +-spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> %% draft-ietf-tls-ssl-version3-00 - 5.2.3.1 %% hash(MAC_write_secret + pad_2 + @@ -113,6 +121,10 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, Length, Fragment) -> ?DBG_HEX(Mac), Mac. +-spec setup_keys(binary(), binary(), binary(), binary(), + integer(), integer(), binary()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> KeyBlock = generate_keyblock(MasterSecret, ServerRandom, ClientRandom, 2*(HS+KML+IVS)), @@ -136,6 +148,8 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HS, KML, _EKML, IVS) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV}. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 70db632835..d1bc0730ba 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -36,6 +36,8 @@ %% Internal application API %%==================================================================== +-spec master_secret(binary(), binary(), binary()) -> binary(). + master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, %% "master secret", ClientHello.random + @@ -43,6 +45,8 @@ master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> prf(PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). +-spec finished(client | server, binary(), {binary(), binary()}) -> binary(). + finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { @@ -56,6 +60,7 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> SHA = hash_final(?SHA, SHAHash), prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). +-spec certificate_verify(key_algo(), {binary(), binary()}) -> binary(). certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; Algorithm == dhe_rsa -> @@ -65,7 +70,11 @@ certificate_verify(Algorithm, {MD5Hash, SHAHash}) when Algorithm == rsa; certificate_verify(dhe_dss, {_, SHAHash}) -> hash_final(?SHA, SHAHash). - + +-spec setup_keys(binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), + binary(), binary(), binary()}. + setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen, IVSize) -> %% RFC 2246 - 6.3. Key calculation @@ -112,6 +121,9 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, %% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, %% ServerWriteKey, undefined, undefined}. +-spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), + integer(), binary()) -> binary(). + mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Length, Fragment) -> %% RFC 2246 & 4346 - 6.2.3.1. @@ -132,6 +144,8 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, ?DBG_HEX(Mac), Mac. +-spec suites() -> [cipher_suite()]. + suites() -> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, |