summaryrefslogtreecommitdiff
path: root/src/ssl/s3_both.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl/s3_both.cc')
-rw-r--r--src/ssl/s3_both.cc70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/ssl/s3_both.cc b/src/ssl/s3_both.cc
index 27e9454f..842ec676 100644
--- a/src/ssl/s3_both.cc
+++ b/src/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 @@ void ssl3_next_message(SSL *ssl) {
}
}
+// 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