Convert SSL_TRANSCRIPT to C++.

Bug: 132
Change-Id: I2d7cb45d56e8dcb223fbc5838922fdbe6f28ded7
Reviewed-on: https://boringssl-review.googlesource.com/18264
Reviewed-by: Steven Valdez <svaldez@google.com>
Commit-Queue: Steven Valdez <svaldez@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/d1_both.cc b/ssl/d1_both.cc
index 3a46977..50cca83 100644
--- a/ssl/d1_both.cc
+++ b/ssl/d1_both.cc
@@ -549,10 +549,10 @@
   }
 
   if (!is_ccs) {
-    /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT
+    /* TODO(svaldez): Move this up a layer to fix abstraction for SSLTranscript
      * on hs. */
     if (ssl->s3->hs != NULL &&
-        !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, data, len)) {
+        !ssl->s3->hs->transcript.Update(data, len)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       OPENSSL_free(data);
       return 0;
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 4c85531..cac65f6 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -715,7 +715,7 @@
   SSL *const ssl = hs->ssl;
   /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we
    * may send multiple ClientHellos if we receive HelloVerifyRequest. */
-  if (!SSL_TRANSCRIPT_init(&hs->transcript)) {
+  if (!hs->transcript.Init()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -1022,8 +1022,7 @@
 
   /* Now that the cipher is known, initialize the handshake hash and hash the
    * ServerHello. */
-  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl),
-                                c->algorithm_prf) ||
+  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl), c->algorithm_prf) ||
       !ssl_hash_current_message(hs)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
@@ -1034,7 +1033,7 @@
    * buffer may be released. */
   if (ssl->session != NULL ||
       !ssl_cipher_uses_certificate_auth(hs->new_cipher)) {
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
   }
 
   /* Only the NULL compression algorithm is supported. */
@@ -1382,7 +1381,7 @@
     ssl->s3->tmp.reuse_message = 1;
     /* If we get here we don't need the handshake buffer as we won't be doing
      * client auth. */
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
     return 1;
   }
 
