diff options
author | Jakub Witczak <u3s@users.noreply.github.com> | 2023-03-28 15:49:22 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-28 15:49:22 +0200 |
commit | ae9bdb95ff6659b4f52afb65e9c00af8151bd8b3 (patch) | |
tree | f1809a87e344460a8671ef3d2cab79879982f162 | |
parent | 8d0442ed1912bb8cc8b39c81af141e591fd32219 (diff) | |
parent | bfcac10062c4cb8cbde6f197649841ab0cc8d07d (diff) | |
download | erlang-ae9bdb95ff6659b4f52afb65e9c00af8151bd8b3.tar.gz |
Merge pull request #7033 from u3s/kuba/ssl/exp_ocsp_refactor
Kuba/ssl/exp ocsp refactor
-rw-r--r-- | lib/public_key/src/pubkey_ocsp.erl | 117 | ||||
-rw-r--r-- | lib/public_key/src/public_key.erl | 81 | ||||
-rw-r--r-- | lib/ssl/src/dtls_connection.erl | 7 | ||||
-rw-r--r-- | lib/ssl/src/ssl.erl | 54 | ||||
-rw-r--r-- | lib/ssl/src/ssl_certificate.erl | 7 | ||||
-rw-r--r-- | lib/ssl/src/ssl_gen_statem.erl | 16 | ||||
-rw-r--r-- | lib/ssl/src/ssl_handshake.erl | 47 | ||||
-rw-r--r-- | lib/ssl/src/ssl_internal.hrl | 10 | ||||
-rw-r--r-- | lib/ssl/src/ssl_trace.erl | 27 | ||||
-rw-r--r-- | lib/ssl/src/tls_dtls_connection.erl | 12 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake.erl | 18 | ||||
-rw-r--r-- | lib/ssl/src/tls_handshake_1_3.erl | 11 | ||||
-rw-r--r-- | lib/ssl/test/openssl_ocsp_SUITE.erl | 116 | ||||
-rw-r--r-- | lib/ssl/test/ssl_api_SUITE.erl | 21 | ||||
-rw-r--r-- | lib/ssl/test/ssl_dist_test_lib.erl | 8 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.erl | 27 | ||||
-rw-r--r-- | lib/ssl/test/ssl_test_lib.hrl | 2 | ||||
-rw-r--r-- | lib/ssl/test/ssl_trace_SUITE.erl | 10 |
18 files changed, 348 insertions, 243 deletions
diff --git a/lib/public_key/src/pubkey_ocsp.erl b/lib/public_key/src/pubkey_ocsp.erl index ea658f1c5d..6bdb9a563f 100644 --- a/lib/public_key/src/pubkey_ocsp.erl +++ b/lib/public_key/src/pubkey_ocsp.erl @@ -19,27 +19,17 @@ %% -module(pubkey_ocsp). - -include("public_key.hrl"). --export([otp_cert/1, - get_ocsp_responder_id/1, +-export([find_single_response/3, + get_acceptable_response_types_extn/0, get_nonce_extn/1, + get_ocsp_responder_id/1, + ocsp_status/1, verify_ocsp_response/3, - get_acceptable_response_types_extn/0, - find_single_response/3, - ocsp_status/1]). - -%% Test related exports --export([decode_ocsp_response/1]). - -otp_cert(#'OTPCertificate'{} = Cert) -> - Cert; -otp_cert(#'Certificate'{} = Cert) -> - public_key:pkix_decode_cert( - public_key:der_encode('Certificate', Cert), otp); -otp_cert(CertDer) when is_binary(CertDer) -> - public_key:pkix_decode_cert(CertDer, otp). + decode_ocsp_response/1]). +%% Tracing +-export([handle_trace/3]). -spec get_ocsp_responder_id(#'Certificate'{}) -> binary(). get_ocsp_responder_id(#'Certificate'{tbsCertificate = TbsCert}) -> @@ -55,11 +45,10 @@ get_nonce_extn(Nonce) when is_binary(Nonce) -> extnValue = Nonce }. --spec verify_ocsp_response(binary(), list(), undefined | binary()) -> +-spec verify_ocsp_response(#'BasicOCSPResponse'{}, list(), undefined | binary()) -> {ok, term()} | {error, term()}. -verify_ocsp_response(OCSPResponseDer, ResponderCerts, Nonce) -> - do_verify_ocsp_response( - decode_ocsp_response(OCSPResponseDer), ResponderCerts, Nonce). +verify_ocsp_response(OCSPResponse, ResponderCerts, Nonce) -> + do_verify_ocsp_response(OCSPResponse, ResponderCerts, Nonce). -spec get_acceptable_response_types_extn() -> #'Extension'{}. get_acceptable_response_types_extn() -> @@ -114,8 +103,7 @@ match_single_response(IssuerName, IssuerKey, SerialNum, match_single_response(IssuerName, IssuerKey, SerialNum, Responses) end. -get_serial_num(Cert) -> - #'OTPCertificate'{tbsCertificate = TbsCert} = otp_cert(Cert), +get_serial_num(#'OTPCertificate'{tbsCertificate = TbsCert}) -> TbsCert#'OTPTBSCertificate'.serialNumber. decode_response_bytes(#'ResponseBytes'{ @@ -125,25 +113,22 @@ decode_response_bytes(#'ResponseBytes'{ decode_response_bytes(#'ResponseBytes'{responseType = RespType}) -> {error, {ocsp_response_type_not_supported, RespType}}. -do_verify_ocsp_response({ok, #'BasicOCSPResponse'{ - tbsResponseData = ResponseData, - signatureAlgorithm = SignatureAlgo, - signature = Signature, - certs = Certs}}, +do_verify_ocsp_response(#'BasicOCSPResponse'{ + tbsResponseData = ResponseData, + signatureAlgorithm = SignatureAlgo, + signature = Signature}, ResponderCerts, Nonce) -> #'ResponseData'{responderID = ResponderID} = ResponseData, case verify_ocsp_signature( public_key:der_encode('ResponseData', ResponseData), SignatureAlgo#'AlgorithmIdentifier'.algorithm, - Signature, Certs ++ ResponderCerts, + Signature, ResponderCerts, ResponderID) of ok -> verify_ocsp_nonce(ResponseData, Nonce); {error, Reason} -> {error, Reason} - end; -do_verify_ocsp_response({error, Reason}, _ResponderCerts, _Nonce) -> - {error, Reason}. + end. verify_ocsp_nonce(ResponseData, Nonce) -> #'ResponseData'{responses = Responses, responseExtensions = ResponseExtns} = @@ -188,18 +173,29 @@ find_responder_cert(ResponderID, [Cert | TCerts]) -> find_responder_cert(ResponderID, TCerts) end. +do_verify_ocsp_signature(ResponseDataDer, Signature, AlgorithmID, Cert) -> + {DigestType, _SignatureType} = public_key:pkix_sign_types(AlgorithmID), + case public_key:verify( + ResponseDataDer, DigestType, Signature, + get_public_key_rec(Cert)) of + true -> + ok; + false -> + {error, ocsp_response_bad_signature} + end. + +get_public_key_rec(#'OTPCertificate'{tbsCertificate = TbsCert}) -> + PKInfo = TbsCert#'OTPTBSCertificate'.subjectPublicKeyInfo, + PKInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey. + is_responder({byName, Name}, Cert) -> public_key:der_encode('Name', Name) == get_subject_name(Cert); is_responder({byKey, Key}, Cert) -> Key == crypto:hash(sha, get_public_key(Cert)). -get_subject_name(#'Certificate'{} = Cert) -> - get_subject_name(otp_cert(Cert)); get_subject_name(#'OTPCertificate'{tbsCertificate = TbsCert}) -> public_key:pkix_encode('Name', TbsCert#'OTPTBSCertificate'.subject, otp). -get_public_key(#'Certificate'{} = Cert) -> - get_public_key(otp_cert(Cert)); get_public_key(#'OTPCertificate'{tbsCertificate = TbsCert}) -> PKInfo = TbsCert#'OTPTBSCertificate'.subjectPublicKeyInfo, enc_pub_key(PKInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey). @@ -211,20 +207,35 @@ enc_pub_key({DsaInt, #'Dss-Parms'{}}) when is_integer(DsaInt) -> enc_pub_key({#'ECPoint'{point = Key}, _ECParam}) -> Key. -do_verify_ocsp_signature(ResponseDataDer, Signature, AlgorithmID, Cert) -> - {DigestType, _SignatureType} = public_key:pkix_sign_types(AlgorithmID), - case public_key:verify( - ResponseDataDer, DigestType, Signature, - get_public_key_rec(Cert)) of - true -> - ok; - false -> - {error, ocsp_response_bad_signature} - end. - -get_public_key_rec(#'Certificate'{} = Cert) -> - get_public_key_rec(otp_cert(Cert)); -get_public_key_rec(#'OTPCertificate'{tbsCertificate = TbsCert}) -> - PKInfo = TbsCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - PKInfo#'OTPSubjectPublicKeyInfo'.subjectPublicKey. - +%%%################################################################ +%%%# +%%%# Tracing +%%%# +handle_trace(csp, + {call, {?MODULE, do_verify_ocsp_response, [BasicOcspResponse | _]}}, Stack) -> + #'BasicOCSPResponse'{ + tbsResponseData = + #'ResponseData'{responderID = ResponderID, + producedAt = ProducedAt}} = BasicOcspResponse, + {io_lib:format("ResponderId = ~W producedAt = ~p", [ResponderID, 5, ProducedAt]), Stack}; +handle_trace(csp, + {call, {?MODULE, match_single_response, + [_IssuerName, _IssuerKey, _SerialNum, + [#'SingleResponse'{thisUpdate = ThisUpdate, + nextUpdate = NextUpdate}]]}}, Stack) -> + {io_lib:format("ThisUpdate = ~p NextUpdate = ~p", [ThisUpdate, NextUpdate]), Stack}; +handle_trace(csp, + {call, {?MODULE, is_responder, [Id, Cert]}}, Stack) -> + {io_lib:format("~nId = ~P~nCert = ~P", [Id, 10, Cert, 10]), Stack}; +handle_trace(csp, + {call, {?MODULE, find_single_response, [Cert, IssuerCert | _]}}, Stack) -> + {io_lib:format("#2 OCSP validation started~nCert = ~W IssuerCert = ~W", + [Cert, 7, IssuerCert, 7]), Stack}; + %% {io_lib:format("#2 OCSP validation started~nCert = ~s IssuerCert = ~s", + %% [ssl_test_lib:format_cert(Cert), + %% ssl_test_lib:format_cert(IssuerCert)]), Stack}; + +handle_trace(csp, + {return_from, {?MODULE, is_responder, 2}, Return}, + Stack) -> + {io_lib:format("Return = ~p", [Return]), Stack}. diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 77e8b70e96..8c3805c219 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -69,6 +69,8 @@ cacerts_load/1, cacerts_clear/0 ]). +%% Tracing +-export([handle_trace/3]). %%---------------- %% Moved to ssh @@ -77,7 +79,6 @@ {ssh_hostkey_fingerprint,1, "use ssh:hostkey_fingerprint/1 instead"}, {ssh_hostkey_fingerprint,2, "use ssh:hostkey_fingerprint/2 instead"} ]). - -export([ssh_curvename2oid/1, oid2ssh_curvename/1]). %% When removing for OTP-25.0, remember to also remove %% - most of pubkey_ssh.erl except @@ -1382,9 +1383,28 @@ pkix_ocsp_validate(Cert, DerIssuerCert, OcspRespDer, ResponderCerts, NonceExt) pkix_ocsp_validate(Cert, pkix_decode_cert(DerIssuerCert, otp), OcspRespDer, ResponderCerts, NonceExt); pkix_ocsp_validate(Cert, IssuerCert, OcspRespDer, ResponderCerts, NonceExt) -> - case ocsp_responses(OcspRespDer, ResponderCerts, NonceExt) of + OcspResponse = pubkey_ocsp:decode_ocsp_response(OcspRespDer), + OcspCertResponses = + case OcspResponse of + {ok, BasicOcspResponse = #'BasicOCSPResponse'{certs = Certs}} -> + OcspResponseCerts = [otp_cert(C) || C <- Certs], + UserResponderCerts = + [otp_cert(pkix_decode_cert(C, plain)) || C <- ResponderCerts], + pubkey_ocsp:verify_ocsp_response( + BasicOcspResponse, OcspResponseCerts ++ UserResponderCerts, + NonceExt); + {error, _} = Error -> + Error + end, + case OcspCertResponses of {ok, Responses} -> - ocsp_status(Cert, IssuerCert, Responses); + case pubkey_ocsp:find_single_response( + otp_cert(Cert), otp_cert(IssuerCert), Responses) of + {ok, #'SingleResponse'{certStatus = CertStatus}} -> + pubkey_ocsp:ocsp_status(CertStatus); + {error, no_matched_response = Reason} -> + {bad_cert, {revocation_status_undetermined, Reason}} + end; {error, Reason} -> {bad_cert, {revocation_status_undetermined, Reason}} end. @@ -1399,12 +1419,12 @@ ocsp_extensions(Nonce) -> erlang:is_record(Extn, 'Extension')]. %%-------------------------------------------------------------------- --spec ocsp_responder_id(#'Certificate'{}) -> binary(). +-spec ocsp_responder_id(binary()) -> binary(). %% %% Description: Get the OCSP responder ID der %%-------------------------------------------------------------------- -ocsp_responder_id(Cert) -> - pubkey_ocsp:get_ocsp_responder_id(Cert). +ocsp_responder_id(CertDer) -> + pubkey_ocsp:get_ocsp_responder_id(pkix_decode_cert(CertDer, plain)). %%-------------------------------------------------------------------- -spec cacerts_get() -> [combined_cert()]. @@ -1622,7 +1642,9 @@ otp_cert(Der) when is_binary(Der) -> otp_cert(#'OTPCertificate'{} = Cert) -> Cert; otp_cert(#cert{otp = OtpCert}) -> - OtpCert. + OtpCert; +otp_cert(#'Certificate'{} = Cert) -> + pkix_decode_cert(der_encode('Certificate', Cert), otp). der_cert(#'OTPCertificate'{} = Cert) -> pkix_encode('OTPCertificate', Cert, otp); @@ -2031,18 +2053,39 @@ format_details([]) -> no_relevant_crls; format_details(Details) -> Details. - -ocsp_status(Cert, IssuerCert, Responses) -> - case pubkey_ocsp:find_single_response(Cert, IssuerCert, Responses) of - {ok, #'SingleResponse'{certStatus = CertStatus}} -> - pubkey_ocsp:ocsp_status(CertStatus); - {error, no_matched_response = Reason} -> - {bad_cert, {revocation_status_undetermined, Reason}} - end. - -ocsp_responses(OCSPResponseDer, ResponderCerts, Nonce) -> - pubkey_ocsp:verify_ocsp_response(OCSPResponseDer, - ResponderCerts, Nonce). subject_public_key_info(Alg, PubKey) -> #'OTPSubjectPublicKeyInfo'{algorithm = Alg, subjectPublicKey = PubKey}. + +%%%################################################################ +%%%# +%%%# Tracing +%%%# +handle_trace(csp, + {call, {?MODULE, ocsp_responder_id, [Cert]}}, Stack) -> + {io_lib:format("pkix_decode_cert(Cert, plain) = ~W", [Cert, 5]), + %% {io_lib:format("pkix_decode_cert(Cert, plain) = ~s", [ssl_test_lib:format_cert(Cert)]), + Stack}; +handle_trace(csp, + {return_from, {?MODULE, ocsp_responder_id, 1}, Return}, + Stack) -> + {io_lib:format("OCSP Responder ID = ~P", [Return, 10]), Stack}; +handle_trace(crt, + {call, {?MODULE, pkix_decode_cert, [Cert, _Type]}}, Stack) -> + {io_lib:format("Cert = ~W", [Cert, 5]), Stack}; + %% {io_lib:format("Cert = ~s", [ssl_test_lib:format_cert(Cert)]), Stack}; +handle_trace(csp, + {call, {?MODULE, pkix_ocsp_validate, [Cert, IssuerCert | _]}}, Stack) -> + {io_lib:format("#2 OCSP validation started~nCert = ~W IssuerCert = ~W", + [Cert, 7, IssuerCert, 7]), Stack}; + %% {io_lib:format("#2 OCSP validation started~nCert = ~s IssuerCert = ~s", + %% [ssl_test_lib:format_cert(Cert), + %% ssl_test_lib:format_cert(IssuerCert)]), Stack}; +handle_trace(csp, + {call, {?MODULE, otp_cert, [Cert]}}, Stack) -> + {io_lib:format("Cert = ~W", [Cert, 5]), Stack}; + %% {io_lib:format("Cert = ~s", [ssl_test_lib:format_cert(otp_cert(Cert))]), Stack}; +handle_trace(csp, + {return_from, {?MODULE, pkix_ocsp_validate, 5}, Return}, + Stack) -> + {io_lib:format("#2 OCSP validation result = ~p", [Return]), Stack}. diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 197d2397bb..a37a72efdc 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -88,7 +88,7 @@ %% | Abbrev Flight 1 to Abbrev Flight 2 part 1 %% | %% New session | Resumed session -%% WAIT_OCSP_STAPELING CERTIFY <----------------------------------> ABBRIVIATED +%% WAIT_OCSP_STAPLING CERTIFY <----------------------------------> ABBREVIATED %% %% <- Possibly Receive -- | | %% OCSP Stapel ------> | Send/ Recv Flight 5 | @@ -307,13 +307,12 @@ hello(internal, #hello_verify_request{cookie = Cookie}, handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, ocsp_stapling_state = OcspState0} = HsEnv, connection_env = CEnv, - ssl_options = #{ocsp_stapling := OcspStaplingOpt, - ocsp_nonce := OcspNonceOpt} = SslOpts, + ssl_options = SslOpts, session = #session{session_id = Id}, connection_states = ConnectionStates0, protocol_specific = PS } = State0) -> - OcspNonce = tls_handshake:ocsp_nonce(OcspNonceOpt, OcspStaplingOpt), + OcspNonce = tls_handshake:ocsp_nonce(SslOpts), Hello = dtls_handshake:client_hello(Host, Port, Cookie, ConnectionStates0, SslOpts, Id, Renegotiation, OcspNonce), Version = Hello#client_hello.client_version, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 8c87329ce9..29d0cb0a32 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1927,23 +1927,32 @@ opt_tickets(UserOpts, #{versions := Versions} = Opts, #{role := server}) -> anti_replay => AntiReplay, stateless_tickets_seed => STS}. opt_ocsp(UserOpts, #{versions := _Versions} = Opts, #{role := Role}) -> - {_, Stapling} = get_opt_bool(ocsp_stapling, false, UserOpts, Opts), + {Stapling, SMap} = + case get_opt(ocsp_stapling, ?DEFAULT_OCSP_STAPLING, UserOpts, Opts) of + {old, Map} when is_map(Map) -> {true, Map}; + {_, Bool} when is_boolean(Bool) -> {Bool, #{}}; + {_, Value} -> option_error(ocsp_stapling, Value) + end, assert_client_only(Role, Stapling, ocsp_stapling), - - {_, Nonce} = get_opt_bool(ocsp_nonce, true, UserOpts, Opts), - option_incompatible(Stapling =:= false andalso Nonce =:= false, [{ocsp_nonce, false}, {ocsp_stapling, false}]), - - {_, ORC} = get_opt_list(ocsp_responder_certs, [], UserOpts, Opts), - option_incompatible(Stapling =:= false andalso ORC =/= [], [ocsp_responder_certs, {ocsp_stapling, false}]), - %% FIXME should not be decoded in users process !! - Decode = fun(CertDer) -> - try public_key:pkix_decode_cert(CertDer, plain) - catch _:_ -> option_error(ocsp_responder_certs, ORC) - end - end, - Certs = [Decode(CertDer) || CertDer <- ORC], - %% FIXME make it one option? - Opts#{ocsp_stapling => Stapling, ocsp_nonce => Nonce, ocsp_responder_certs => Certs}. + {_, Nonce} = get_opt_bool(ocsp_nonce, ?DEFAULT_OCSP_NONCE, UserOpts, SMap), + option_incompatible(Stapling =:= false andalso Nonce =:= false, + [{ocsp_nonce, false}, {ocsp_stapling, false}]), + {_, ORC} = get_opt_list(ocsp_responder_certs, ?DEFAULT_OCSP_RESPONDER_CERTS, + UserOpts, SMap), + CheckBinary = fun(Cert) when is_binary(Cert) -> ok; + (_Cert) -> option_error(ocsp_responder_certs, ORC) + end, + [CheckBinary(C) || C <- ORC], + option_incompatible(Stapling =:= false andalso ORC =/= [], + [ocsp_responder_certs, {ocsp_stapling, false}]), + case Stapling of + true -> + Opts#{ocsp_stapling => + #{ocsp_nonce => Nonce, + ocsp_responder_certs => ORC}}; + false -> + Opts + end. opt_sni(UserOpts, #{versions := _Versions} = Opts, #{role := server}) -> {_, SniHosts} = get_opt_list(sni_hosts, [], UserOpts, Opts), @@ -2857,11 +2866,10 @@ unambiguous_path(Value) -> %%%# %%%# Tracing %%%# +handle_trace(csp, {call, {?MODULE, opt_ocsp, [UserOpts | _]}}, Stack) -> + {format_ocsp_params(UserOpts), Stack}; handle_trace(csp, {return_from, {?MODULE, opt_ocsp, 3}, Return}, Stack) -> - #{ocsp_stapling := Stapling, ocsp_nonce := Nonce, - ocsp_responder_certs := Certs} = Return, - {io_lib:format("Stapling = ~w Nonce = ~W Certs = ~W", - [Stapling, Nonce, 5, Certs, 5]), Stack}; + {format_ocsp_params(Return), Stack}; handle_trace(rle, {call, {?MODULE, listen, Args}}, Stack0) -> Role = server, {io_lib:format("(*~w) Args = ~W", [Role, Args, 10]), [{role, Role} | Stack0]}; @@ -2869,3 +2877,9 @@ handle_trace(rle, {call, {?MODULE, connect, Args}}, Stack0) -> Role = client, {io_lib:format("(*~w) Args = ~W", [Role, Args, 10]), [{role, Role} | Stack0]}. +format_ocsp_params(Map) -> + Stapling = maps:get(ocsp_stapling, Map, '?'), + Nonce = maps:get(ocsp_nonce, Map, '?'), + Certs = maps:get(ocsp_responder_certs, Map, '?'), + io_lib:format("Stapling = ~W Nonce = ~W Certs = ~W", + [Stapling, 5, Nonce, 5, Certs, 5]). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 5b45354a9b..cc641ca2f5 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -834,11 +834,14 @@ cert_auth_member(ChainSubjects, CertAuths) -> handle_trace(crt, {call, {?MODULE, validate, [Cert, StatusOrExt| _]}}, Stack) -> {io_lib:format("[~W] StatusOrExt = ~W", [Cert, 3, StatusOrExt, 10]), Stack}; + %% {io_lib:format("(~s) StatusOrExt = ~W", + %% [ssl_test_lib:format_cert(Cert), StatusOrExt, 10]), Stack}; handle_trace(crt, {call, {?MODULE, verify_cert_extensions, [Cert, _UserState, [], _Context]}}, Stack) -> {io_lib:format(" no more extensions [~W]", [Cert, 3]), Stack}; + %% {io_lib:format(" no more extensions (~s)", [ssl_test_lib:format_cert(Cert)]), Stack}; handle_trace(crt, {call, {?MODULE, verify_cert_extensions, [Cert, #{ocsp_responder_certs := _ResponderCerts, @@ -849,7 +852,11 @@ handle_trace(crt, {call, {?MODULE, verify_cert_extensions, {io_lib:format("#2 OcspState = ~W Issuer = [~W] OcspResponsDer = ~W [~W]", [OcspState, 10, Issuer, 3, OcspResponsDer, 2, Cert, 3]), Stack}; + %% {io_lib:format("#2 OcspState = ~W Issuer = (~s) OcspResponsDer = ~W (~s)", + %% [OcspState, 10, ssl_test_lib:format_cert(Issuer), + %% OcspResponsDer, 2, ssl_test_lib:format_cert(Cert)]), handle_trace(crt, {return_from, {ssl_certificate, verify_cert_extensions, 4}, {valid, #{issuer := Issuer}}}, Stack) -> {io_lib:format(" extensions valid Issuer = ~W", [Issuer, 3]), Stack}. + %% {io_lib:format(" extensions valid Issuer = ~s", [ssl_test_lib:format_cert(Issuer)]), Stack}. diff --git a/lib/ssl/src/ssl_gen_statem.erl b/lib/ssl/src/ssl_gen_statem.erl index 139d922172..d0941a9689 100644 --- a/lib/ssl/src/ssl_gen_statem.erl +++ b/lib/ssl/src/ssl_gen_statem.erl @@ -489,8 +489,6 @@ initial_hello({call, From}, {start, Timeout}, %% Versions is a descending list of supported versions. versions := [HelloVersion|_] = Versions, session_tickets := SessionTickets, - ocsp_stapling := OcspStaplingOpt, - ocsp_nonce := OcspNonceOpt, early_data := EarlyData} = SslOpts, session = Session, connection_states = ConnectionStates0 @@ -502,7 +500,7 @@ initial_hello({call, From}, {start, Timeout}, %% the max_early_data_size or if it can only be used for session resumption. {UseTicket, State1} = tls_client_connection_1_3:maybe_automatic_session_resumption(State0), TicketData = tls_handshake_1_3:get_ticket_data(self(), SessionTickets, UseTicket), - OcspNonce = tls_handshake:ocsp_nonce(OcspNonceOpt, OcspStaplingOpt), + OcspNonce = tls_handshake:ocsp_nonce(SslOpts), Hello0 = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, Session#session.session_id, Renegotiation, @@ -544,13 +542,16 @@ initial_hello({call, From}, {start, Timeout}, {#state{handshake_env = HsEnv1} = State5, _} = Connection:send_handshake_flight(State4), + OcspStaplingKeyPresent = maps:is_key(ocsp_stapling, SslOpts), State = State5#state{ connection_env = CEnv#connection_env{ negotiated_version = RequestedVersion}, session = Session, - handshake_env = HsEnv1#handshake_env{ - ocsp_stapling_state = OcspState0#{ocsp_nonce => OcspNonce, - ocsp_stapling => OcspStaplingOpt}}, + handshake_env = + HsEnv1#handshake_env{ + ocsp_stapling_state = + OcspState0#{ocsp_nonce => OcspNonce, + ocsp_stapling => OcspStaplingKeyPresent}}, start_or_recv_from = From, key_share = KeyShare}, NextState = next_statem_state(Versions, Role), @@ -2243,6 +2244,9 @@ maybe_generate_client_shares(_) -> %%%# %%%# Tracing %%%# +handle_trace(api, + {call, {?MODULE, connect, [Connection | _]}}, Stack0) -> + {io_lib:format("Connection = ~w", [Connection]), Stack0}; handle_trace(rle, {call, {?MODULE, init, Args = [[Role | _]]}}, Stack0) -> {io_lib:format("(*~w) Args = ~W", [Role, Args, 3]), [{role, Role} | Stack0]}; diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 152d3f16bd..1915b6f10c 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -84,7 +84,6 @@ -export([get_cert_params/1, select_own_cert/1, server_name/3, - path_validate/9, path_validation/10, validation_fun_and_state/4, path_validation_alert/1]). @@ -1323,13 +1322,9 @@ maybe_add_tls13_extensions({3,4}, maybe_add_tls13_extensions(_, HelloExtensions, _, _, _, _,_) -> HelloExtensions. -maybe_add_certificate_status_request( - _Version, #{ocsp_stapling := false}, _OcspNonce, HelloExtensions) -> - HelloExtensions; -maybe_add_certificate_status_request( - _Version, #{ocsp_stapling := true, - ocsp_responder_certs := OcspResponderCerts}, - OcspNonce, HelloExtensions) -> +maybe_add_certificate_status_request(_Version, #{ocsp_stapling := OcspStapling}, + OcspNonce, HelloExtensions) -> + OcspResponderCerts = maps:get(ocsp_responder_certs, OcspStapling), OcspResponderList = get_ocsp_responder_list(OcspResponderCerts), OcspRequestExtns = public_key:ocsp_extensions(OcspNonce), Req = #ocsp_status_request{responder_id_list = OcspResponderList, @@ -1338,7 +1333,10 @@ maybe_add_certificate_status_request( status_type = ?CERTIFICATE_STATUS_TYPE_OCSP, request = Req }, - HelloExtensions#{status_request => CertStatusReqExtn}. + HelloExtensions#{status_request => CertStatusReqExtn}; +maybe_add_certificate_status_request(_Version, _SslOpts, _OcspNonce, + HelloExtensions) -> + HelloExtensions. get_ocsp_responder_list(ResponderCerts) -> get_ocsp_responder_list(ResponderCerts, []). @@ -1537,8 +1535,8 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, Exts, Version, - #{secure_renegotiate := SecureRenegotation, - ocsp_stapling := Stapling} = SslOpts, + #{secure_renegotiate := SecureRenegotation} = + SslOpts, ConnectionStates0, Renegotiation, IsNew) -> ConnectionStates = handle_renegotiation_extension(client, RecordCB, Version, maps:get(renegotiation_info, Exts, undefined), Random, @@ -1561,7 +1559,7 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, ok end, - case handle_ocsp_extension(Stapling, Exts) of + case handle_ocsp_extension(SslOpts, Exts) of #alert{} = Alert -> Alert; OcspState -> @@ -1956,21 +1954,21 @@ extension_value(#psk_key_exchange_modes{ke_modes = Modes}) -> extension_value(#cookie{cookie = Cookie}) -> Cookie. -handle_ocsp_extension(true = Stapling, Extensions) -> +handle_ocsp_extension(#{ocsp_stapling := _OcspStapling}, Extensions) -> case maps:get(status_request, Extensions, false) of undefined -> %% status_request in server hello is empty - #{ocsp_stapling => Stapling, + #{ocsp_stapling => true, ocsp_expect => staple}; false -> %% status_request is missing (not negotiated) - #{ocsp_stapling => Stapling, + #{ocsp_stapling => true, ocsp_expect => no_staple}; _Else -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, status_request_not_empty) end; -handle_ocsp_extension(false = Stapling, Extensions) -> +handle_ocsp_extension(_SslOpts, Extensions) -> case maps:get(status_request, Extensions, false) of false -> %% status_request is missing (not negotiated) - #{ocsp_stapling => Stapling, + #{ocsp_stapling => false, ocsp_expect => no_staple}; _Else -> %% unsolicited status_request ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_status_request) @@ -2123,8 +2121,7 @@ path_validation_alert({bad_cert, unknown_critical_extension}) -> path_validation_alert({bad_cert, {revoked, _}}) -> ?ALERT_REC(?FATAL, ?CERTIFICATE_REVOKED); path_validation_alert({bad_cert, {revocation_status_undetermined, Details}}) -> - Alert = ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE), - Alert#alert{reason = Details}; + ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, Details); path_validation_alert({bad_cert, selfsigned_peer}) -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE); path_validation_alert({bad_cert, unknown_ca}) -> @@ -2208,11 +2205,6 @@ cert_status_check(_OtpCert, ocsp_expect := undetermined}}, _VerifyResult, _CertPath, _LogLevel) -> {bad_cert, {revocation_status_undetermined, not_stapled}}; -cert_status_check(OtpCert, - #{ocsp_state := #{ocsp_stapling := best_effort, %% TODO support this ? - ocsp_expect := undetermined}} = SslState, - VerifyResult, CertPath, LogLevel) -> - maybe_check_crl(OtpCert, SslState, VerifyResult, CertPath, LogLevel); cert_status_check(_OtpCert, #{ocsp_state := #{ocsp_stapling := true, ocsp_expect := no_staple}}, @@ -3941,8 +3933,9 @@ path_validation_cb(_) -> %%%# handle_trace(csp, {call, {?MODULE, maybe_add_certificate_status_request, - [_Version, #{ocsp_stapling := true}, + [_Version, SslOpts, _OcspNonce, _HelloExtensions]}}, Stack) -> - {io_lib:format("#1 ADDING certificate status request", - []), Stack}. + OcspStapling = maps:get(ocsp_stapling, SslOpts, false), + {io_lib:format("#1 ADD crt status request / OcspStapling option = ~W", + [OcspStapling, 10]), Stack}. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index d2da9bd6fd..684f2c8530 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -201,11 +201,7 @@ }). -define(DEFAULT_DEPTH, 10). - - +-define(DEFAULT_OCSP_STAPLING, false). +-define(DEFAULT_OCSP_NONCE, true). +-define(DEFAULT_OCSP_RESPONDER_CERTS, []). -endif. % -ifdef(ssl_internal). - - - - - diff --git a/lib/ssl/src/ssl_trace.erl b/lib/ssl/src/ssl_trace.erl index 2bbbb79933..c8ac32712e 100644 --- a/lib/ssl/src/ssl_trace.erl +++ b/lib/ssl/src/ssl_trace.erl @@ -421,7 +421,9 @@ trace_profiles() -> [{ssl, [{listen,2}, {connect,3}, {handshake,2}, {close, 1}]}, {ssl_gen_statem, - [{initial_hello,3}, {connect, 8}, {close, 2}, {terminate_alert, 1}]} + [{initial_hello,3}, {connect, 8}, {close, 2}, {terminate_alert, 1}]}, + {tls_gen_connection, + [{start_connection_tree, 5}, {socket_control, 6}]} ]}, {csp, %% OCSP fun(M, F, A) -> dbg:tpl(M, F, A, x) end, @@ -429,27 +431,40 @@ trace_profiles() -> [{ssl_handshake, [{maybe_add_certificate_status_request, 4}, {client_hello_extensions, 10}, {cert_status_check, 5}, {get_ocsp_responder_list, 1}, {handle_ocsp_extension, 2}, + {path_validation, 10}, {handle_server_hello_extensions, 10}, {handle_client_hello_extensions, 10}, {cert_status_check, 5}]}, - {public_key, [{ocsp_extensions, 1}, %%{pkix_decode_cert, 2}, - {pkix_ocsp_validate, 5}]}, + {public_key, [{ocsp_extensions, 1}, {pkix_ocsp_validate, 5}, + {ocsp_responder_id, 1}, {otp_cert, 1}]}, + {pubkey_ocsp, [{find_responder_cert, 2}, {do_verify_ocsp_signature, 4}, + {verify_ocsp_response, 3}, {verify_ocsp_nonce, 2}, + {verify_ocsp_signature, 5}, {do_verify_ocsp_response, 3}, + {is_responder, 2}, {find_single_response, 3}, + {ocsp_status, 1}, {match_single_response, 4}]}, {ssl, [{opt_ocsp, 3}]}, {ssl_certificate, [{verify_cert_extensions, 4}]}, {ssl_test_lib, [{init_openssl_server, 3}, {openssl_server_loop, 3}]}, {tls_connection, [{wait_ocsp_stapling, 3}]}, {dtls_connection, [{initial_hello, 3}, {hello, 3}, {connection, 3}]}, {tls_dtls_connection, [{wait_ocsp_stapling, 3}, {certify, 3}]}, - {tls_handshake, [{ocsp_expect, 1}, {client_hello, 11}]}, + {tls_handshake, [{ocsp_nonce, 1}, {ocsp_expect, 1}, {client_hello, 11}]}, {dtls_handshake, [{client_hello, 8}]}]}, {crt, %% certificates fun(M, F, A) -> dbg:tpl(M, F, A, x) end, fun(M, F, A) -> dbg:ctpl(M, F, A) end, - [{public_key, [{pkix_path_validation, 3}]}, - {ssl_certificate, [{validate, 3}]}, + [{public_key, [{pkix_path_validation, 3}, {path_validation, 2}, + {pkix_decode_cert, 2}]}, + {ssl_certificate, [{validate, 3}, {trusted_cert_and_paths, 4}, + {certificate_chain, 3}, {certificate_chain, 5}, + {issuer, 1}]}, + {ssl_cipher, [{filter, 3}]}, {ssl_gen_statem, [{initial_hello, 3}]}, {ssl_handshake, [{path_validate, 11}, {path_validation, 10}, + {select_hashsign, 5}, {get_cert_params, 1}, + {cert_curve, 3}, {maybe_check_hostname, 3}, {maybe_check_hostname, 3}]}, + {ssl_pkix_db, [{decode_cert, 2}]}, {tls_handshake_1_3, [{path_validation, 10}]}, {tls_server_connection_1_3, [{init,1}]}, {tls_client_connection_1_3, [{init,1}]}, diff --git a/lib/ssl/src/tls_dtls_connection.erl b/lib/ssl/src/tls_dtls_connection.erl index c663d1de8f..51bbc79c98 100644 --- a/lib/ssl/src/tls_dtls_connection.erl +++ b/lib/ssl/src/tls_dtls_connection.erl @@ -1682,18 +1682,16 @@ ensure_tls({254, _} = Version) -> ensure_tls(Version) -> Version. -ocsp_info(#{ocsp_expect := stapled, - ocsp_response := CertStatus} = OcspState, - #{ocsp_responder_certs := OcspResponderCerts}, PeerCert) -> +ocsp_info(#{ocsp_expect := stapled, ocsp_response := CertStatus} = OcspState, + #{ocsp_stapling := OcspStapling} = _SslOpts, PeerCert) -> + #{ocsp_responder_certs := OcspResponderCerts} = OcspStapling, #{cert_ext => #{public_key:pkix_subject_id(PeerCert) => [CertStatus]}, ocsp_responder_certs => OcspResponderCerts, - ocsp_state => OcspState - }; + ocsp_state => OcspState}; ocsp_info(#{ocsp_expect := no_staple} = OcspState, _, PeerCert) -> #{cert_ext => #{public_key:pkix_subject_id(PeerCert) => []}, ocsp_responder_certs => [], - ocsp_state => OcspState - }. + ocsp_state => OcspState}. select_client_cert_key_pair(Session0,_, [#{private_key := NoKey, certs := [[]] = NoCerts}], diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index e73324eb11..6e2d0fa903 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -45,7 +45,7 @@ -export([get_tls_handshakes/4, decode_handshake/3]). %% Handshake helper --export([ocsp_nonce/2]). +-export([ocsp_nonce/1]). -type tls_handshake() :: #client_hello{} | ssl_handshake:ssl_handshake(). @@ -161,8 +161,9 @@ hello(#server_hello{server_version = LegacyVersion, #{server_hello_selected_version := #server_hello_selected_version{ selected_version = Version}} = HelloExt}, - #{versions := SupportedVersions, ocsp_stapling := Stapling} = SslOpt, + #{versions := SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation, OldId) -> + Stapling = maps:get(ocsp_stapling, SslOpt, ?DEFAULT_OCSP_STAPLING), %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version %% number for TLS 1.2. @@ -307,14 +308,17 @@ get_tls_handshakes(Version, Data, Buffer, Options) -> %%-------------------------------------------------------------------- %%-------------------------------------------------------------------- --spec ocsp_nonce(boolean(), boolean()) -> binary() | undefined. +-spec ocsp_nonce(map()) -> binary() | undefined. %% %% Description: Get an OCSP nonce %%-------------------------------------------------------------------- -ocsp_nonce(true, true) -> - public_key:der_encode('Nonce', crypto:strong_rand_bytes(8)); -ocsp_nonce(_OcspNonceOpt, _OcspStaplingOpt) -> - undefined. +ocsp_nonce(SslOpts) -> + case maps:get(ocsp_stapling, SslOpts, disabled) of + #{ocsp_nonce := true} -> + public_key:der_encode('Nonce', crypto:strong_rand_bytes(8)); + _ -> + undefined + end. %%-------------------------------------------------------------------- %%% Internal functions diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 70a2691ac4..6f883b7b64 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -814,10 +814,15 @@ update_encryption_state(client, State) -> validate_certificate_chain(CertEntries, CertDbHandle, CertDbRef, - #{ocsp_responder_certs := OcspResponderCerts - } = SslOptions, CRLDbHandle, Role, Host, OcspState0) -> + SslOptions, CRLDbHandle, Role, Host, OcspState0) -> {Certs, CertExt, OcspState} = split_cert_entries(CertEntries, OcspState0), - + OcspResponderCerts = + case maps:get(ocsp_stapling, SslOptions, disabled) of + #{ocsp_responder_certs := V} -> + V; + disabled -> + ?DEFAULT_OCSP_RESPONDER_CERTS + end, ssl_handshake:certify(#certificate{asn1_certificates = Certs}, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host, {3,4}, #{cert_ext => CertExt, diff --git a/lib/ssl/test/openssl_ocsp_SUITE.erl b/lib/ssl/test/openssl_ocsp_SUITE.erl index a06dcbcb7b..045915cc84 100644 --- a/lib/ssl/test/openssl_ocsp_SUITE.erl +++ b/lib/ssl/test/openssl_ocsp_SUITE.erl @@ -35,17 +35,18 @@ end_per_testcase/2]). %% Testcases --export([ocsp_stapling_basic/0,ocsp_stapling_basic/1, - ocsp_stapling_with_nonce/0, ocsp_stapling_with_nonce/1, - ocsp_stapling_with_responder_cert/0,ocsp_stapling_with_responder_cert/1, - ocsp_stapling_revoked/0, ocsp_stapling_revoked/1, - ocsp_stapling_undetermined/0, ocsp_stapling_undetermined/1, - ocsp_stapling_no_staple/0, ocsp_stapling_no_staple/1 +-export([stapling_basic/0, stapling_basic/1, + stapling_with_nonce/0, stapling_with_nonce/1, + stapling_with_responder_cert/0, stapling_with_responder_cert/1, + stapling_revoked/0, stapling_revoked/1, + stapling_undetermined/0, stapling_undetermined/1, + stapling_no_staple/0, stapling_no_staple/1 ]). %% spawn export --export([ocsp_responder_init/3]). - +-export([ocsp_responder_init/4]). +-define(OCSP_RESPONDER_LOG, "ocsp_resp_log.txt"). +-define(DEBUG, false). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -61,17 +62,18 @@ groups() -> {'dtlsv1.2', [], ocsp_tests()}]. ocsp_tests() -> - [ocsp_stapling_basic, - ocsp_stapling_with_nonce, - ocsp_stapling_with_responder_cert, - ocsp_stapling_revoked, - ocsp_stapling_undetermined, - ocsp_stapling_no_staple + [stapling_basic, + stapling_with_nonce, + stapling_with_responder_cert, + stapling_revoked, + stapling_undetermined, + stapling_no_staple ]. %%-------------------------------------------------------------------- init_per_suite(Config0) -> - Config = ssl_test_lib:init_per_suite(Config0, openssl), + Config = lists:merge([{debug, ?DEBUG}], + ssl_test_lib:init_per_suite(Config0, openssl)), case ssl_test_lib:openssl_ocsp_support(Config) of true -> do_init_per_suite(Config); @@ -87,7 +89,7 @@ do_init_per_suite(Config) -> {ok, _} = make_certs:all(DataDir, PrivDir), ResponderPort = get_free_port(), - Pid = start_ocsp_responder(ResponderPort, PrivDir), + Pid = start_ocsp_responder(ResponderPort, PrivDir, ?config(debug, Config)), NewConfig = lists:merge( @@ -97,10 +99,10 @@ do_init_per_suite(Config) -> ssl_test_lib:cert_options(NewConfig). - end_per_suite(Config) -> ResponderPid = proplists:get_value(responder_pid, Config), ssl_test_lib:close(ResponderPid), + [ssl_test_lib:ct_pal_file(?OCSP_RESPONDER_LOG) || ?config(debug, Config)], ssl_test_lib:end_per_suite(Config). %%-------------------------------------------------------------------- @@ -122,31 +124,30 @@ end_per_testcase(_TestCase, Config) -> %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- -ocsp_stapling_basic() -> +stapling_basic() -> [{doc, "Verify OCSP stapling works without nonce and responder certs."}]. -ocsp_stapling_basic(Config) +stapling_basic(Config) when is_list(Config) -> - ocsp_stapling_helper(Config, [{ocsp_nonce, false}]). + stapling_helper(Config, [{ocsp_nonce, false}]). -ocsp_stapling_with_nonce() -> +stapling_with_nonce() -> [{doc, "Verify OCSP stapling works with nonce."}]. -ocsp_stapling_with_nonce(Config) +stapling_with_nonce(Config) when is_list(Config) -> - ocsp_stapling_helper(Config, [{ocsp_nonce, true}]). + stapling_helper(Config, [{ocsp_nonce, true}]). -ocsp_stapling_with_responder_cert() -> +stapling_with_responder_cert() -> [{doc, "Verify OCSP stapling works with nonce and responder certs."}]. -ocsp_stapling_with_responder_cert(Config) +stapling_with_responder_cert(Config) when is_list(Config) -> PrivDir = proplists:get_value(priv_dir, Config), {ok, ResponderCert} = file:read_file(filename:join(PrivDir, "b.server/cert.pem")), [{'Certificate', Der, _IsEncrypted}] = public_key:pem_decode(ResponderCert), - ocsp_stapling_helper(Config, [{ocsp_nonce, true}, - {ocsp_responder_certs, [Der]}]). + stapling_helper(Config, [{ocsp_nonce, true}, {ocsp_responder_certs, [Der]}]). -ocsp_stapling_helper(Config, Opts) -> +stapling_helper(Config, Opts) -> PrivDir = proplists:get_value(priv_dir, Config), CACertsFile = filename:join(PrivDir, "a.server/cacerts.pem"), Data = "ping", %% 4 bytes @@ -159,7 +160,8 @@ ocsp_stapling_helper(Config, Opts) -> ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, {cacertfile, CACertsFile}, {server_name_indication, disable}, - {ocsp_stapling, true}] ++ Opts, Config), + {ocsp_stapling, true}] ++ Opts, + Config), Client = ssl_test_lib:start_client(erlang, [{port, Port}, {options, ClientOpts}], Config), @@ -169,29 +171,29 @@ ocsp_stapling_helper(Config, Opts) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -ocsp_stapling_revoked() -> +stapling_revoked() -> [{doc, "Verify OCSP stapling works with revoked certificate."}]. -ocsp_stapling_revoked(Config) +stapling_revoked(Config) when is_list(Config) -> - ocsp_stapling_negative_helper(Config, "revoked/cacerts.pem", + stapling_negative_helper(Config, "revoked/cacerts.pem", openssl_ocsp_revoked, certificate_revoked). -ocsp_stapling_undetermined() -> +stapling_undetermined() -> [{doc, "Verify OCSP stapling works with certificate with undetermined status."}]. -ocsp_stapling_undetermined(Config) +stapling_undetermined(Config) when is_list(Config) -> - ocsp_stapling_negative_helper(Config, "undetermined/cacerts.pem", + stapling_negative_helper(Config, "undetermined/cacerts.pem", openssl_ocsp_undetermined, bad_certificate). -ocsp_stapling_no_staple() -> +stapling_no_staple() -> [{doc, "Verify OCSP stapling works with a missing OCSP response."}]. -ocsp_stapling_no_staple(Config) +stapling_no_staple(Config) when is_list(Config) -> %% Start a server that will not include an OCSP response. - ocsp_stapling_negative_helper(Config, "a.server/cacerts.pem", + stapling_negative_helper(Config, "a.server/cacerts.pem", openssl, bad_certificate). -ocsp_stapling_negative_helper(Config, CACertsPath, ServerVariant, ExpectedError) -> +stapling_negative_helper(Config, CACertsPath, ServerVariant, ExpectedError) -> PrivDir = proplists:get_value(priv_dir, Config), CACertsFile = filename:join(PrivDir, CACertsPath), GroupName = undefined, @@ -214,12 +216,13 @@ ocsp_stapling_negative_helper(Config, CACertsPath, ServerVariant, ExpectedError) ssl_test_lib:check_client_alert(Client, ExpectedError). %%-------------------------------------------------------------------- -%% Intrernal functions ----------------------------------------------- +%% Internal functions ----------------------------------------------- %%-------------------------------------------------------------------- -start_ocsp_responder(ResponderPort, PrivDir) -> +start_ocsp_responder(ResponderPort, PrivDir, Debug) -> Starter = self(), - Pid = erlang:spawn_link( - ?MODULE, ocsp_responder_init, [ResponderPort, PrivDir, Starter]), + Pid = erlang:spawn( + ?MODULE, ocsp_responder_init, + [ResponderPort, PrivDir, Starter, Debug]), receive {started, Pid} -> Pid; @@ -227,31 +230,40 @@ start_ocsp_responder(ResponderPort, PrivDir) -> throw({unable_to_start_ocsp_service, Reason}) end. -ocsp_responder_init(ResponderPort, PrivDir, Starter) -> +ocsp_responder_init(ResponderPort, PrivDir, Starter, Debug) -> Index = filename:join(PrivDir, "otpCA/index.txt"), CACerts = filename:join(PrivDir, "b.server/cacerts.pem"), Cert = filename:join(PrivDir, "b.server/cert.pem"), Key = filename:join(PrivDir, "b.server/key.pem"), - + DebugArgs = case Debug of + true -> ["-text", "-out", ?OCSP_RESPONDER_LOG]; + _ -> [] + end, Args = ["ocsp", "-index", Index, "-CA", CACerts, "-rsigner", Cert, - "-rkey", Key, "-port", erlang:integer_to_list(ResponderPort)], + "-rkey", Key, "-port", erlang:integer_to_list(ResponderPort)] ++ + DebugArgs, process_flag(trap_exit, true), Port = ssl_test_lib:portable_open_port("openssl", Args), + ?CT_LOG("OCSP responder: Started Port = ~p", [Port]), ocsp_responder_loop(Port, {new, Starter}). ocsp_responder_loop(Port, {Status, Starter} = State) -> receive - {_Port, closed} -> - ?CT_LOG("Port Closed"), + close -> + ?CT_LOG("OCSP responder: received close", []), + ok; + {Port, closed} -> + ?CT_LOG("OCSP responder: Port = ~p Closed", [Port]), ok; - {'EXIT', _Port, Reason} -> - ?CT_LOG("Port Closed ~p",[Reason]), + {'EXIT', Sender, _Reason} -> + ?CT_LOG("OCSP responder: Sender = ~p Closed",[Sender]), ok; - {Port, {data, _Msg}} when Status == new -> + {Port, {data, Msg}} when Status == new -> + ?CT_LOG("OCSP responder: Msg = ~p", [Msg]), Starter ! {started, self()}, ocsp_responder_loop(Port, {started, undefined}); {Port, {data, Msg}} -> - ?CT_PAL("Responder Msg ~p",[Msg]), + ?CT_LOG("OCSP responder: Responder Msg = ~p",[Msg]), ocsp_responder_loop(Port, State) after 1000 -> case Status of diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl index 5cd25ae352..664f59a47d 100644 --- a/lib/ssl/test/ssl_api_SUITE.erl +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -2839,14 +2839,19 @@ options_frag_len(_Config) -> %% max_fragment_length ok. options_oscp(Config) -> - Cert = proplists:get_value(cert, ssl_test_lib:ssl_options(server_rsa_der_opts, Config)), - ?OK(#{ocsp_stapling := false, ocsp_nonce := true, ocsp_responder_certs := []}, - [], client), - ?OK(#{ocsp_stapling := true}, - [{ocsp_stapling, true}], client), - ?OK(#{ocsp_stapling := true, ocsp_nonce := false, ocsp_responder_certs := [_,_]}, - [{ocsp_stapling, true}, {ocsp_nonce, false}, {ocsp_responder_certs, [Cert,Cert]}], - client), + Cert = proplists:get_value( + cert, ssl_test_lib:ssl_options(server_rsa_der_opts, Config)), + NoOcspOption = [ocsp_stapling, ocsp_nonce, ocsp_responder_certs], + + ?OK(#{}, [], client, NoOcspOption), + ?OK(#{}, [{ocsp_stapling, false}], client, NoOcspOption), + ?OK(#{ocsp_stapling := #{ocsp_nonce := true, ocsp_responder_certs := []}}, + [{ocsp_stapling, true}], client, [ocsp_nonce, ocsp_responder_certs]), + ?OK(#{ocsp_stapling := + #{ocsp_nonce := false, ocsp_responder_certs := [_, _]}}, + [{ocsp_stapling, true}, {ocsp_nonce, false}, + {ocsp_responder_certs, [Cert,Cert]}], + client, [ocsp_nonce, ocsp_responder_certs]), %% Errors ?ERR({ocsp_stapling, foo}, [{ocsp_stapling, 'foo'}], client), ?ERR({ocsp_nonce, foo}, [{ocsp_nonce, 'foo'}], client), diff --git a/lib/ssl/test/ssl_dist_test_lib.erl b/lib/ssl/test/ssl_dist_test_lib.erl index 8740aa2a47..78f99477a8 100644 --- a/lib/ssl/test/ssl_dist_test_lib.erl +++ b/lib/ssl/test/ssl_dist_test_lib.erl @@ -94,13 +94,7 @@ stop_ssl_node(#node_handle{connection_handler = Handler, erlang:demonitor(Mon, [flush]), ct:pal("stop_ssl_node/1 ~s Warning ~p ~n", [Name,Error]) end, - case file:read_file(LogPath) of - {ok, Binary} -> - ct:pal("LogPath(~pB) = ~p~n~s", [filelib:file_size(LogPath), LogPath, - Binary]); - _ -> - ok - end, + ssl_test_lib:ct_pal_file(LogPath), ct:pal("DumpPath(~pB) = ~p~n", [filelib:file_size(DumpPath), DumpPath]). start_ssl_node(Name, Args) -> diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index ec90a616d0..eda86fe936 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -118,7 +118,6 @@ session_id/1, update_keys/2, sanity_check/2, - oscp_responder/6, supported_eccs/1, no_result/1, receive_tickets/1, @@ -222,7 +221,8 @@ portable_open_port/2, close_port/1, verify_early_data/1, - trace/0 + trace/0, + ct_pal_file/1 ]). %% Tracing -export([handle_trace/3]). @@ -925,15 +925,6 @@ init_openssl_server(Mode, ResponderPort, Options) when Mode == openssl_ocsp orel Pid ! {self(), {port, Port}}, openssl_server_loop(Pid, SslPort, Args). -oscp_responder(Port, Index, CACerts, Cert, Key, Starter) -> - Args = ["ocsp", "-index", Index, "-CA", CACerts, "-rsigner", Cert, - "-rkey", Key, "-port", erlang:integer_to_list(Port)], - Responder = portable_open_port("openssl", Args), - wait_for_openssl_server(Port, tls), - - openssl_server_loop(Starter, Responder, []). - - openssl_dtls_opt('dtlsv1.2') -> ["-dtls"]; openssl_dtls_opt(_Other) -> @@ -1464,7 +1455,9 @@ format_cert(#'OTPCertificate'{tbsCertificate = Cert} = OtpCert) -> {error, _} -> io_lib:format("~.3w:~s -> :~s", [Nr, format_subject(Subject), format_subject(Issuer)]) end - end. + end; +format_cert(Cert) -> + io_lib:format("Format failed for ~p", [Cert]). format_subject({rdnSequence, Seq}) -> format_subject(Seq); @@ -4265,3 +4258,13 @@ ktls_set_cipher(Socket, OS, TxRx, Seed) -> key = TLS_KEY, iv = <<TLS_SALT/binary, TLS_IV/binary>> }, inet_tls_dist:set_ktls_cipher(KtlsInfo, OS, CipherState, 0, TxRx). + +ct_pal_file(FilePath) -> + case file:read_file(FilePath) of + {ok, Binary} -> + ?CT_PAL("~s ~pB~n~s", + [FilePath, filelib:file_size(FilePath), Binary]); + _ -> + ?CT_PAL("Failed to log ~s", [FilePath]), + ok + end. diff --git a/lib/ssl/test/ssl_test_lib.hrl b/lib/ssl/test/ssl_test_lib.hrl index 947c765c2a..d6d928610e 100644 --- a/lib/ssl/test/ssl_test_lib.hrl +++ b/lib/ssl/test/ssl_test_lib.hrl @@ -8,6 +8,8 @@ ?SSL_TEST_LIB_FORMAT ++ F, ?SSL_TEST_LIB_ARGS ++ Args, [esc_chars]))). +-define(CT_PAL(F), + (ct:pal(?SSL_TEST_LIB_FORMAT ++ F, ?SSL_TEST_LIB_ARGS))). -define(CT_PAL(F, Args), (ct:pal(?SSL_TEST_LIB_FORMAT ++ F, ?SSL_TEST_LIB_ARGS ++ Args))). -define(CT_FAIL(F, Args), diff --git a/lib/ssl/test/ssl_trace_SUITE.erl b/lib/ssl/test/ssl_trace_SUITE.erl index 8c8dc240ce..c33b29e90c 100644 --- a/lib/ssl/test/ssl_trace_SUITE.erl +++ b/lib/ssl/test/ssl_trace_SUITE.erl @@ -138,7 +138,6 @@ tc_api_profile(Config) -> #{ call => [{" (server) -> ssl:handshake/2", ssl, handshake}, - {" (client) -> ssl_gen_statem:connect/8", ssl_gen_statem, connect}, {" (server) -> ssl_gen_statem:initial_hello/3", ssl_gen_statem, initial_hello}, {" (client) -> ssl_gen_statem:initial_hello/3", @@ -161,7 +160,8 @@ tc_api_profile(Config) -> "rle ('?') -> ssl:listen/2 (*server) Args", "rle ('?') -> ssl:connect/3 (*client) Args", "rle ('?') -> tls_sender:init/3 (*server)", - "rle ('?') -> tls_sender:init/3 (*client)"]}, + "rle ('?') -> tls_sender:init/3 (*client)", + "api (client) -> ssl_gen_statem:connect/8"]}, TracesAfterDisconnect = #{ call => @@ -197,8 +197,8 @@ tc_api_profile(Config) -> {ok, Delta} = ssl_trace:off(Off), [Server, Client] = ssl_connect(Config), UnhandledTraceCnt1 = - #{call => 0, processed => 0, exception_from => no_trace_received, - return_from => 0}, + #{call => 2, processed => 0, exception_from => no_trace_received, + return_from => 2}, check_trace_map(Ref, TracesAfterConnect, UnhandledTraceCnt1), ssl_test_lib:close(Server), ssl_test_lib:close(Client), @@ -370,7 +370,7 @@ receive_map(Ref, check_trace_map(Ref, ExpectedTraces) -> Received = receive_map(Ref), L = [check_key(Type, ExpectedTraces, maps:get(Type, Received)) || - Type <- maps:keys(Received)], + Type <- maps:keys(Received)], maps:from_list(L). check_trace_map(Ref, ExpectedTraces, ExpectedRemainders) -> |