From 22ce783fe400ee5e3996802a634c8d5868edc82f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Dimitrov?= Date: Mon, 11 Mar 2019 16:40:47 +0100 Subject: ssl: Improve verification of received Certificate Validate peer certificate against supported signature algorithms. Send 'Hanshake Failure' Alert if signature algorithm is not supported by the server. Change-Id: Iad428aad337f0f9764d23404c203f966664c4555 --- lib/ssl/src/ssl_cipher.erl | 14 ++++++++++- lib/ssl/src/tls_handshake_1_3.erl | 53 ++++++++++++++++++++++++++++----------- 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 6e751f9ceb..fe8736d2df 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -45,7 +45,7 @@ random_bytes/1, calc_mac_hash/4, calc_mac_hash/6, is_stream_ciphersuite/1, signature_scheme/1, scheme_to_components/1, hash_size/1, effective_key_bits/1, - key_material/1]). + key_material/1, signature_algorithm_to_scheme/1]). %% RFC 8446 TLS 1.3 -export([generate_client_shares/1, generate_server_share/1, add_zero_padding/2]). @@ -900,6 +900,18 @@ scheme_to_components(rsa_pss_pss_sha512) -> {sha512, rsa_pss_pss, undefined}; scheme_to_components(rsa_pkcs1_sha1) -> {sha1, rsa_pkcs1, undefined}; scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}. + +%% TODO: Add support for EC and RSA-SSA signatures +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha256WithRSAEncryption}) -> + rsa_pkcs1_sha256; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha384WithRSAEncryption}) -> + rsa_pkcs1_sha384; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha512WithRSAEncryption}) -> + rsa_pkcs1_sha512. + + %% RFC 5246: 6.2.3.2. CBC Block Cipher %% %% Implementation note: Canvel et al. [CBCTIME] have demonstrated a diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 9c6c9190a1..1e8b046c1e 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -503,7 +503,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers, {Ref, no_suitable_cipher} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_signature_algorithm); + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); {Ref, {insufficient_security, no_suitable_public_key}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) end. @@ -589,6 +589,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason), State}; {Ref, {{internal_error, Reason}, State}} -> {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; + {Ref, {{handshake_failure, Reason}, State}} -> + {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; {#alert{} = Alert, State} -> {Alert, State} end. @@ -723,7 +725,9 @@ process_client_certificate(#certificate_1_3{ State = ssl_record:step_encryption_state(State1), {error, {certificate_required, State}}; process_client_certificate(#certificate_1_3{certificate_list = Certs0}, - #state{ssl_options = SslOptions, + #state{ssl_options = + #ssl_options{signature_algs = SignAlgs, + signature_algs_cert = SignAlgsCert} = SslOptions, static_env = #static_env{ role = Role, @@ -735,23 +739,44 @@ process_client_certificate(#certificate_1_3{certificate_list = Certs0}, %% Remove extensions from list of certificates! Certs = convert_certificate_chain(Certs0), - - case validate_certificate_chain(Certs, CertDbHandle, CertDbRef, - SslOptions, CRLDbHandle, Role, Host) of - {ok, {PeerCert, PublicKeyInfo}} -> - State = store_peer_cert(State0, PeerCert, PublicKeyInfo), - {ok, {State, wait_cv}}; - {error, Reason} -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), - {error, {Reason, State}}; - #alert{} = Alert -> + case is_supported_signature_algorithm(Certs, SignAlgs, SignAlgsCert) of + true -> + case validate_certificate_chain(Certs, CertDbHandle, CertDbRef, + SslOptions, CRLDbHandle, Role, Host) of + {ok, {PeerCert, PublicKeyInfo}} -> + State = store_peer_cert(State0, PeerCert, PublicKeyInfo), + {ok, {State, wait_cv}}; + {error, Reason} -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {error, {Reason, State}}; + #alert{} = Alert -> + State1 = calculate_traffic_secrets(State0), + State = ssl_record:step_encryption_state(State1), + {Alert, State} + end; + false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), - {Alert, State} + {error, {{handshake_failure, + "Client certificate uses unsupported signature algorithm"}, State}} end. +%% TODO: check whole chain! +is_supported_signature_algorithm(Certs, SignAlgs, undefined) -> + is_supported_signature_algorithm(Certs, SignAlgs); +is_supported_signature_algorithm(Certs, _, SignAlgsCert) -> + is_supported_signature_algorithm(Certs, SignAlgsCert). +%% +is_supported_signature_algorithm([BinCert|_], SignAlgs0) -> + #'OTPCertificate'{signatureAlgorithm = SignAlg} = + public_key:pkix_decode_cert(BinCert, otp), + SignAlgs = filter_tls13_algs(SignAlgs0), + Scheme = ssl_cipher:signature_algorithm_to_scheme(SignAlg), + lists:member(Scheme, SignAlgs). + + validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), [PeerCert | ChainCerts ] = Certs, -- cgit v1.2.3