@@ -1478,7 +1477,7 @@
 
   if (!ssl_has_certificate(ssl)) {
     /* Without a client certificate, the handshake buffer may be released. */
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
 
     /* In SSL 3.0, the Certificate message is replaced with a warning alert. */
     if (ssl->version == SSL3_VERSION) {
@@ -1720,9 +1719,8 @@
 
     uint8_t digest[EVP_MAX_MD_SIZE];
     size_t digest_len;
-    if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(
-            &hs->transcript, digest, &digest_len, hs->new_session.get(),
-            signature_algorithm)) {
+    if (!hs->transcript.GetSSL3CertVerifyHash(
+            digest, &digest_len, hs->new_session.get(), signature_algorithm)) {
       return -1;
     }
 
@@ -1733,10 +1731,9 @@
       return -1;
     }
   } else {
-    switch (ssl_private_key_sign(hs, ptr, &sig_len, max_sig_len,
-                                 signature_algorithm,
-                                 (const uint8_t *)hs->transcript.buffer->data,
-                                 hs->transcript.buffer->length)) {
+    switch (ssl_private_key_sign(
+        hs, ptr, &sig_len, max_sig_len, signature_algorithm,
+        hs->transcript.buffer_data(), hs->transcript.buffer_len())) {
       case ssl_private_key_success:
         break;
       case ssl_private_key_failure:
@@ -1753,7 +1750,7 @@
   }
 
   /* The handshake buffer is no longer necessary. */
-  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+  hs->transcript.FreeBuffer();
   return 1;
 }
 
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 48d3426..b57b67a 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -930,8 +930,8 @@
 
   /* Now that all parameters are known, initialize the handshake hash and hash
    * the ClientHello. */
-  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, ssl3_protocol_version(ssl),
-                                hs->new_cipher->algorithm_prf) ||
+  if (!hs->transcript.InitHash(ssl3_protocol_version(ssl),
+                               hs->new_cipher->algorithm_prf) ||
       !ssl_hash_current_message(hs)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return -1;
@@ -939,7 +939,7 @@
 
   /* Release the handshake buffer if client authentication isn't required. */
   if (!hs->cert_request) {
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
   }
 
   return 1;
@@ -1251,7 +1251,7 @@
 
   if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0) {
     /* No client certificate so the handshake buffer may be discarded. */
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
 
     /* In SSL 3.0, sending no certificate is signaled by omitting the
      * Certificate message. */
@@ -1540,7 +1540,7 @@
    * CertificateVerify is required if and only if there's a client certificate.
    * */
   if (!hs->peer_pubkey) {
-    SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+    hs->transcript.FreeBuffer();
     return 1;
   }
 
@@ -1590,9 +1590,8 @@
   if (ssl3_protocol_version(ssl) == SSL3_VERSION) {
     uint8_t digest[EVP_MAX_MD_SIZE];
     size_t digest_len;
-    if (!SSL_TRANSCRIPT_ssl3_cert_verify_hash(
-            &hs->transcript, digest, &digest_len, hs->new_session.get(),
-            signature_algorithm)) {
+    if (!hs->transcript.GetSSL3CertVerifyHash(
+            digest, &digest_len, hs->new_session.get(), signature_algorithm)) {
       return -1;
     }
 
@@ -1605,8 +1604,8 @@
   } else {
     sig_ok = ssl_public_key_verify(
         ssl, CBS_data(&signature), CBS_len(&signature), signature_algorithm,
-        hs->peer_pubkey.get(), (const uint8_t *)hs->transcript.buffer->data,
-        hs->transcript.buffer->length);
+        hs->peer_pubkey.get(), hs->transcript.buffer_data(),
+        hs->transcript.buffer_len());
   }
 
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
@@ -1621,7 +1620,7 @@
 
   /* The handshake buffer is no longer necessary, and we may hash the current
    * message.*/
-  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+  hs->transcript.FreeBuffer();
   if (!ssl_hash_current_message(hs)) {
     return -1;
   }
diff --git a/ssl/internal.h b/ssl/internal.h
index 160247a..f7d1cde 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -365,75 +365,75 @@
 
 /* Transcript layer. */
 
-/* SSL_TRANSCRIPT maintains the handshake transcript as a combination of a
+/* SSLTranscript maintains the handshake transcript as a combination of a
  * buffer and running hash. */
-struct SSL_TRANSCRIPT {
-  /* buffer, if non-NULL, contains the handshake transcript. */
-  BUF_MEM *buffer;
+class SSLTranscript {
+ public:
+  SSLTranscript();
+  ~SSLTranscript();
+
+  /* Init initializes the handshake transcript. If called on an existing
+   * transcript, it resets the transcript and hash. It returns true on success
+   * and false on failure. */
+  bool Init();
+
+  /* InitHash initializes the handshake hash based on the PRF and contents of
+   * the handshake transcript. Subsequent calls to |Update| will update the
+   * rolling hash. It returns one on success and zero on failure. It is an error
+   * to call this function after the handshake buffer is released. */
+  bool InitHash(uint16_t version, int algorithm_prf);
+
+  const uint8_t *buffer_data() const {
+    return reinterpret_cast<const uint8_t *>(buffer_->data);
+  }
+  size_t buffer_len() const { return buffer_->length; }
+
+  /* FreeBuffer releases the handshake buffer. Subsequent calls to
+   * |Update| will not update the handshake buffer. */
+  void FreeBuffer();
+
+  /* DigestLen returns the length of the PRF hash. */
+  size_t DigestLen() const;
+
+  /* Digest returns the PRF hash. For TLS 1.1 and below, this is
+   * |EVP_md5_sha1|. */
+  const EVP_MD *Digest() const;
+
+  /* Update adds |in| to the handshake buffer and handshake hash, whichever is
+   * enabled. It returns true on success and false on failure. */
+  bool Update(const uint8_t *in, size_t in_len);
+
+  /* GetHash writes the handshake hash to |out| which must have room for at
+   * least |DigestLen| bytes. On success, it returns true and sets |*out_len| to
+   * the number of bytes written. Otherwise, it returns false. */
+  bool GetHash(uint8_t *out, size_t *out_len);
+
+  /* GetSSL3CertVerifyHash writes the SSL 3.0 CertificateVerify hash into the
+   * bytes pointed to by |out| and writes the number of bytes to
+   * |*out_len|. |out| must have room for |EVP_MAX_MD_SIZE| bytes. It returns
+   * one on success and zero on failure. */
+  bool GetSSL3CertVerifyHash(uint8_t *out, size_t *out_len,
+                             const SSL_SESSION *session,
+                             uint16_t signature_algorithm);
+
+  /* GetFinishedMAC computes the MAC for the Finished message into the bytes
+   * pointed by |out| and writes the number of bytes to |*out_len|. |out| must
+   * have room for |EVP_MAX_MD_SIZE| bytes. It returns true on success and false
+   * on failure. */
+  bool GetFinishedMAC(uint8_t *out, size_t *out_len, const SSL_SESSION *session,
+                      bool from_server, uint16_t version);
+
+ private:
+  /* buffer_, if non-null, contains the handshake transcript. */
+  UniquePtr<BUF_MEM> buffer_;
   /* hash, if initialized with an |EVP_MD|, maintains the handshake hash. For
    * TLS 1.1 and below, it is the SHA-1 half. */
-  EVP_MD_CTX hash;
+  ScopedEVP_MD_CTX hash_;
   /* md5, if initialized with an |EVP_MD|, maintains the MD5 half of the
    * handshake hash for TLS 1.1 and below. */
-  EVP_MD_CTX md5;
+  ScopedEVP_MD_CTX md5_;
 };
 
-/* SSL_TRANSCRIPT_init initializes the handshake transcript. If called on an
- * existing transcript, it resets the transcript and hash. It returns one on
- * success and zero on failure. */
-int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript);
-
-/* SSL_TRANSCRIPT_init_hash initializes the handshake hash based on the PRF and
- * contents of the handshake transcript. Subsequent calls to
- * |SSL_TRANSCRIPT_update| will update the rolling hash. It returns one on
- * success and zero on failure. It is an error to call this function after the
- * handshake buffer is released. */
-int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version,
-                             int algorithm_prf);
-
-/* SSL_TRANSCRIPT_cleanup cleans up the hash and transcript. */
-void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript);
-
-/* SSL_TRANSCRIPT_free_buffer releases the handshake buffer. Subsequent calls to
- * |SSL_TRANSCRIPT_update| will not update the handshake buffer. */
-void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript);
-
-/* SSL_TRANSCRIPT_digest_len returns the length of the PRF hash. */
-size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript);
-
-/* SSL_TRANSCRIPT_md returns the PRF hash. For TLS 1.1 and below, this is
- * |EVP_md5_sha1|. */
-const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript);
-
-/* SSL_TRANSCRIPT_update adds |in| to the handshake buffer and handshake hash,
- * whichever is enabled. It returns one on success and zero on failure. */
-int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in,
-                          size_t in_len);
-
-/* SSL_TRANSCRIPT_get_hash writes the handshake hash to |out| which must have
- * room for at least |SSL_TRANSCRIPT_digest_len| bytes. On success, it returns
- * one and sets |*out_len| to the number of bytes written. Otherwise, it returns
- * zero. */
-int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out,
-                            size_t *out_len);
-
-/* SSL_TRANSCRIPT_ssl3_cert_verify_hash writes the SSL 3.0 CertificateVerify
- * hash into the bytes pointed to by |out| and writes the number of bytes to
- * |*out_len|. |out| must have room for |EVP_MAX_MD_SIZE| bytes. It returns one
- * on success and zero on failure. */
-int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript,
-                                         uint8_t *out, size_t *out_len,
-                                         const SSL_SESSION *session,
-                                         int signature_algorithm);
-
-/* SSL_TRANSCRIPT_finish_mac computes the MAC for the Finished message into the
- * bytes pointed by |out| and writes the number of bytes to |*out_len|. |out|
- * must have room for |EVP_MAX_MD_SIZE| bytes. It returns one on success and
- * zero on failure. */
-int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out,
-                              size_t *out_len, const SSL_SESSION *session,
-                              int from_server, uint16_t version);
-
 /* tls1_prf computes the PRF function for |ssl|. It writes |out_len| bytes to
  * |out|, using |secret| as the secret and |label| as the label. |seed1| and
  * |seed2| are concatenated to form the seed parameter. It returns one on
@@ -1152,7 +1152,7 @@
   SSL_ECDH_CTX ecdh_ctx;
 
   /* transcript is the current handshake transcript. */
