Factor out TLS cipher selection to ssl_choose_tls_cipher. This is factored out since ESNI will need to do its own cipher selection. Bug: 275 Change-Id: Id87fd91272fbcd9098b3f2a9caa78a2129b154b5 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/36544 Commit-Queue: Steven Valdez <svaldez@google.com> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/internal.h b/ssl/internal.h index f03271e..bc17af9 100644 --- a/ssl/internal.h +++ b/ssl/internal.h
@@ -560,6 +560,12 @@ // it returns zero. size_t ssl_cipher_get_record_split_len(const SSL_CIPHER *cipher); +// ssl_choose_tls13_cipher returns an |SSL_CIPHER| corresponding with the best +// available from |cipher_suites| compatible with |version| and |group_id|. It +// returns NULL if there isn't a compatible cipher. +const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version, + uint16_t group_id); + // Transcript layer.
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc index 27e9454..842ec67 100644 --- a/ssl/s3_both.cc +++ b/ssl/s3_both.cc
@@ -116,6 +116,8 @@ #include <limits.h> #include <string.h> +#include <tuple> + #include <openssl/buf.h> #include <openssl/bytestring.h> #include <openssl/err.h> @@ -652,4 +654,72 @@ } } +// CipherScorer produces a "score" for each possible cipher suite offered by +// the client. +class CipherScorer { + public: + CipherScorer(uint16_t group_id) + : aes_is_fine_(EVP_has_aes_hardware()), + security_128_is_fine_(group_id != SSL_CURVE_CECPQ2 && + group_id != SSL_CURVE_CECPQ2b) {} + + typedef std::tuple<bool, bool, bool> Score; + + // MinScore returns a |Score| that will compare less than the score of all + // cipher suites. + Score MinScore() const { + return Score(false, false, false); + } + + Score Evaluate(const SSL_CIPHER *a) const { + return Score( + // Something is always preferable to nothing. + true, + // Either 128-bit is fine, or 256-bit is preferred. + security_128_is_fine_ || a->algorithm_enc != SSL_AES128GCM, + // Either AES is fine, or else ChaCha20 is preferred. + aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305); + } + + private: + const bool aes_is_fine_; + const bool security_128_is_fine_; +}; + +const SSL_CIPHER *ssl_choose_tls13_cipher(CBS cipher_suites, uint16_t version, + uint16_t group_id) { + if (CBS_len(&cipher_suites) % 2 != 0) { + return nullptr; + } + + const SSL_CIPHER *best = nullptr; + CipherScorer scorer(group_id); + CipherScorer::Score best_score = scorer.MinScore(); + + while (CBS_len(&cipher_suites) > 0) { + uint16_t cipher_suite; + if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { + return nullptr; + } + + // Limit to TLS 1.3 ciphers we know about. + const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite); + if (candidate == nullptr || + SSL_CIPHER_get_min_version(candidate) > version || + SSL_CIPHER_get_max_version(candidate) < version) { + continue; + } + + const CipherScorer::Score candidate_score = scorer.Evaluate(candidate); + // |candidate_score| must be larger to displace the current choice. That way + // the client's order controls between ciphers with an equal score. + if (candidate_score > best_score) { + best = candidate; + best_score = candidate_score; + } + } + + return best; +} + BSSL_NAMESPACE_END
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc index 8fb0d5c..4ebea5b 100644 --- a/ssl/tls13_server.cc +++ b/ssl/tls13_server.cc
@@ -103,78 +103,15 @@ return 1; } -// CipherScorer produces a "score" for each possible cipher suite offered by -// the client. -class CipherScorer { - public: - CipherScorer(uint16_t group_id) - : aes_is_fine_(EVP_has_aes_hardware()), - security_128_is_fine_(group_id != SSL_CURVE_CECPQ2 && - group_id != SSL_CURVE_CECPQ2b) {} - - typedef std::tuple<bool, bool, bool> Score; - - // MinScore returns a |Score| that will compare less than the score of all - // cipher suites. - Score MinScore() const { - return Score(false, false, false); - } - - Score Evaluate(const SSL_CIPHER *a) const { - return Score( - // Something is always preferable to nothing. - true, - // Either 128-bit is fine, or 256-bit is preferred. - security_128_is_fine_ || a->algorithm_enc != SSL_AES128GCM, - // Either AES is fine, or else ChaCha20 is preferred. - aes_is_fine_ || a->algorithm_enc == SSL_CHACHA20POLY1305); - } - - private: - const bool aes_is_fine_; - const bool security_128_is_fine_; -}; - static const SSL_CIPHER *choose_tls13_cipher( const SSL *ssl, const SSL_CLIENT_HELLO *client_hello, uint16_t group_id) { - if (client_hello->cipher_suites_len % 2 != 0) { - return nullptr; - } - CBS cipher_suites; CBS_init(&cipher_suites, client_hello->cipher_suites, client_hello->cipher_suites_len); const uint16_t version = ssl_protocol_version(ssl); - const SSL_CIPHER *best = nullptr; - CipherScorer scorer(group_id); - CipherScorer::Score best_score = scorer.MinScore(); - - while (CBS_len(&cipher_suites) > 0) { - uint16_t cipher_suite; - if (!CBS_get_u16(&cipher_suites, &cipher_suite)) { - return nullptr; - } - - // Limit to TLS 1.3 ciphers we know about. - const SSL_CIPHER *candidate = SSL_get_cipher_by_value(cipher_suite); - if (candidate == nullptr || - SSL_CIPHER_get_min_version(candidate) > version || - SSL_CIPHER_get_max_version(candidate) < version) { - continue; - } - - const CipherScorer::Score candidate_score = scorer.Evaluate(candidate); - // |candidate_score| must be larger to displace the current choice. That way - // the client's order controls between ciphers with an equal score. - if (candidate_score > best_score) { - best = candidate; - best_score = candidate_score; - } - } - - return best; + return ssl_choose_tls13_cipher(cipher_suites, version, group_id); } static bool add_new_session_tickets(SSL_HANDSHAKE *hs, bool *out_sent_tickets) {