Tighten up SSLCipherPreferenceList

Some minor tweaks with no intended functional changes, in preparation
for using SSLCipherPreferenceList for TLS 1.3 ciphers as well.

Bug: 545123692
Change-Id: I1d7ad9942c2651feb02ebbd626a63a726a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/101568
Auto-Submit: Lily Chen <chlily@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index c8d9897..00d8cf4 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -146,9 +146,9 @@
       return false;
     }
   }
-  STACK_OF(SSL_CIPHER) *configured =
-      ssl->config->cipher_list ? ssl->config->cipher_list->ciphers.get()
-                               : ssl->ctx->cipher_list->ciphers.get();
+  const STACK_OF(SSL_CIPHER) *configured =
+      ssl->config->cipher_list ? ssl->config->cipher_list->ciphers()
+                               : ssl->ctx->cipher_list->ciphers();
   bssl::UniquePtr<STACK_OF(SSL_CIPHER)> unsupported(sk_SSL_CIPHER_new_null());
   if (!unsupported) {
     return false;
@@ -164,7 +164,7 @@
   if (sk_SSL_CIPHER_num(unsupported.get()) && !ssl->config->cipher_list) {
     ssl->config->cipher_list = bssl::MakeUnique<SSLCipherPreferenceList>();
     if (!ssl->config->cipher_list ||
-        !ssl->config->cipher_list->Init(*ssl->ctx->cipher_list)) {
+        !ssl->config->cipher_list->CopyFrom(*ssl->ctx->cipher_list)) {
       return false;
     }
   }
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 64ea061..0b4f1a4 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -160,11 +160,11 @@
                                        uint32_t mask_k, uint32_t mask_a) {
   SSLImpl *const ssl = hs->ssl;
   const STACK_OF(SSL_CIPHER) *prio, *allow;
-  // in_group_flags will either be NULL, or will point to an array of bytes
+  // in_group_flags will either be empty, or will contain an array of bytes
   // which indicate equal-preference groups in the `prio` stack. See the
   // comment about `in_group_flags` in the `SSLCipherPreferenceList`
   // struct.
-  const bool *in_group_flags;
+  Span<const bool> in_group_flags;
   // best_index contains the index of the best matching cipher suite found so
   // far, indexed into `allow`. If `best_index` is `SIZE_MAX`, no matching
   // cipher suite has been found yet.
@@ -174,18 +174,18 @@
       hs->config->cipher_list ? hs->config->cipher_list.get()
                               : ssl->ctx->cipher_list.get();
   if (ssl->options & SSL_OP_CIPHER_SERVER_PREFERENCE) {
-    prio = server_pref->ciphers.get();
-    in_group_flags = server_pref->in_group_flags;
+    prio = server_pref->ciphers();
+    in_group_flags = server_pref->in_group_flags();
     allow = client_pref;
   } else {
     prio = client_pref;
-    in_group_flags = nullptr;
-    allow = server_pref->ciphers.get();
+    in_group_flags = Span<const bool>();
+    allow = server_pref->ciphers();
   }
 
   for (size_t i = 0; i < sk_SSL_CIPHER_num(prio); i++) {
     const SSL_CIPHER *c = sk_SSL_CIPHER_value(prio, i);
-    const bool in_group = in_group_flags != nullptr && in_group_flags[i];
+    const bool in_group = !in_group_flags.empty() && in_group_flags[i];
 
     size_t cipher_index;
     if (  // Check if the cipher is supported for the current version.
diff --git a/ssl/internal.h b/ssl/internal.h
index b60f5a1..e3962b4 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -287,20 +287,35 @@
 //    A         E
 //    B -> D -> F
 //    C
-struct SSLCipherPreferenceList {
+class SSLCipherPreferenceList {
+ public:
   static constexpr bool kAllowUniquePtr = true;
 
   SSLCipherPreferenceList() = default;
-  ~SSLCipherPreferenceList();
+  ~SSLCipherPreferenceList() = default;
 
+  // Initializes a list with the specified ciphers and flags.
   bool Init(UniquePtr<STACK_OF(SSL_CIPHER)> ciphers,
-            Span<const bool> in_group_flags);
-  bool Init(const SSLCipherPreferenceList &);
+            Array<bool> in_group_flags);
 
+  // Makes `this` a deep copy of another (already initialized) instance.
+  bool CopyFrom(const SSLCipherPreferenceList &);
+
+  // Removes `cipher` from the preference list.
   void Remove(const SSL_CIPHER *cipher);
 
-  UniquePtr<STACK_OF(SSL_CIPHER)> ciphers;
-  bool *in_group_flags = nullptr;
+  size_t size() const { return sk_SSL_CIPHER_num(ciphers_.get()); }
+
+  const STACK_OF(SSL_CIPHER) *ciphers() const { return ciphers_.get(); }
+  STACK_OF(SSL_CIPHER) *ciphers() { return ciphers_.get(); }
+
+  Span<const bool> in_group_flags() const { return in_group_flags_; }
+
+ private:
+  // SSL_CIPHERs are maintained in a stack so they are easily accessible in the
+  // form required for `SSL{_CTX}_get_ciphers`.
+  UniquePtr<STACK_OF(SSL_CIPHER)> ciphers_;
+  Array<bool> in_group_flags_;
 };
 
 // AllCiphers returns an array of all supported ciphers, sorted by id.
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index e32bec5..4b7a78b 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -605,50 +605,44 @@
   *head = curr;
 }
 
-SSLCipherPreferenceList::~SSLCipherPreferenceList() {
-  OPENSSL_free(in_group_flags);
-}
-
-bool SSLCipherPreferenceList::Init(UniquePtr<STACK_OF(SSL_CIPHER)> ciphers_arg,
-                                   Span<const bool> in_group_flags_arg) {
-  if (sk_SSL_CIPHER_num(ciphers_arg.get()) != in_group_flags_arg.size()) {
+bool SSLCipherPreferenceList::Init(UniquePtr<STACK_OF(SSL_CIPHER)> ciphers,
+                                   Array<bool> in_group_flags) {
+  if (sk_SSL_CIPHER_num(ciphers.get()) != in_group_flags.size()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return false;
   }
 
-  Array<bool> copy;
-  if (!copy.CopyFrom(in_group_flags_arg)) {
-    return false;
-  }
-  ciphers = std::move(ciphers_arg);
-  size_t unused_len;
-  copy.Release(&in_group_flags, &unused_len);
+  ciphers_ = std::move(ciphers);
+  in_group_flags_ = std::move(in_group_flags);
   return true;
 }
 
-bool SSLCipherPreferenceList::Init(const SSLCipherPreferenceList &other) {
-  size_t size = sk_SSL_CIPHER_num(other.ciphers.get());
-  Span<const bool> other_flags(other.in_group_flags, size);
+bool SSLCipherPreferenceList::CopyFrom(const SSLCipherPreferenceList &other) {
   UniquePtr<STACK_OF(SSL_CIPHER)> other_ciphers(
-      sk_SSL_CIPHER_dup(other.ciphers.get()));
+      sk_SSL_CIPHER_dup(other.ciphers()));
   if (!other_ciphers) {
     return false;
   }
-  return Init(std::move(other_ciphers), other_flags);
+  Array<bool> other_flags;
+  if (!other_flags.CopyFrom(other.in_group_flags())) {
+    return false;
+  }
+  return Init(std::move(other_ciphers), std::move(other_flags));
 }
 
 void SSLCipherPreferenceList::Remove(const SSL_CIPHER *cipher) {
   size_t index;
-  if (!sk_SSL_CIPHER_find(ciphers.get(), &index, cipher)) {
+  if (!sk_SSL_CIPHER_find(ciphers_.get(), &index, cipher)) {
     return;
   }
-  if (!in_group_flags[index] /* last element of group */ && index > 0) {
-    in_group_flags[index - 1] = false;
+  if (!in_group_flags_[index] /* last element of group */ && index > 0) {
+    in_group_flags_[index - 1] = false;
   }
-  for (size_t i = index; i < sk_SSL_CIPHER_num(ciphers.get()) - 1; ++i) {
-    in_group_flags[i] = in_group_flags[i + 1];
+  for (size_t i = index; i < size() - 1; ++i) {
+    in_group_flags_[i] = in_group_flags_[i + 1];
   }
-  sk_SSL_CIPHER_delete(ciphers.get(), index);
+  sk_SSL_CIPHER_delete(ciphers_.get(), index);
+  in_group_flags_.Shrink(size());
 }
 
 bool ssl_cipher_is_deprecated(const SSL_CIPHER *cipher) {
@@ -1133,7 +1127,8 @@
 
   UniquePtr<SSLCipherPreferenceList> pref_list =
       MakeUnique<SSLCipherPreferenceList>();
-  if (!pref_list || !pref_list->Init(std::move(cipherstack), in_group_flags)) {
+  if (!pref_list ||
+      !pref_list->Init(std::move(cipherstack), std::move(in_group_flags))) {
     return false;
   }
 
@@ -1141,7 +1136,7 @@
 
   // Configuring an empty cipher list is an error but still updates the
   // output.
-  if (sk_SSL_CIPHER_num((*out_cipher_list)->ciphers.get()) == 0) {
+  if (sk_SSL_CIPHER_num((*out_cipher_list)->ciphers()) == 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CIPHER_MATCH);
     return false;
   }
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index 101ca7e..fd526a6 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -2146,15 +2146,15 @@
 int SSL_set_tmp_dh(SSL *ssl, const DH *dh) { return 1; }
 
 STACK_OF(SSL_CIPHER) *SSL_CTX_get_ciphers(const SSL_CTX *ctx) {
-  return FromOpaque(ctx)->cipher_list->ciphers.get();
+  return FromOpaque(ctx)->cipher_list->ciphers();
 }
 
 int SSL_CTX_cipher_in_group(const SSL_CTX *ctx, size_t i) {
   auto *ctx_impl = FromOpaque(ctx);
-  if (i >= sk_SSL_CIPHER_num(ctx_impl->cipher_list->ciphers.get())) {
+  if (i >= sk_SSL_CIPHER_num(ctx_impl->cipher_list->ciphers())) {
     return 0;
   }
-  return ctx_impl->cipher_list->in_group_flags[i];
+  return ctx_impl->cipher_list->in_group_flags()[i];
 }
 
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl) {
@@ -2168,8 +2168,8 @@
   }
 
   return ssl_impl->config->cipher_list
-             ? ssl_impl->config->cipher_list->ciphers.get()
-             : ssl_impl->ctx->cipher_list->ciphers.get();
+             ? ssl_impl->config->cipher_list->ciphers()
+             : ssl_impl->ctx->cipher_list->ciphers();
 }
 
 const char *SSL_get_cipher_list(const SSL *ssl, int n) {