-  SSL_TRANSCRIPT transcript;
+  SSLTranscript transcript;
 
   /* cookie is the value of the cookie received from the server, if any. */
   uint8_t *cookie = nullptr;
diff --git a/ssl/s3_both.cc b/ssl/s3_both.cc
index 1c47c1c..2ac2837 100644
--- a/ssl/s3_both.cc
+++ b/ssl/s3_both.cc
@@ -152,7 +152,6 @@
       extended_master_secret(0),
       pending_private_key_op(0) {
   OPENSSL_memset(&ecdh_ctx, 0, sizeof(ecdh_ctx));
-  OPENSSL_memset(&transcript, 0, sizeof(transcript));
 }
 
 SSL_HANDSHAKE::~SSL_HANDSHAKE() {
@@ -163,7 +162,6 @@
   OPENSSL_cleanse(client_traffic_secret_0, sizeof(client_traffic_secret_0));
   OPENSSL_cleanse(server_traffic_secret_0, sizeof(server_traffic_secret_0));
   SSL_ECDH_CTX_cleanup(&ecdh_ctx);
-  SSL_TRANSCRIPT_cleanup(&transcript);
   OPENSSL_free(cookie);
   OPENSSL_free(key_share_bytes);
   OPENSSL_free(ecdh_public_key);
@@ -183,7 +181,7 @@
 SSL_HANDSHAKE *ssl_handshake_new(SSL *ssl) {
   UniquePtr<SSL_HANDSHAKE> hs = MakeUnique<SSL_HANDSHAKE>(ssl);
   if (!hs ||
-      !SSL_TRANSCRIPT_init(&hs->transcript)) {
+      !hs->transcript.Init()) {
     return nullptr;
   }
   return hs.release();
@@ -283,10 +281,10 @@
   } while (added < len);
 
   ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg, len);
-  /* TODO(svaldez): Move this up a layer to fix abstraction for SSL_TRANSCRIPT
-   * on hs. */
+  /* TODO(svaldez): Move this up a layer to fix abstraction for SSLTranscript on
+   * hs. */
   if (ssl->s3->hs != NULL &&
-      !SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript, msg, len)) {
+      !ssl->s3->hs->transcript.Update(msg, len)) {
     goto err;
   }
   ret = 1;
