Make SSL3_BUFFER a proper C++ class.

As with SSLTranscript before, we temporarily need some nastiness in
SSL3_STATE, but this is in preparation of giving SSL3_STATE a
constructor and destructor.

Change-Id: Ifc0ce34fdcd8691d521d8ea03ff5e83dad43b4a3
Reviewed-on: https://boringssl-review.googlesource.com/21944
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/d1_pkt.cc b/ssl/d1_pkt.cc
index c6be93d..d29a5c2 100644
--- a/ssl/d1_pkt.cc
+++ b/ssl/d1_pkt.cc
@@ -187,8 +187,8 @@
   return ssl_open_record_success;
 }
 
-int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake,
-                         const uint8_t *buf, int len) {
+int dtls1_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
+                         int len) {
   assert(!SSL_in_init(ssl));
   *out_needs_handshake = false;
 
@@ -211,7 +211,7 @@
     return 0;
   }
 
-  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len,
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, in, (size_t)len,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
     return ret;
@@ -219,29 +219,29 @@
   return len;
 }
 
-int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
+int dtls1_write_record(SSL *ssl, int type, const uint8_t *in, size_t len,
                        enum dtls1_use_epoch_t use_epoch) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
   // There should never be a pending write buffer in DTLS. One can't write half
   // a datagram, so the write buffer is always dropped in
   // |ssl_write_buffer_flush|.
-  assert(!ssl_write_buffer_is_pending(ssl));
+  assert(buf->empty());
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  size_t max_out = len + SSL_max_seal_overhead(ssl);
-  uint8_t *out;
   size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out) ||
-      !dtls_seal_record(ssl, out, &ciphertext_len, max_out, type, buf, len,
-                        use_epoch)) {
-    ssl_write_buffer_clear(ssl);
+  if (!buf->EnsureCap(ssl_seal_align_prefix_len(ssl),
+                      len + SSL_max_seal_overhead(ssl)) ||
+      !dtls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                        buf->remaining().size(), type, in, len, use_epoch)) {
+    buf->Clear();
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   int ret = ssl_write_buffer_flush(ssl);
   if (ret <= 0) {
diff --git a/ssl/handshake.cc b/ssl/handshake.cc
index 8531ca4..3cb9a9c 100644
--- a/ssl/handshake.cc
+++ b/ssl/handshake.cc
@@ -505,10 +505,10 @@
         ssl_open_record_t ret;
         if (hs->wait == ssl_hs_read_change_cipher_spec) {
           ret = ssl_open_change_cipher_spec(ssl, &consumed, &alert,
-                                            ssl_read_buffer(ssl));
+                                            ssl->s3->read_buffer.span());
         } else {
-          ret =
-              ssl_open_handshake(ssl, &consumed, &alert, ssl_read_buffer(ssl));
+          ret = ssl_open_handshake(ssl, &consumed, &alert,
+                                   ssl->s3->read_buffer.span());
         }
         if (ret == ssl_open_record_error &&
             hs->wait == ssl_hs_read_server_hello) {
@@ -533,7 +533,7 @@
         if (retry) {
           continue;
         }
-        ssl_read_buffer_discard(ssl);
+        ssl->s3->read_buffer.DiscardConsumed();
         break;
       }
 
diff --git a/ssl/internal.h b/ssl/internal.h
index 174445a..e502ec0 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1036,8 +1036,57 @@
 
 // Transport buffers.
 
-// ssl_read_buffer returns the current read buffer.
-Span<uint8_t> ssl_read_buffer(SSL *ssl);
+class SSLBuffer {
+ public:
+  SSLBuffer() {}
+  ~SSLBuffer() { Clear(); }
+
+  SSLBuffer(const SSLBuffer &) = delete;
+  SSLBuffer &operator=(const SSLBuffer &) = delete;
+
+  uint8_t *data() { return buf_ + offset_; }
+  size_t size() const { return size_; }
+  bool empty() const { return size_ == 0; }
+  size_t cap() const { return cap_; }
+
+  Span<uint8_t> span() { return MakeSpan(data(), size()); }
+
+  Span<uint8_t> remaining() {
+    return MakeSpan(data() + size(), cap() - size());
+  }
+
+  // Clear releases the buffer.
+  void Clear();
+
+  // EnsureCap ensures the buffer has capacity at least |new_cap|, aligned such
+  // that data written after |header_len| is aligned to a
+  // |SSL3_ALIGN_PAYLOAD|-byte boundary. It returns true on success and false
+  // on error.
+  bool EnsureCap(size_t header_len, size_t new_cap);
+
+  // DidWrite extends the buffer by |len|. The caller must have filled in to
+  // this point.
+  void DidWrite(size_t len);
+
+  // Consume consumes |len| bytes from the front of the buffer.  The memory
+  // consumed will remain valid until the next call to |DiscardConsumed| or
+  // |Clear|.
+  void Consume(size_t len);
+
+  // DiscardConsumed discards the consumed bytes from the buffer. If the buffer
+  // is now empty, it releases memory used by it.
+  void DiscardConsumed();
+
+ private:
+  // buf_ is the memory allocated for this buffer.
+  uint8_t *buf_ = nullptr;
+  // offset_ is the offset into |buf_| which the buffer contents start at.
+  uint16_t offset_ = 0;
+  // size_ is the size of the buffer contents from |buf_| + |offset_|.
+  uint16_t size_ = 0;
+  // cap_ is how much memory beyond |buf_| + |offset_| is available.
+  uint16_t cap_ = 0;
+};
 
 // ssl_read_buffer_extend_to extends the read buffer to the desired length. For
 // TLS, it reads to the end of the buffer until the buffer is |len| bytes
@@ -1048,49 +1097,18 @@
 // non-empty.
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len);
 
-// ssl_read_buffer_consume consumes |len| bytes from the read buffer. It
-// advances the data pointer and decrements the length. The memory consumed will
-// remain valid until the next call to |ssl_read_buffer_extend| or it is
-// discarded with |ssl_read_buffer_discard|.
-void ssl_read_buffer_consume(SSL *ssl, size_t len);
-
-// ssl_read_buffer_discard discards the consumed bytes from the read buffer. If
-// the buffer is now empty, it releases memory used by it.
-void ssl_read_buffer_discard(SSL *ssl);
-
-// ssl_read_buffer_clear releases all memory associated with the read buffer and
-// zero-initializes it.
-void ssl_read_buffer_clear(SSL *ssl);
-
-// ssl_handle_open_record handles the result of passing |ssl_read_buffer| to a
-// record-processing function. If |ret| is a success or if the caller should
-// retry, it returns one and sets |*out_retry|. Otherwise, it returns <= 0.
+// ssl_handle_open_record handles the result of passing |ssl->s3->read_buffer|
+// to a record-processing function. If |ret| is a success or if the caller
+// should retry, it returns one and sets |*out_retry|. Otherwise, it returns <=
+// 0.
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert);
 
-// ssl_write_buffer_is_pending returns one if the write buffer has pending data
-// and zero if is empty.
-int ssl_write_buffer_is_pending(const SSL *ssl);
-
-// ssl_write_buffer_init initializes the write buffer. On success, it sets
-// |*out_ptr| to the start of the write buffer with space for up to |max_len|
-// bytes. It returns one on success and zero on failure. Call
-// |ssl_write_buffer_set_len| to complete initialization.
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len);
-
-// ssl_write_buffer_set_len is called after |ssl_write_buffer_init| to complete
-// initialization after |len| bytes are written to the buffer.
-void ssl_write_buffer_set_len(SSL *ssl, size_t len);
-
 // ssl_write_buffer_flush flushes the write buffer to the transport. It returns
 // one on success and <= 0 on error. For DTLS, whether or not the write
 // succeeds, the write buffer will be cleared.
 int ssl_write_buffer_flush(SSL *ssl);
 