@@ -383,9 +381,8 @@
 
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len;
-  if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len,
-                                 session, ssl->server,
-                                 ssl3_protocol_version(ssl))) {
+  if (!hs->transcript.GetFinishedMAC(finished, &finished_len, session,
+                                     ssl->server, ssl3_protocol_version(ssl))) {
     return 0;
   }
 
@@ -439,9 +436,9 @@
   /* Snapshot the finished hash before incorporating the new message. */
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len;
-  if (!SSL_TRANSCRIPT_finish_mac(&hs->transcript, finished, &finished_len,
-                                 SSL_get_session(ssl), !ssl->server,
-                                 ssl3_protocol_version(ssl)) ||
+  if (!hs->transcript.GetFinishedMAC(finished, &finished_len,
+                                     SSL_get_session(ssl), !ssl->server,
+                                     ssl3_protocol_version(ssl)) ||
       !ssl_hash_current_message(hs)) {
     return -1;
   }
@@ -593,9 +590,8 @@
   /* 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. */
-  if (!SSL_TRANSCRIPT_update(&ssl->s3->hs->transcript,
-                             CBS_data(&v2_client_hello),
-                             CBS_len(&v2_client_hello))) {
+  if (!ssl->s3->hs->transcript.Update(CBS_data(&v2_client_hello),
+                                      CBS_len(&v2_client_hello))) {
     return -1;
   }
 
@@ -758,7 +754,7 @@
 
   CBS cbs;
   hs->ssl->method->get_current_message(hs->ssl, &cbs);
-  return SSL_TRANSCRIPT_update(&hs->transcript, CBS_data(&cbs), CBS_len(&cbs));
+  return hs->transcript.Update(CBS_data(&cbs), CBS_len(&cbs));
 }
 
 void ssl3_release_current_message(SSL *ssl, int free_buffer) {
diff --git a/ssl/ssl_transcript.cc b/ssl/ssl_transcript.cc
index cd62a3d..4a00d0f 100644
--- a/ssl/ssl_transcript.cc
+++ b/ssl/ssl_transcript.cc
@@ -152,136 +152,115 @@
 
 namespace bssl {
 
-int SSL_TRANSCRIPT_init(SSL_TRANSCRIPT *transcript) {
-  SSL_TRANSCRIPT_cleanup(transcript);
-  transcript->buffer = BUF_MEM_new();
-  return transcript->buffer != NULL;
+SSLTranscript::SSLTranscript() {}
+
+SSLTranscript::~SSLTranscript() {}
+
+bool SSLTranscript::Init() {
+  buffer_.reset(BUF_MEM_new());
+  if (!buffer_) {
+    return false;
+  }
+
+  hash_.Reset();
+  md5_.Reset();
+  return true;
 }
 
-/* init_digest_with_data calls |EVP_DigestInit_ex| on |ctx| with |md| and then
+/* InitDigestWithData calls |EVP_DigestInit_ex| on |ctx| with |md| and then
  * writes the data in |buf| to it. */
-static int init_digest_with_data(EVP_MD_CTX *ctx, const EVP_MD *md,
-                                 const BUF_MEM *buf) {
+static bool InitDigestWithData(EVP_MD_CTX *ctx, const EVP_MD *md,
+                               const BUF_MEM *buf) {
   if (!EVP_DigestInit_ex(ctx, md, NULL)) {
-    return 0;
+    return false;
   }
   EVP_DigestUpdate(ctx, buf->data, buf->length);
-  return 1;
+  return true;
 }
 
-int SSL_TRANSCRIPT_init_hash(SSL_TRANSCRIPT *transcript, uint16_t version,
-                             int algorithm_prf) {
+bool SSLTranscript::InitHash(uint16_t version, int algorithm_prf) {
   const EVP_MD *md = ssl_get_handshake_digest(algorithm_prf, version);
 
   /* To support SSL 3.0's Finished and CertificateVerify constructions,
    * EVP_md5_sha1() is split into MD5 and SHA-1 halves. When SSL 3.0 is removed,
    * we can simplify this. */
   if (md == EVP_md5_sha1()) {
-    if (!init_digest_with_data(&transcript->md5, EVP_md5(),
-                               transcript->buffer)) {
-      return 0;
+    if (!InitDigestWithData(md5_.get(), EVP_md5(), buffer_.get())) {
+      return false;
     }
     md = EVP_sha1();
   }
 
-  if (!init_digest_with_data(&transcript->hash, md, transcript->buffer)) {
-    return 0;
-  }
-
-  return 1;
+  return InitDigestWithData(hash_.get(), md, buffer_.get());
 }
 
-void SSL_TRANSCRIPT_cleanup(SSL_TRANSCRIPT *transcript) {
-  SSL_TRANSCRIPT_free_buffer(transcript);
-  EVP_MD_CTX_cleanup(&transcript->hash);
-  EVP_MD_CTX_cleanup(&transcript->md5);
+void SSLTranscript::FreeBuffer() {
+  buffer_.reset();
 }
 
-void SSL_TRANSCRIPT_free_buffer(SSL_TRANSCRIPT *transcript) {
-  BUF_MEM_free(transcript->buffer);
-  transcript->buffer = NULL;
+size_t SSLTranscript::DigestLen() const {
+  return EVP_MD_size(Digest());
 }
 
-size_t SSL_TRANSCRIPT_digest_len(const SSL_TRANSCRIPT *transcript) {
-  return EVP_MD_size(SSL_TRANSCRIPT_md(transcript));
-}
-
-const EVP_MD *SSL_TRANSCRIPT_md(const SSL_TRANSCRIPT *transcript) {
-  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
+const EVP_MD *SSLTranscript::Digest() const {
+  if (EVP_MD_CTX_md(md5_.get()) != nullptr) {
     return EVP_md5_sha1();
   }
-  return EVP_MD_CTX_md(&transcript->hash);
+  return EVP_MD_CTX_md(hash_.get());
 }
 
-int SSL_TRANSCRIPT_update(SSL_TRANSCRIPT *transcript, const uint8_t *in,
-                          size_t in_len) {
+bool SSLTranscript::Update(const uint8_t *in, size_t in_len) {
   /* Depending on the state of the handshake, either the handshake buffer may be
    * active, the rolling hash, or both. */
-  if (transcript->buffer != NULL) {
-    size_t new_len = transcript->buffer->length + in_len;
+  if (buffer_) {
+    size_t new_len = buffer_->length + in_len;
     if (new_len < in_len) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
-      return 0;
+      return false;
     }
-    if (!BUF_MEM_grow(transcript->buffer, new_len)) {
-      return 0;
+    if (!BUF_MEM_grow(buffer_.get(), new_len)) {
+      return false;
     }
-    OPENSSL_memcpy(transcript->buffer->data + new_len - in_len, in, in_len);
+    OPENSSL_memcpy(buffer_->data + new_len - in_len, in, in_len);
   }
 
-  if (EVP_MD_CTX_md(&transcript->hash) != NULL) {
-    EVP_DigestUpdate(&transcript->hash, in, in_len);
+  if (EVP_MD_CTX_md(hash_.get()) != NULL) {
+    EVP_DigestUpdate(hash_.get(), in, in_len);
   }
-  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
-    EVP_DigestUpdate(&transcript->md5, in, in_len);
+  if (EVP_MD_CTX_md(md5_.get()) != NULL) {
+    EVP_DigestUpdate(md5_.get(), in, in_len);
   }
 
-  return 1;
+  return true;
 }
 
-int SSL_TRANSCRIPT_get_hash(const SSL_TRANSCRIPT *transcript, uint8_t *out,
-                            size_t *out_len) {
-  int ret = 0;
-  EVP_MD_CTX ctx;
-  EVP_MD_CTX_init(&ctx);
+bool SSLTranscript::GetHash(uint8_t *out, size_t *out_len) {
+  ScopedEVP_MD_CTX ctx;
   unsigned md5_len = 0;
-  if (EVP_MD_CTX_md(&transcript->md5) != NULL) {
-    if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->md5) ||
-        !EVP_DigestFinal_ex(&ctx, out, &md5_len)) {
-      goto err;
+  if (EVP_MD_CTX_md(md5_.get()) != NULL) {
+    if (!EVP_MD_CTX_copy_ex(ctx.get(), md5_.get()) ||
+        !EVP_DigestFinal_ex(ctx.get(), out, &md5_len)) {
+      return false;
     }
   }
 
   unsigned len;
-  if (!EVP_MD_CTX_copy_ex(&ctx, &transcript->hash) ||
-      !EVP_DigestFinal_ex(&ctx, out + md5_len, &len)) {
-    goto err;
+  if (!EVP_MD_CTX_copy_ex(ctx.get(), hash_.get()) ||
+      !EVP_DigestFinal_ex(ctx.get(), out + md5_len, &len)) {
+    return false;
   }
 
   *out_len = md5_len + len;
-  ret = 1;
-
-err:
-  EVP_MD_CTX_cleanup(&ctx);
-  return ret;
+  return true;
 }
 
-static int ssl3_handshake_mac(SSL_TRANSCRIPT *transcript,
-                              const SSL_SESSION *session,
-                              const EVP_MD_CTX *ctx_template,
-                              const char *sender, size_t sender_len,
-                              uint8_t *p, size_t *out_len) {
-  unsigned int len;
-  size_t npad, n;
-  unsigned int i;
-  uint8_t md_buf[EVP_MAX_MD_SIZE];
-  EVP_MD_CTX ctx;
-
-  EVP_MD_CTX_init(&ctx);
-  if (!EVP_MD_CTX_copy_ex(&ctx, ctx_template)) {
-    EVP_MD_CTX_cleanup(&ctx);
+static bool SSL3HandshakeMAC(const SSL_SESSION *session,
+                             const EVP_MD_CTX *ctx_template, const char *sender,
+                             size_t sender_len, uint8_t *p, size_t *out_len) {
+  ScopedEVP_MD_CTX ctx;
+  if (!EVP_MD_CTX_copy_ex(ctx.get(), ctx_template)) {
     OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
-    return 0;
+    return false;
   }
 
   static const uint8_t kPad1[48] = {
@@ -298,89 +277,83 @@
       0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c, 0x5c,
   };
 
-  n = EVP_MD_CTX_size(&ctx);
+  size_t n = EVP_MD_CTX_size(ctx.get());
 
-  npad = (48 / n) * n;
-  if (sender != NULL) {
-    EVP_DigestUpdate(&ctx, sender, sender_len);
-  }
-  EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length);
-  EVP_DigestUpdate(&ctx, kPad1, npad);
-  EVP_DigestFinal_ex(&ctx, md_buf, &i);
+  size_t npad = (48 / n) * n;
+  EVP_DigestUpdate(ctx.get(), sender, sender_len);
+  EVP_DigestUpdate(ctx.get(), session->master_key, session->master_key_length);
+  EVP_DigestUpdate(ctx.get(), kPad1, npad);
+  unsigned md_buf_len;
+  uint8_t md_buf[EVP_MAX_MD_SIZE];
+  EVP_DigestFinal_ex(ctx.get(), md_buf, &md_buf_len);
 
-  if (!EVP_DigestInit_ex(&ctx, EVP_MD_CTX_md(&ctx), NULL)) {
-    EVP_MD_CTX_cleanup(&ctx);
+  if (!EVP_DigestInit_ex(ctx.get(), EVP_MD_CTX_md(ctx.get()), NULL)) {
     OPENSSL_PUT_ERROR(SSL, ERR_LIB_EVP);
-    return 0;
+    return false;
   }
-  EVP_DigestUpdate(&ctx, session->master_key, session->master_key_length);
-  EVP_DigestUpdate(&ctx, kPad2, npad);
-  EVP_DigestUpdate(&ctx, md_buf, i);
-  EVP_DigestFinal_ex(&ctx, p, &len);
-
-  EVP_MD_CTX_cleanup(&ctx);
+  EVP_DigestUpdate(ctx.get(), session->master_key, session->master_key_length);
+  EVP_DigestUpdate(ctx.get(), kPad2, npad);
+  EVP_DigestUpdate(ctx.get(), md_buf, md_buf_len);
+  unsigned len;
+  EVP_DigestFinal_ex(ctx.get(), p, &len);
 
   *out_len = len;
-  return 1;
+  return true;
 }
 
-int SSL_TRANSCRIPT_ssl3_cert_verify_hash(SSL_TRANSCRIPT *transcript,
-                                         uint8_t *out, size_t *out_len,
-                                         const SSL_SESSION *session,
-                                         int signature_algorithm) {
-  if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) {
+bool SSLTranscript::GetSSL3CertVerifyHash(uint8_t *out, size_t *out_len,
+                                          const SSL_SESSION *session,
+                                          uint16_t signature_algorithm) {
+  if (Digest() != EVP_md5_sha1()) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return false;
   }
 
   if (signature_algorithm == SSL_SIGN_RSA_PKCS1_MD5_SHA1) {
     size_t md5_len, len;
-    if (!ssl3_handshake_mac(transcript, session, &transcript->md5, NULL, 0, out,
-                            &md5_len) ||
-        !ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0,
-                            out + md5_len, &len)) {
-      return 0;
+    if (!SSL3HandshakeMAC(session, md5_.get(), NULL, 0, out, &md5_len) ||
+        !SSL3HandshakeMAC(session, hash_.get(), NULL, 0, out + md5_len, &len)) {
+      return false;
     }
     *out_len = md5_len + len;
-    return 1;
+    return true;
   }
 
   if (signature_algorithm == SSL_SIGN_ECDSA_SHA1) {
-    return ssl3_handshake_mac(transcript, session, &transcript->hash, NULL, 0,
-                              out, out_len);
+    return SSL3HandshakeMAC(session, hash_.get(), NULL, 0, out, out_len);
   }
 
   OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-  return 0;
+  return false;
 }
 
-int SSL_TRANSCRIPT_finish_mac(SSL_TRANSCRIPT *transcript, uint8_t *out,
-                              size_t *out_len, const SSL_SESSION *session,
-                              int from_server, uint16_t version) {
+bool SSLTranscript::GetFinishedMAC(uint8_t *out, size_t *out_len,
+                                   const SSL_SESSION *session, bool from_server,
+                                   uint16_t version) {
   if (version == SSL3_VERSION) {
-    if (SSL_TRANSCRIPT_md(transcript) != EVP_md5_sha1()) {
+    if (Digest() != EVP_md5_sha1()) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
+      return false;
     }
 
     const char *sender = from_server ? SSL3_MD_SERVER_FINISHED_CONST
                                      : SSL3_MD_CLIENT_FINISHED_CONST;
     const size_t sender_len = 4;
     size_t md5_len, len;
-    if (!ssl3_handshake_mac(transcript, session, &transcript->md5, sender,
-                            sender_len, out, &md5_len) ||
-        !ssl3_handshake_mac(transcript, session, &transcript->hash, sender,
-                            sender_len, out + md5_len, &len)) {
-      return 0;
+    if (!SSL3HandshakeMAC(session, md5_.get(), sender, sender_len, out,
+                          &md5_len) ||
+        !SSL3HandshakeMAC(session, hash_.get(), sender, sender_len,
+                          out + md5_len, &len)) {
+      return false;
     }
 
     *out_len = md5_len + len;
-    return 1;
+    return true;
   }
 
   /* At this point, the handshake should have released the handshake buffer on
    * its own. */
-  assert(transcript->buffer == NULL);
+  assert(!buffer_);
 
   const char *label = TLS_MD_CLIENT_FINISH_CONST;
   size_t label_len = TLS_MD_SERVER_FINISH_CONST_SIZE;
@@ -391,19 +364,19 @@
 
   uint8_t digests[EVP_MAX_MD_SIZE];
   size_t digests_len;
-  if (!SSL_TRANSCRIPT_get_hash(transcript, digests, &digests_len)) {
-    return 0;
+  if (!GetHash(digests, &digests_len)) {
+    return false;
   }
 
   static const size_t kFinishedLen = 12;
-  if (!tls1_prf(SSL_TRANSCRIPT_md(transcript), out, kFinishedLen,
-                session->master_key, session->master_key_length, label,
-                label_len, digests, digests_len, NULL, 0)) {
-    return 0;
+  if (!tls1_prf(Digest(), out, kFinishedLen, session->master_key,
+                session->master_key_length, label, label_len, digests,
+                digests_len, NULL, 0)) {
+    return false;
   }
 
   *out_len = kFinishedLen;
-  return 1;
+  return true;
 }
 
 }  // namespace bssl
diff --git a/ssl/t1_enc.cc b/ssl/t1_enc.cc
index 4fbc6c4..2349df0 100644
--- a/ssl/t1_enc.cc
+++ b/ssl/t1_enc.cc
@@ -443,10 +443,9 @@
   if (hs->extended_master_secret) {
     uint8_t digests[EVP_MAX_MD_SIZE];
     size_t digests_len;
-    if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, digests, &digests_len) ||
-        !tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out,
-                  SSL3_MASTER_SECRET_SIZE, premaster, premaster_len,
-                  TLS_MD_EXTENDED_MASTER_SECRET_CONST,
+    if (!hs->transcript.GetHash(digests, &digests_len) ||
+        !tls1_prf(hs->transcript.Digest(), out, SSL3_MASTER_SECRET_SIZE,
+                  premaster, premaster_len, TLS_MD_EXTENDED_MASTER_SECRET_CONST,
                   TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE, digests,
                   digests_len, NULL, 0)) {
       return 0;
@@ -460,11 +459,11 @@
         return 0;
       }
     } else {
-      if (!tls1_prf(SSL_TRANSCRIPT_md(&hs->transcript), out,
-                    SSL3_MASTER_SECRET_SIZE, premaster, premaster_len,
-                    TLS_MD_MASTER_SECRET_CONST, TLS_MD_MASTER_SECRET_CONST_SIZE,
-                    ssl->s3->client_random, SSL3_RANDOM_SIZE,
-                    ssl->s3->server_random, SSL3_RANDOM_SIZE)) {
+      if (!tls1_prf(hs->transcript.Digest(), out, SSL3_MASTER_SECRET_SIZE,
+                    premaster, premaster_len, TLS_MD_MASTER_SECRET_CONST,
+                    TLS_MD_MASTER_SECRET_CONST_SIZE, ssl->s3->client_random,
+                    SSL3_RANDOM_SIZE, ssl->s3->server_random,
+                    SSL3_RANDOM_SIZE)) {
         return 0;
       }
     }
diff --git a/ssl/t1_lib.cc b/ssl/t1_lib.cc
index 118bc4a..b4cd964 100644
--- a/ssl/t1_lib.cc
+++ b/ssl/t1_lib.cc
@@ -3443,7 +3443,7 @@
 
   uint8_t hs_hash[EVP_MAX_MD_SIZE];
   size_t hs_hash_len;
-  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, hs_hash, &hs_hash_len)) {
+  if (!hs->transcript.GetHash(hs_hash, &hs_hash_len)) {
     return 0;
   }
   SHA256_Update(&ctx, hs_hash, (size_t)hs_hash_len);
@@ -3469,9 +3469,8 @@
       "original_handshake_hash is too small");
 
   size_t digest_len;
-  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript,
-                               hs->new_session->original_handshake_hash,
-                               &digest_len)) {
+  if (!hs->transcript.GetHash(hs->new_session->original_handshake_hash,
+                              &digest_len)) {
     return -1;
   }
 
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index 90fbf1c..338975b 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -181,8 +181,7 @@
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
-                               &context_hash_len) ||
+  if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
       !CBB_add_bytes(cbb.get(), context_hash, context_hash_len) ||
       !CBB_finish(cbb.get(), out, out_len)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/ssl/tls13_enc.cc b/ssl/tls13_enc.cc
index 14dbb9d..1ae4849 100644
--- a/ssl/tls13_enc.cc
+++ b/ssl/tls13_enc.cc
@@ -34,11 +34,11 @@
 
 static int init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
                               int algorithm_prf) {
-  if (!SSL_TRANSCRIPT_init_hash(&hs->transcript, version, algorithm_prf)) {
+  if (!hs->transcript.InitHash(version, algorithm_prf)) {
     return 0;
   }
 
-  hs->hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript);
+  hs->hash_len = hs->transcript.DigestLen();
 
   /* Initialize the secret to the zero key. */
   OPENSSL_memset(hs->secret, 0, hs->hash_len);
@@ -52,7 +52,7 @@
     return 0;
   }
 
-  SSL_TRANSCRIPT_free_buffer(&hs->transcript);
+  hs->transcript.FreeBuffer();
   return 1;
 }
 
@@ -64,9 +64,8 @@
 
 int tls13_advance_key_schedule(SSL_HANDSHAKE *hs, const uint8_t *in,
                                size_t len) {
-  return HKDF_extract(hs->secret, &hs->hash_len,
-                      SSL_TRANSCRIPT_md(&hs->transcript), in, len, hs->secret,
-                      hs->hash_len);
+  return HKDF_extract(hs->secret, &hs->hash_len, hs->transcript.Digest(), in,
+                      len, hs->secret, hs->hash_len);
 }
 
 static int hkdf_expand_label(uint8_t *out, const EVP_MD *digest,
@@ -105,12 +104,11 @@
                          const uint8_t *label, size_t label_len) {
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
-                               &context_hash_len)) {
+  if (!hs->transcript.GetHash(context_hash, &context_hash_len)) {
     return 0;
   }
 
-  return hkdf_expand_label(out, SSL_TRANSCRIPT_md(&hs->transcript), hs->secret,
+  return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret,
                            hs->hash_len, label, label_len, context_hash,
                            context_hash_len, len);
 }
@@ -313,11 +311,9 @@
 
   uint8_t context_hash[EVP_MAX_MD_SIZE];
   size_t context_hash_len;
-  if (!SSL_TRANSCRIPT_get_hash(&hs->transcript, context_hash,
-                               &context_hash_len) ||
-      !tls13_verify_data(SSL_TRANSCRIPT_md(&hs->transcript), out, out_len,
-                         traffic_secret, hs->hash_len, context_hash,
-                         context_hash_len)) {
+  if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
+      !tls13_verify_data(hs->transcript.Digest(), out, out_len, traffic_secret,
+                         hs->hash_len, context_hash, context_hash_len)) {
     return 0;
   }
   return 1;
@@ -387,8 +383,8 @@
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
   if (!EVP_DigestInit_ex(ctx.get(), digest, NULL) ||
-      !EVP_DigestUpdate(ctx.get(), hs->transcript.buffer->data,
-                        hs->transcript.buffer->length) ||
+      !EVP_DigestUpdate(ctx.get(), hs->transcript.buffer_data(),
+                        hs->transcript.buffer_len()) ||
       !EVP_DigestUpdate(ctx.get(), msg, len - hash_len - 3) ||
       !EVP_DigestFinal_ex(ctx.get(), context, &context_len)) {
     return 0;
@@ -407,7 +403,7 @@
 
 int tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
                             CBS *binders) {
-  size_t hash_len = SSL_TRANSCRIPT_digest_len(&hs->transcript);
+  size_t hash_len = hs->transcript.DigestLen();
 
   /* Get the full ClientHello, including message header. It must be large enough
    * to exclude the binders. */
@@ -423,14 +419,13 @@
   uint8_t context[EVP_MAX_MD_SIZE];
   unsigned context_len;
   if (!EVP_Digest(CBS_data(&message), CBS_len(&message) - CBS_len(binders) - 2,
-                  context, &context_len, SSL_TRANSCRIPT_md(&hs->transcript),
-                  NULL)) {
+                  context, &context_len, hs->transcript.Digest(), NULL)) {
     return 0;
   }
 
   uint8_t verify_data[EVP_MAX_MD_SIZE] = {0};
   CBS binder;
-  if (!tls13_psk_binder(verify_data, SSL_TRANSCRIPT_md(&hs->transcript),
+  if (!tls13_psk_binder(verify_data, hs->transcript.Digest(),
                         session->master_key, session->master_key_length,
                         context, context_len, hash_len) ||
       /* We only consider the first PSK, so compare against the first binder. */
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index e455d68..067c427 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -671,12 +671,11 @@
      * TODO(davidben): This will need to be updated for DTLS 1.3. */
     assert(!SSL_is_dtls(hs->ssl));
     assert(hs->hash_len <= 0xff);
-    uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0, static_cast<uint8_t>(hs->hash_len)};
-    if (!SSL_TRANSCRIPT_update(&hs->transcript, header, sizeof(header)) ||
-        !SSL_TRANSCRIPT_update(&hs->transcript, hs->expected_client_finished,
-                               hs->hash_len) ||
-        !tls13_derive_resumption_secret(hs) ||
-        !add_new_session_tickets(hs)) {
+    uint8_t header[4] = {SSL3_MT_FINISHED, 0, 0,
+                         static_cast<uint8_t>(hs->hash_len)};
+    if (!hs->transcript.Update(header, sizeof(header)) ||
+        !hs->transcript.Update(hs->expected_client_finished, hs->hash_len) ||
+        !tls13_derive_resumption_secret(hs) || !add_new_session_tickets(hs)) {
       return ssl_hs_error;
     }
   }