-// ssl_write_buffer_clear releases all memory associated with the write buffer
-// and zero-initializes it.
-void ssl_write_buffer_clear(SSL *ssl);
-
 
 // Certificate functions.
 
@@ -2134,17 +2152,6 @@
   bool ed25519_enabled:1;
 };
 
-struct SSL3_BUFFER {
-  // buf is the memory allocated for this buffer.
-  uint8_t *buf;
-  // offset is the offset into |buf| which the buffer contents start at.
-  uint16_t offset;
-  // len is the length of the buffer contents from |buf| + |offset|.
-  uint16_t len;
-  // cap is how much memory beyond |buf| + |offset| is available.
-  uint16_t cap;
-};
-
 // An ssl_shutdown_t describes the shutdown state of one end of the connection,
 // whether it is alive or has been shutdown via close_notify or fatal alert.
 enum ssl_shutdown_t {
@@ -2161,9 +2168,9 @@
   uint8_t client_random[SSL3_RANDOM_SIZE];
 
   // read_buffer holds data from the transport to be processed.
-  SSL3_BUFFER read_buffer;
+  SSLBuffer read_buffer;
   // write_buffer holds data to be written to the transport.
-  SSL3_BUFFER write_buffer;
+  SSLBuffer write_buffer;
 
   // pending_app_data is the unconsumed application data. It points into
   // |read_buffer|.
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index 1459085..8c41179 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -246,7 +246,7 @@
 
   // If there is pending data in the write buffer, it must be flushed out before
   // any new data in pending_flight.
-  if (ssl_write_buffer_is_pending(ssl)) {
+  if (!ssl->s3->write_buffer.empty()) {
     int ret = ssl_write_buffer_flush(ssl);
     if (ret <= 0) {
       ssl->rwstate = SSL_WRITING;
@@ -303,7 +303,7 @@
     return ssl_open_record_partial;
   }
 
-  CBS v2_client_hello = CBS(ssl_read_buffer(ssl).subspan(2, msg_length));
+  CBS v2_client_hello = CBS(ssl->s3->read_buffer.span().subspan(2, msg_length));
   // The V2ClientHello without the length is incorporated into the handshake
   // hash. This is only ever called at the start of the handshake, so hs is
   // guaranteed to be non-NULL.
diff --git a/ssl/s3_lib.cc b/ssl/s3_lib.cc
index f3f99fa..d7af4f4 100644
--- a/ssl/s3_lib.cc
+++ b/ssl/s3_lib.cc
@@ -178,6 +178,8 @@
     return false;
   }
   OPENSSL_memset(s3, 0, sizeof *s3);
+  new (&s3->read_buffer) SSLBuffer();
+  new (&s3->write_buffer) SSLBuffer();
 
   s3->hs = ssl_handshake_new(ssl);
   if (s3->hs == NULL) {
@@ -203,9 +205,8 @@
     return;
   }
 
-  ssl_read_buffer_clear(ssl);
-  ssl_write_buffer_clear(ssl);
-
+  ssl->s3->read_buffer.~SSLBuffer();
+  ssl->s3->write_buffer.~SSLBuffer();
   ERR_SAVE_STATE_free(ssl->s3->read_error);
   SSL_SESSION_free(ssl->s3->established_session);
   ssl_handshake_free(ssl->s3->hs);
diff --git a/ssl/s3_pkt.cc b/ssl/s3_pkt.cc
index 7966e6f..ad4f82a 100644
--- a/ssl/s3_pkt.cc
+++ b/ssl/s3_pkt.cc
@@ -124,9 +124,9 @@
 
 namespace bssl {
 
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len);
 
-int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *buf,
+int ssl3_write_app_data(SSL *ssl, bool *out_needs_handshake, const uint8_t *in,
                         int len) {
   assert(ssl_can_write(ssl));
   assert(!ssl->s3->aead_write_ctx->is_null_cipher());
@@ -180,7 +180,7 @@
       nw = n;
     }
 
-    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
+    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &in[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
       return ret;
@@ -199,11 +199,11 @@
   }
 }
 
-static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *buf,
+static int ssl3_write_pending(SSL *ssl, int type, const uint8_t *in,
                               unsigned int len) {
   if (ssl->s3->wpend_tot > (int)len ||
       (!(ssl->mode & SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER) &&
-       ssl->s3->wpend_buf != buf) ||
+       ssl->s3->wpend_buf != in) ||
       ssl->s3->wpend_type != type) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_WRITE_RETRY);
     return -1;
@@ -218,13 +218,14 @@
 }
 
 // do_ssl3_write writes an SSL record of the given type.
-static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len) {
+static int do_ssl3_write(SSL *ssl, int type, const uint8_t *in, unsigned len) {
   // If there is still data from the previous record, flush it.
   if (ssl->s3->wpend_pending) {
-    return ssl3_write_pending(ssl, type, buf, len);
+    return ssl3_write_pending(ssl, type, in, len);
   }
 
-  if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (len > SSL3_RT_MAX_PLAIN_LENGTH || buf->size() > 0) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -246,9 +247,7 @@
   }
   max_out += flight_len;
 
-  uint8_t *out;
-  size_t ciphertext_len;
-  if (!ssl_write_buffer_init(ssl, &out, max_out)) {
+  if (!buf->EnsureCap(flight_len + ssl_seal_align_prefix_len(ssl), max_out)) {
     return -1;
   }
 
@@ -258,18 +257,21 @@
   // order.
   if (ssl->s3->pending_flight != NULL) {
     OPENSSL_memcpy(
-        out, ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
+        buf->remaining().data(),
+        ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
         flight_len);
     BUF_MEM_free(ssl->s3->pending_flight);
     ssl->s3->pending_flight = NULL;
     ssl->s3->pending_flight_offset = 0;
+    buf->DidWrite(flight_len);
   }
 
-  if (!tls_seal_record(ssl, out + flight_len, &ciphertext_len,
-                       max_out - flight_len, type, buf, len)) {
+  size_t ciphertext_len;
+  if (!tls_seal_record(ssl, buf->remaining().data(), &ciphertext_len,
+                       buf->remaining().size(), type, in, len)) {
     return -1;
   }
-  ssl_write_buffer_set_len(ssl, flight_len + ciphertext_len);
+  buf->DidWrite(ciphertext_len);
 
   // Now that we've made progress on the connection, uncork KeyUpdate
   // acknowledgments.
@@ -278,13 +280,13 @@
   // Memorize arguments so that ssl3_write_pending can detect bad write retries
   // later.
   ssl->s3->wpend_tot = len;
-  ssl->s3->wpend_buf = buf;
+  ssl->s3->wpend_buf = in;
   ssl->s3->wpend_type = type;
   ssl->s3->wpend_ret = len;
   ssl->s3->wpend_pending = true;
 
   // We now just need to write the buffer.
-  return ssl3_write_pending(ssl, type, buf, len);
+  return ssl3_write_pending(ssl, type, in, len);
 }
 
 ssl_open_record_t ssl3_open_app_data(SSL *ssl, Span<uint8_t> *out,
@@ -408,7 +410,7 @@
   ssl->s3->alert_dispatch = 1;
   ssl->s3->send_alert[0] = level;
   ssl->s3->send_alert[1] = desc;
-  if (!ssl_write_buffer_is_pending(ssl)) {
+  if (ssl->s3->write_buffer.empty()) {
     // Nothing is being written out, so the alert may be dispatched
     // immediately.
     return ssl->method->dispatch_alert(ssl);
diff --git a/ssl/ssl_buffer.cc b/ssl/ssl_buffer.cc
index 412df73..a942054 100644
--- a/ssl/ssl_buffer.cc
+++ b/ssl/ssl_buffer.cc
@@ -36,17 +36,22 @@
 static_assert((SSL3_ALIGN_PAYLOAD & (SSL3_ALIGN_PAYLOAD - 1)) == 0,
               "SSL3_ALIGN_PAYLOAD must be a power of 2");
 
-// ensure_buffer ensures |buf| has capacity at least |cap|, aligned such that
-// data written after |header_len| is aligned to a |SSL3_ALIGN_PAYLOAD|-byte
-// boundary. It returns one on success and zero on error.
-static int ensure_buffer(SSL3_BUFFER *buf, size_t header_len, size_t cap) {
-  if (cap > 0xffff) {
+void SSLBuffer::Clear() {
+  free(buf_);  // Allocated with malloc().
+  buf_ = nullptr;
+  offset_ = 0;
+  size_ = 0;
+  cap_ = 0;
+}
+
+bool SSLBuffer::EnsureCap(size_t header_len, size_t new_cap) {
+  if (new_cap > 0xffff) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
-  if (buf->cap >= cap) {
-    return 1;
+  if (cap_ >= new_cap) {
+    return true;
   }
 
   // Add up to |SSL3_ALIGN_PAYLOAD| - 1 bytes of slack for alignment.
@@ -54,88 +59,88 @@
   // Since this buffer gets allocated quite frequently and doesn't contain any
   // sensitive data, we allocate with malloc rather than |OPENSSL_malloc| and
   // avoid zeroing on free.
-  uint8_t *new_buf = (uint8_t *)malloc(cap + SSL3_ALIGN_PAYLOAD - 1);
+  uint8_t *new_buf = (uint8_t *)malloc(new_cap + SSL3_ALIGN_PAYLOAD - 1);
   if (new_buf == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
+    return false;
   }
 
   // Offset the buffer such that the record body is aligned.
   size_t new_offset =
       (0 - header_len - (uintptr_t)new_buf) & (SSL3_ALIGN_PAYLOAD - 1);
 
-  if (buf->buf != NULL) {
-    OPENSSL_memcpy(new_buf + new_offset, buf->buf + buf->offset, buf->len);
-    free(buf->buf);  // Allocated with malloc().
+  if (buf_ != NULL) {
+    OPENSSL_memcpy(new_buf + new_offset, buf_ + offset_, size_);
+    free(buf_);  // Allocated with malloc().
   }
 
-  buf->buf = new_buf;
-  buf->offset = new_offset;
-  buf->cap = cap;
-  return 1;
+  buf_ = new_buf;
+  offset_ = new_offset;
+  cap_ = new_cap;
+  return true;
 }
 
-static void consume_buffer(SSL3_BUFFER *buf, size_t len) {
-  if (len > buf->len) {
+void SSLBuffer::DidWrite(size_t new_size) {
+  if (new_size > cap() - size()) {
     abort();
   }
-  buf->offset += (uint16_t)len;
-  buf->len -= (uint16_t)len;
-  buf->cap -= (uint16_t)len;
+  size_ += new_size;
 }
 
-static void clear_buffer(SSL3_BUFFER *buf) {
-  free(buf->buf);  // Allocated with malloc().
-  OPENSSL_memset(buf, 0, sizeof(SSL3_BUFFER));
+void SSLBuffer::Consume(size_t len) {
+  if (len > size_) {
+    abort();
+  }
+  offset_ += (uint16_t)len;
+  size_ -= (uint16_t)len;
+  cap_ -= (uint16_t)len;
 }
 
-Span<uint8_t> ssl_read_buffer(SSL *ssl) {
-  return MakeSpan(ssl->s3->read_buffer.buf + ssl->s3->read_buffer.offset,
-                  ssl->s3->read_buffer.len);
+void SSLBuffer::DiscardConsumed() {
+  if (size_ == 0) {
+    Clear();
+  }
 }
 
 static int dtls_read_buffer_next_packet(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (buf->len > 0) {
+  if (!buf->empty()) {
     // It is an error to call |dtls_read_buffer_extend| when the read buffer is
     // not empty.
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
 
-  // Read a single packet from |ssl->rbio|. |buf->cap| must fit in an int.
-  int ret = BIO_read(ssl->rbio, buf->buf + buf->offset, (int)buf->cap);
+  // Read a single packet from |ssl->rbio|. |buf->cap()| must fit in an int.
+  int ret = BIO_read(ssl->rbio, buf->data(), static_cast<int>(buf->cap()));
   if (ret <= 0) {
     ssl->rwstate = SSL_READING;
     return ret;
   }
-  // |BIO_read| was bound by |buf->cap|, so this cannot overflow.
-  buf->len = (uint16_t)ret;
+  buf->DidWrite(static_cast<size_t>(ret));
   return 1;
 }
 
 static int tls_read_buffer_extend_to(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
+  SSLBuffer *buf = &ssl->s3->read_buffer;
 
-  if (len > buf->cap) {
+  if (len > buf->cap()) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return -1;
   }
 
   // Read until the target length is reached.
-  while (buf->len < len) {
+  while (buf->size() < len) {
     // The amount of data to read is bounded by |buf->cap|, which must fit in an
     // int.
-    int ret = BIO_read(ssl->rbio, buf->buf + buf->offset + buf->len,
-                       (int)(len - buf->len));
+    int ret = BIO_read(ssl->rbio, buf->data() + buf->size(),
+                       static_cast<int>(len - buf->size()));
     if (ret <= 0) {
       ssl->rwstate = SSL_READING;
       return ret;
     }
-    // |BIO_read| was bound by |buf->cap - buf->len|, so this cannot
-    // overflow.
-    buf->len += (uint16_t)ret;
+    buf->DidWrite(static_cast<size_t>(ret));
   }
 
   return 1;
@@ -143,7 +148,7 @@
 
 int ssl_read_buffer_extend_to(SSL *ssl, size_t len) {
   // |ssl_read_buffer_extend_to| implicitly discards any consumed data.
-  ssl_read_buffer_discard(ssl);
+  ssl->s3->read_buffer.DiscardConsumed();
 
   if (SSL_is_dtls(ssl)) {
     static_assert(
@@ -154,7 +159,7 @@
     len = DTLS1_RT_HEADER_LENGTH + SSL3_RT_MAX_ENCRYPTED_LENGTH;
   }
 
-  if (!ensure_buffer(&ssl->s3->read_buffer, ssl_record_prefix_len(ssl), len)) {
+  if (!ssl->s3->read_buffer.EnsureCap(ssl_record_prefix_len(ssl), len)) {
     return -1;
   }
 
@@ -174,43 +179,20 @@
   if (ret <= 0) {
     // If the buffer was empty originally and remained empty after attempting to
     // extend it, release the buffer until the next attempt.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }
 
-void ssl_read_buffer_consume(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->read_buffer;
-
-  consume_buffer(buf, len);
-
-  // The TLS stack never reads beyond the current record, so there will never be
-  // unconsumed data. If read-ahead is ever reimplemented,
-  // |ssl_read_buffer_discard| will require a |memcpy| to shift the excess back
-  // to the front of the buffer, to ensure there is enough space for the next
-  // record.
-  assert(SSL_is_dtls(ssl) || len == 0 || buf->len == 0);
-}
-
-void ssl_read_buffer_discard(SSL *ssl) {
-  if (ssl->s3->read_buffer.len == 0) {
-    ssl_read_buffer_clear(ssl);
-  }
-}
-
-void ssl_read_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->read_buffer);
-}
-
 int ssl_handle_open_record(SSL *ssl, bool *out_retry, ssl_open_record_t ret,
                            size_t consumed, uint8_t alert) {
   *out_retry = false;
   if (ret != ssl_open_record_partial) {
-    ssl_read_buffer_consume(ssl, consumed);
+    ssl->s3->read_buffer.Consume(consumed);
   }
   if (ret != ssl_open_record_success) {
     // Nothing was returned to the caller, so discard anything marked consumed.
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   switch (ret) {
     case ssl_open_record_success:
@@ -243,10 +225,6 @@
 }
 
 
-int ssl_write_buffer_is_pending(const SSL *ssl) {
-  return ssl->s3->write_buffer.len > 0;
-}
-
 static_assert(SSL3_RT_HEADER_LENGTH * 2 +
                       SSL3_RT_SEND_MAX_ENCRYPTED_OVERHEAD * 2 +
                       SSL3_RT_MAX_PLAIN_LENGTH <=
@@ -258,61 +236,37 @@
                   0xffff,
               "maximum DTLS write buffer is too large");
 
-int ssl_write_buffer_init(SSL *ssl, uint8_t **out_ptr, size_t max_len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (buf->buf != NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  if (!ensure_buffer(buf, ssl_seal_align_prefix_len(ssl), max_len)) {
-    return 0;
-  }
-  *out_ptr = buf->buf + buf->offset;
-  return 1;
-}
-
-void ssl_write_buffer_set_len(SSL *ssl, size_t len) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-
-  if (len > buf->cap) {
-    abort();
-  }
-  buf->len = len;
-}
-
 static int tls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
+  SSLBuffer *buf = &ssl->s3->write_buffer;
 
-  while (buf->len > 0) {
-    int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  while (!buf->empty()) {
+    int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
     if (ret <= 0) {
       ssl->rwstate = SSL_WRITING;
       return ret;
     }
-    consume_buffer(buf, (size_t)ret);
+    buf->Consume(static_cast<size_t>(ret));
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
 static int dtls_write_buffer_flush(SSL *ssl) {
-  SSL3_BUFFER *buf = &ssl->s3->write_buffer;
-  if (buf->len == 0) {
+  SSLBuffer *buf = &ssl->s3->write_buffer;
+  if (buf->empty()) {
     return 1;
   }
 
-  int ret = BIO_write(ssl->wbio, buf->buf + buf->offset, buf->len);
+  int ret = BIO_write(ssl->wbio, buf->data(), buf->size());
   if (ret <= 0) {
     ssl->rwstate = SSL_WRITING;
     // If the write failed, drop the write buffer anyway. Datagram transports
     // can't write half a packet, so the caller is expected to retry from the
     // top.
-    ssl_write_buffer_clear(ssl);
+    buf->Clear();
     return ret;
   }
-  ssl_write_buffer_clear(ssl);
+  buf->Clear();
   return 1;
 }
 
@@ -329,8 +283,4 @@
   }
 }
 
-void ssl_write_buffer_clear(SSL *ssl) {
-  clear_buffer(&ssl->s3->write_buffer);
-}
-
 }  // namespace bssl
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc
index e3f8a88..f5d1202 100644
--- a/ssl/ssl_lib.cc
+++ b/ssl/ssl_lib.cc
@@ -937,7 +937,7 @@
   // protocol, namely in HTTPS, just before reading the HTTP response. Require
   // the record-layer be idle and avoid complexities of sending a handshake
   // record while an application_data record is being written.
-  if (ssl_write_buffer_is_pending(ssl) ||
+  if (!ssl->s3->write_buffer.empty() ||
       ssl->s3->write_shutdown != ssl_shutdown_none) {
     goto no_renegotiation;
   }
@@ -1004,7 +1004,7 @@
     uint8_t alert = SSL_AD_DECODE_ERROR;
     size_t consumed = 0;
     auto ret = ssl_open_app_data(ssl, &ssl->s3->pending_app_data, &consumed,
-                                 &alert, ssl_read_buffer(ssl));
+                                 &alert, ssl->s3->read_buffer.span());
     bool retry;
     int bio_ret = ssl_handle_open_record(ssl, &retry, ret, consumed, alert);
     if (bio_ret <= 0) {
@@ -1029,7 +1029,7 @@
   ssl->s3->pending_app_data =
       ssl->s3->pending_app_data.subspan(static_cast<size_t>(ret));
   if (ssl->s3->pending_app_data.empty()) {
-    ssl_read_buffer_discard(ssl);
+    ssl->s3->read_buffer.DiscardConsumed();
   }
   return ret;
 }