Merge third_party/boringssl/src from https://boringssl.googlesource.com/boringssl.git at 03a739d8d2cdc2560531a7446ead0f705409670a

This commit was generated by merge_from_chromium.py.

Change-Id: I52d83e7a5a0a9b94fd10dbaf5350aef57d1c1f88
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 5d656ec..f98c7c8 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -12,12 +12,16 @@
 	if (CMAKE_CL_64)
 		message("Using masm")
 		set(PERLASM_STYLE masm)
+		enable_language(ASM_MASM)
 	else()
 		message("Using win32n")
 		set(PERLASM_STYLE win32n)
+
+		# On 32-bit, upstream supports only NASM, not MASM. We'll use Yasm, specifically.
+		set(CMAKE_ASM_NASM_COMPILER "yasm")
+		enable_language(ASM_NASM)
 	endif()
 	set(ASM_EXT asm)
-	enable_language(ASM_MASM)
 endif()
 
 function(perlasm dest src)
@@ -27,9 +31,10 @@
 		DEPENDS
 		${src}
 		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86_64-xlate.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86asm.pl
 		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86gas.pl
-		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86asm.pl
-		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86asm.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86masm.pl
+		${PROJECT_SOURCE_DIR}/crypto/perlasm/x86nasm.pl
 		WORKING_DIRECTORY .
 	)
 endfunction()
diff --git a/crypto/asn1/asn1_locl.h b/crypto/asn1/asn1_locl.h
index 1444390..ca5f612 100644
--- a/crypto/asn1/asn1_locl.h
+++ b/crypto/asn1/asn1_locl.h
@@ -71,25 +71,3 @@
 	unsigned long oid_flags;
 	unsigned long str_flags;
 	} /* ASN1_PCTX */;
-
-/* ASN1 public key method structure */
-
-
-/* Method to handle CRL access.
- * In general a CRL could be very large (several Mb) and can consume large
- * amounts of resources if stored in memory by multiple processes.
- * This method allows general CRL operations to be redirected to more
- * efficient callbacks: for example a CRL entry database.
- */
-
-#define X509_CRL_METHOD_DYNAMIC		1
-
-struct x509_crl_method_st
-	{
-	int flags;
-	int (*crl_init)(X509_CRL *crl);
-	int (*crl_free)(X509_CRL *crl);
-	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
-				ASN1_INTEGER *ser, X509_NAME *issuer);
-	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk);
-	};
diff --git a/crypto/bio/CMakeLists.txt b/crypto/bio/CMakeLists.txt
index 86092f0..9178931 100644
--- a/crypto/bio/CMakeLists.txt
+++ b/crypto/bio/CMakeLists.txt
@@ -26,3 +26,6 @@
 )
 
 target_link_libraries(bio_test crypto)
+if (WIN32)
+	target_link_libraries(bio_test ws2_32)
+endif()
diff --git a/crypto/bn/generic.c b/crypto/bn/generic.c
index b745750..c60cfd9 100644
--- a/crypto/bn/generic.c
+++ b/crypto/bn/generic.c
@@ -61,7 +61,7 @@
 #include "internal.h"
 
 
-#if defined(OPENSSL_WINDOWS) || defined(OPENSSL_NO_ASM) || \
+#if defined(OPENSSL_NO_ASM) || \
     (!defined(OPENSSL_X86_64) && !defined(OPENSSL_X86))
 
 #if defined(OPENSSL_WINDOWS)
diff --git a/crypto/bytestring/bytestring_test.c b/crypto/bytestring/bytestring_test.c
index f30179d..ba70bcf 100644
--- a/crypto/bytestring/bytestring_test.c
+++ b/crypto/bytestring/bytestring_test.c
@@ -19,6 +19,7 @@
 #include <openssl/bytestring.h>
 
 #include "internal.h"
+#include "../internal.h"
 
 
 static int test_skip(void) {
@@ -105,8 +106,14 @@
   static const uint8_t kData3[] = {0x30, 0x80};
   static const uint8_t kData4[] = {0x30, 0x81, 1, 1};
   static const uint8_t kData5[] = {0x30, 0x82, 0, 1, 1};
+  static const uint8_t kData6[] = {0xa1, 3, 0x4, 1, 1};
+  static const uint8_t kData7[] = {0xa1, 3, 0x4, 2, 1};
+  static const uint8_t kData8[] = {0xa1, 3, 0x2, 1, 1};
+  static const uint8_t kData9[] = {0xa1, 3, 0x2, 1, 0xff};
 
   CBS data, contents;
+  int present;
+  uint64_t value;
 
   CBS_init(&data, kData1, sizeof(kData1));
   if (CBS_peek_asn1_tag(&data, 0x1) ||
@@ -150,10 +157,106 @@
   }
 
   CBS_init(&data, NULL, 0);
+  /* peek at empty data. */
   if (CBS_peek_asn1_tag(&data, 0x30)) {
     return 0;
   }
 
+  CBS_init(&data, NULL, 0);
+  /* optional elements at empty data. */
+  if (!CBS_get_optional_asn1(&data, &contents, &present, 0xa0) ||
+      present ||
+      !CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0) ||
+      present ||
+      CBS_len(&contents) != 0 ||
+      !CBS_get_optional_asn1_octet_string(&data, &contents, NULL, 0xa0) ||
+      CBS_len(&contents) != 0 ||
+      !CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42) ||
+      value != 42) {
+    return 0;
+  }
+
+  CBS_init(&data, kData6, sizeof(kData6));
+  /* optional element. */
+  if (!CBS_get_optional_asn1(&data, &contents, &present, 0xa0) ||
+      present ||
+      !CBS_get_optional_asn1(&data, &contents, &present, 0xa1) ||
+      !present ||
+      CBS_len(&contents) != 3 ||
+      memcmp(CBS_data(&contents), "\x04\x01\x01", 3) != 0) {
+    return 0;
+  }
+
+  CBS_init(&data, kData6, sizeof(kData6));
+  /* optional octet string. */
+  if (!CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa0) ||
+      present ||
+      CBS_len(&contents) != 0 ||
+      !CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1) ||
+      !present ||
+      CBS_len(&contents) != 1 ||
+      CBS_data(&contents)[0] != 1) {
+    return 0;
+  }
+
+  CBS_init(&data, kData7, sizeof(kData7));
+  /* invalid optional octet string. */
+  if (CBS_get_optional_asn1_octet_string(&data, &contents, &present, 0xa1)) {
+    return 0;
+  }
+
+  CBS_init(&data, kData8, sizeof(kData8));
+  /* optional octet string. */
+  if (!CBS_get_optional_asn1_uint64(&data, &value, 0xa0, 42) ||
+      value != 42 ||
+      !CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42) ||
+      value != 1) {
+    return 0;
+  }
+
+  CBS_init(&data, kData9, sizeof(kData9));
+  /* invalid optional integer. */
+  if (CBS_get_optional_asn1_uint64(&data, &value, 0xa1, 42)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int test_get_optional_asn1_bool(void) {
+  CBS data;
+  int val;
+
+  static const uint8_t kTrue[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0xff};
+  static const uint8_t kFalse[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x00};
+  static const uint8_t kInvalid[] = {0x0a, 3, CBS_ASN1_BOOLEAN, 1, 0x01};
+
+  CBS_init(&data, NULL, 0);
+  val = 2;
+  if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) ||
+      val != 0) {
+    return 0;
+  }
+
+  CBS_init(&data, kTrue, sizeof(kTrue));
+  val = 2;
+  if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 0) ||
+      val != 1) {
+    return 0;
+  }
+
+  CBS_init(&data, kFalse, sizeof(kFalse));
+  val = 2;
+  if (!CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1) ||
+      val != 0) {
+    return 0;
+  }
+
+  CBS_init(&data, kInvalid, sizeof(kInvalid));
+  if (CBS_get_optional_asn1_bool(&data, &val, 0x0a, 1)) {
+    return 0;
+  }
+
   return 1;
 }
 
@@ -443,6 +546,89 @@
                         sizeof(kNSSBER));
 }
 
+typedef struct {
+  uint64_t value;
+  const char *encoding;
+  size_t encoding_len;
+} ASN1_UINT64_TEST;
+
+static const ASN1_UINT64_TEST kAsn1Uint64Tests[] = {
+  {0, "\x02\x01\x00", 3},
+  {1, "\x02\x01\x01", 3},
+  {127, "\x02\x01\x7f", 3},
+  {128, "\x02\x02\x00\x80", 4},
+  {0xdeadbeef, "\x02\x05\x00\xde\xad\xbe\xef", 7},
+  {OPENSSL_U64(0x0102030405060708),
+   "\x02\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10},
+  {OPENSSL_U64(0xffffffffffffffff),
+    "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11},
+};
+
+typedef struct {
+  const char *encoding;
+  size_t encoding_len;
+} ASN1_INVALID_UINT64_TEST;
+
+static const ASN1_INVALID_UINT64_TEST kAsn1InvalidUint64Tests[] = {
+  /* Bad tag. */
+  {"\x03\x01\x00", 3},
+  /* Empty contents. */
+  {"\x02\x00", 2},
+  /* Negative number. */
+  {"\x02\x01\x80", 3},
+  /* Overflow */
+  {"\x02\x09\x01\x00\x00\x00\x00\x00\x00\x00\x00", 11},
+};
+
+static int test_asn1_uint64(void) {
+  size_t i;
+
+  for (i = 0; i < sizeof(kAsn1Uint64Tests) / sizeof(kAsn1Uint64Tests[0]); i++) {
+    const ASN1_UINT64_TEST *test = &kAsn1Uint64Tests[i];
+    CBS cbs;
+    uint64_t value;
+    CBB cbb;
+    uint8_t *out;
+    size_t len;
+
+    CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len);
+    if (!CBS_get_asn1_uint64(&cbs, &value) ||
+        CBS_len(&cbs) != 0 ||
+        value != test->value) {
+      return 0;
+    }
+
+    if (!CBB_init(&cbb, 0)) {
+      return 0;
+    }
+    if (!CBB_add_asn1_uint64(&cbb, test->value) ||
+        !CBB_finish(&cbb, &out, &len)) {
+      CBB_cleanup(&cbb);
+      return 0;
+    }
+    if (len != test->encoding_len || memcmp(out, test->encoding, len) != 0) {
+      free(out);
+      return 0;
+    }
+    free(out);
+  }
+
+  for (i = 0;
+       i < sizeof(kAsn1InvalidUint64Tests) / sizeof(kAsn1InvalidUint64Tests[0]);
+       i++) {
+    const ASN1_INVALID_UINT64_TEST *test = &kAsn1InvalidUint64Tests[i];
+    CBS cbs;
+    uint64_t value;
+
+    CBS_init(&cbs, (const uint8_t *)test->encoding, test->encoding_len);
+    if (CBS_get_asn1_uint64(&cbs, &value)) {
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
 int main(void) {
   CRYPTO_library_init();
 
@@ -457,7 +643,9 @@
       !test_cbb_misuse() ||
       !test_cbb_prefixed() ||
       !test_cbb_asn1() ||
-      !test_ber_convert()) {
+      !test_ber_convert() ||
+      !test_asn1_uint64() ||
+      !test_get_optional_asn1_bool()) {
     return 1;
   }
 
diff --git a/crypto/bytestring/cbb.c b/crypto/bytestring/cbb.c
index 6767b9d..f8e1070 100644
--- a/crypto/bytestring/cbb.c
+++ b/crypto/bytestring/cbb.c
@@ -336,3 +336,39 @@
 
   return cbb_buffer_add_u(cbb->base, value, 3);
 }
+
+int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {
+  CBB child;
+  size_t i;
+  int started = 0;
+
+  if (!CBB_add_asn1(cbb, &child, CBS_ASN1_INTEGER)) {
+    return 0;
+  }
+
+  for (i = 0; i < 8; i++) {
+    uint8_t byte = (value >> 8*(7-i)) & 0xff;
+    if (!started) {
+      if (byte == 0) {
+        /* Don't encode leading zeros. */
+        continue;
+      }
+      /* If the high bit is set, add a padding byte to make it
+       * unsigned. */
+      if ((byte & 0x80) && !CBB_add_u8(&child, 0)) {
+        return 0;
+      }
+      started = 1;
+    }
+    if (!CBB_add_u8(&child, byte)) {
+      return 0;
+    }
+  }
+
+  /* 0 is encoded as a single 0, not the empty string. */
+  if (!started && !CBB_add_u8(&child, 0)) {
+    return 0;
+  }
+
+  return CBB_flush(cbb);
+}
diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c
index 07cc126..b417716 100644
--- a/crypto/bytestring/cbs.c
+++ b/crypto/bytestring/cbs.c
@@ -284,7 +284,12 @@
   data = CBS_data(&bytes);
   len = CBS_len(&bytes);
 
-  if (len > 0 && (data[0] & 0x80) != 0) {
+  if (len == 0) {
+    /* An INTEGER is encoded with at least one octet. */
+    return 0;
+  }
+
+  if ((data[0] & 0x80) != 0) {
     /* negative number */
     return 0;
   }
@@ -300,3 +305,84 @@
 
   return 1;
 }
+
+int CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present, unsigned tag) {
+  if (CBS_peek_asn1_tag(cbs, tag)) {
+    if (!CBS_get_asn1(cbs, out, tag)) {
+      return 0;
+    }
+    *out_present = 1;
+  } else {
+    *out_present = 0;
+  }
+  return 1;
+}
+
+int CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out, int *out_present,
+                                       unsigned tag) {
+  CBS child;
+  int present;
+  if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+    return 0;
+  }
+  if (present) {
+    if (!CBS_get_asn1(&child, out, CBS_ASN1_OCTETSTRING) ||
+        CBS_len(&child) != 0) {
+      return 0;
+    }
+  } else {
+    CBS_init(out, NULL, 0);
+  }
+  if (out_present) {
+    *out_present = present;
+  }
+  return 1;
+}
+
+int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out, unsigned tag,
+                                 uint64_t default_value) {
+  CBS child;
+  int present;
+  if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+    return 0;
+  }
+  if (present) {
+    if (!CBS_get_asn1_uint64(&child, out) ||
+        CBS_len(&child) != 0) {
+      return 0;
+    }
+  } else {
+    *out = default_value;
+  }
+  return 1;
+}
+
+int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag,
+                               int default_value) {
+  CBS child, child2;
+  int present;
+  if (!CBS_get_optional_asn1(cbs, &child, &present, tag)) {
+    return 0;
+  }
+  if (present) {
+    uint8_t boolean;
+
+    if (!CBS_get_asn1(&child, &child2, CBS_ASN1_BOOLEAN) ||
+        CBS_len(&child2) != 1 ||
+        CBS_len(&child) != 0) {
+      return 0;
+    }
+
+    boolean = CBS_data(&child2)[0];
+    if (boolean == 0) {
+      *out = 0;
+    } else if (boolean == 0xff) {
+      *out = 1;
+    } else {
+      return 0;
+    }
+  } else {
+    *out = default_value;
+  }
+  return 1;
+}
diff --git a/crypto/evp/digestsign.c b/crypto/evp/digestsign.c
index 08968ed..c86b805 100644
--- a/crypto/evp/digestsign.c
+++ b/crypto/evp/digestsign.c
@@ -168,22 +168,15 @@
     if (has_signctx || !r) {
       return r;
     }
-    if (EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, md, mdlen) <= 0) {
-      return 0;
-    }
+    return EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, md, mdlen);
   } else {
     if (has_signctx) {
-      if (ctx->pctx->pmeth->signctx(ctx->pctx, out_sig, out_sig_len, ctx) <= 0) {
-        return 0;
-      }
+      return ctx->pctx->pmeth->signctx(ctx->pctx, out_sig, out_sig_len, ctx);
     } else {
       size_t s = EVP_MD_size(ctx->digest);
-      if (EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, NULL, s) <= 0) {
-        return 0;
-      }
+      return EVP_PKEY_sign(ctx->pctx, out_sig, out_sig_len, NULL, s);
     }
   }
-  return 1;
 }
 
 int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
@@ -196,7 +189,7 @@
 
   EVP_MD_CTX_init(&tmp_ctx);
   if (!EVP_MD_CTX_copy_ex(&tmp_ctx, ctx)) {
-    return -1;
+    return 0;
   }
   if (has_verifyctx) {
     r = tmp_ctx.pctx->pmeth->verifyctx(tmp_ctx.pctx, sig, sig_len, &tmp_ctx);
diff --git a/crypto/evp/example_sign.c b/crypto/evp/example_sign.c
index 42a19ec..2d4c071 100644
--- a/crypto/evp/example_sign.c
+++ b/crypto/evp/example_sign.c
@@ -196,12 +196,12 @@
 
   pkey = load_example_rsa_key();
   if (pkey == NULL ||
-      EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1 ||
-      EVP_DigestSignUpdate(&md_ctx, kMsg, sizeof(kMsg)) != 1) {
+      !EVP_DigestSignInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestSignUpdate(&md_ctx, kMsg, sizeof(kMsg))) {
     goto out;
   }
   /* Determine the size of the signature. */
-  if (EVP_DigestSignFinal(&md_ctx, NULL, &sig_len) != 1) {
+  if (!EVP_DigestSignFinal(&md_ctx, NULL, &sig_len)) {
     goto out;
   }
   /* Sanity check for testing. */
@@ -211,14 +211,14 @@
   }
 
   sig = malloc(sig_len);
-  if (sig == NULL || EVP_DigestSignFinal(&md_ctx, sig, &sig_len) != 1) {
+  if (sig == NULL || !EVP_DigestSignFinal(&md_ctx, sig, &sig_len)) {
     goto out;
   }
 
   /* Ensure that the signature round-trips. */
-  if (EVP_DigestVerifyInit(&md_ctx_verify, NULL, EVP_sha256(), NULL, pkey) != 1 ||
-      EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) != 1 ||
-      EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len) != 1) {
+  if (!EVP_DigestVerifyInit(&md_ctx_verify, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len)) {
     goto out;
   }
 
@@ -250,9 +250,9 @@
 
   pkey = load_example_rsa_key();
   if (pkey == NULL ||
-      EVP_DigestVerifyInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1 ||
-      EVP_DigestVerifyUpdate(&md_ctx, kMsg, sizeof(kMsg)) != 1 ||
-      EVP_DigestVerifyFinal(&md_ctx, kSignature, sizeof(kSignature)) != 1) {
+      !EVP_DigestVerifyInit(&md_ctx, NULL, EVP_sha256(), NULL, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx, kSignature, sizeof(kSignature))) {
     goto out;
   }
   ret = 1;
@@ -282,7 +282,7 @@
 
   EVP_MD_CTX_init(&md_ctx_verify);
 
-  if (EVP_DigestSignUpdate(md_ctx, kMsg, sizeof(kMsg)) != 1) {
+  if (!EVP_DigestSignUpdate(md_ctx, kMsg, sizeof(kMsg))) {
     goto out;
   }
 
@@ -293,7 +293,7 @@
   }
 
   /* Determine the size of the signature. */
-  if (EVP_DigestSignFinal(md_ctx, NULL, &sig_len) != 1) {
+  if (!EVP_DigestSignFinal(md_ctx, NULL, &sig_len)) {
     goto out;
   }
   /* Sanity check for testing. */
@@ -303,14 +303,14 @@
   }
 
   sig = malloc(sig_len);
-  if (sig == NULL || EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
+  if (sig == NULL || !EVP_DigestSignFinal(md_ctx, sig, &sig_len)) {
     goto out;
   }
 
   /* Ensure that the signature round-trips. */
-  if (EVP_DigestVerifyInitFromAlgorithm(&md_ctx_verify, algor, pkey) != 1 ||
-      EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) != 1 ||
-      EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len) != 1) {
+  if (!EVP_DigestVerifyInitFromAlgorithm(&md_ctx_verify, algor, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx_verify, kMsg, sizeof(kMsg)) ||
+      !EVP_DigestVerifyFinal(&md_ctx_verify, sig, sig_len)) {
     goto out;
   }
 
@@ -342,7 +342,7 @@
   }
 
   /* Test a simple AlgorithmIdentifier. */
-  if (EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) != 1 ||
+  if (!EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) ||
       !test_algorithm_roundtrip(&md_ctx, pkey)) {
     fprintf(stderr, "RSA with SHA-256 failed\n");
     goto out;
@@ -352,7 +352,7 @@
   EVP_MD_CTX_init(&md_ctx);
 
   /* Test RSA-PSS with custom parameters. */
-  if (EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) != 1 ||
+  if (!EVP_DigestSignInit(&md_ctx, &pkey_ctx, EVP_sha256(), NULL, pkey) ||
       EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) != 1 ||
       EVP_PKEY_CTX_set_rsa_mgf1_md(pkey_ctx, EVP_sha512()) != 1 ||
       !test_algorithm_roundtrip(&md_ctx, pkey)) {
@@ -412,11 +412,11 @@
 
   pkey = load_example_rsa_key();
   if (pkey == NULL ||
-      EVP_DigestVerifyInitFromAlgorithm(&md_ctx, algor, pkey) != 1||
-      EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&tbs_cert),
-                             CBS_len(&tbs_cert)) != 1 ||
-      EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
-                            CBS_len(&signature)) != 1) {
+      !EVP_DigestVerifyInitFromAlgorithm(&md_ctx, algor, pkey) ||
+      !EVP_DigestVerifyUpdate(&md_ctx, CBS_data(&tbs_cert),
+                              CBS_len(&tbs_cert)) ||
+      !EVP_DigestVerifyFinal(&md_ctx, CBS_data(&signature),
+                             CBS_len(&signature))) {
     goto out;
   }
   ret = 1;
diff --git a/crypto/perlasm/x86asm.pl b/crypto/perlasm/x86asm.pl
index bab15e7..3c7be40 100644
--- a/crypto/perlasm/x86asm.pl
+++ b/crypto/perlasm/x86asm.pl
@@ -235,9 +235,9 @@
 
 sub ::asm_finish
 {   &file_end();
-    print "#if defined(__i386__)\n";
+    print "#if defined(__i386__)\n" unless $win32;
     print @out;
-    print "#endif\n";
+    print "#endif\n" unless $win32;
 }
 
 sub ::asm_init
diff --git a/crypto/perlasm/x86masm.pl b/crypto/perlasm/x86masm.pl
index 1741342..a491529 100644
--- a/crypto/perlasm/x86masm.pl
+++ b/crypto/perlasm/x86masm.pl
@@ -82,7 +82,7 @@
 IF \@Version LT 800
 ECHO MASM version 8.00 or later is strongly recommended.
 ENDIF
-.486
+.686
 .MODEL	FLAT
 OPTION	DOTNAME
 IF \@Version LT 800
@@ -166,7 +166,10 @@
 {   push(@out,("DW\t").join(',',@_)."\n");	}
 
 sub ::data_word
-{   push(@out,("DD\t").join(',',@_)."\n");	}
+{   # MASM can't handle long lines, so emit one word at a time.
+    foreach(@_)
+    {	push(@out,"DD\t$_\n");	}
+}
 
 sub ::align
 {   push(@out,"ALIGN\t$_[0]\n");	}
diff --git a/crypto/perlasm/x86nasm.pl b/crypto/perlasm/x86nasm.pl
index 5d92f60..f8332bb 100644
--- a/crypto/perlasm/x86nasm.pl
+++ b/crypto/perlasm/x86nasm.pl
@@ -83,7 +83,15 @@
 %ifidn __OUTPUT_FORMAT__,obj
 section	code	use32 class=code align=64
 %elifidn __OUTPUT_FORMAT__,win32
+%ifdef __YASM_VERSION_ID__
+%if __YASM_VERSION_ID__ < 01010000h
+%error yasm version 1.1.0 or later needed.
+%endif
+; Yasm automatically includes @feat.00 and complains about redefining it.
+; https://www.tortall.net/projects/yasm/manual/html/objfmt-win32-safeseh.html
+%else
 \$\@feat.00 equ 1
+%endif
 section	.text	code align=64
 %else
 section	.text	code
diff --git a/crypto/x509/x_crl.c b/crypto/x509/x_crl.c
index 21785dc..8e64f76 100644
--- a/crypto/x509/x_crl.c
+++ b/crypto/x509/x_crl.c
@@ -64,8 +64,24 @@
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
-#include "../asn1/asn1_locl.h"
+/* Method to handle CRL access.
+ * In general a CRL could be very large (several Mb) and can consume large
+ * amounts of resources if stored in memory by multiple processes.
+ * This method allows general CRL operations to be redirected to more
+ * efficient callbacks: for example a CRL entry database.
+ */
 
+#define X509_CRL_METHOD_DYNAMIC		1
+
+struct x509_crl_method_st
+	{
+	int flags;
+	int (*crl_init)(X509_CRL *crl);
+	int (*crl_free)(X509_CRL *crl);
+	int (*crl_lookup)(X509_CRL *crl, X509_REVOKED **ret,
+				ASN1_INTEGER *ser, X509_NAME *issuer);
+	int (*crl_verify)(X509_CRL *crl, EVP_PKEY *pk);
+	};
 
 static int X509_REVOKED_cmp(const X509_REVOKED **a,
 				const X509_REVOKED **b);
diff --git a/crypto/x509v3/v3nametest.c b/crypto/x509v3/v3nametest.c
index 6a2ea85..b2e9c09 100644
--- a/crypto/x509v3/v3nametest.c
+++ b/crypto/x509v3/v3nametest.c
@@ -53,9 +53,9 @@
  * Hudson (tjh@cryptsoft.com). */
 
 #include <string.h>
-#include <strings.h>
 
 #include <openssl/crypto.h>
+#include <openssl/mem.h>
 #include <openssl/x509.h>
 #include <openssl/x509v3.h>
 
@@ -326,7 +326,7 @@
 	const char *const *pname = names;
 	while (*pname)
 		{
-		int samename = strcasecmp(nameincert, *pname) == 0;
+		int samename = OPENSSL_strcasecmp(nameincert, *pname) == 0;
 		size_t namelen = strlen(*pname);
 		char *name = malloc(namelen);
 		int match, ret;
diff --git a/include/openssl/base.h b/include/openssl/base.h
index 52cb1e9..6207c54 100644
--- a/include/openssl/base.h
+++ b/include/openssl/base.h
@@ -159,7 +159,6 @@
 typedef struct X509_POLICY_TREE_st X509_POLICY_TREE;
 typedef struct X509_algor_st X509_ALGOR;
 typedef struct X509_crl_st X509_CRL;
-typedef struct X509_name_st X509_NAME;
 typedef struct X509_pubkey_st X509_PUBKEY;
 typedef struct bignum_ctx BN_CTX;
 typedef struct bignum_st BIGNUM;
diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h
index acaba8e..d5e6cf9 100644
--- a/include/openssl/bytestring.h
+++ b/include/openssl/bytestring.h
@@ -166,6 +166,41 @@
  * in 64 bits. */
 OPENSSL_EXPORT int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out);
 
+/* CBS_get_optional_asn1 gets an optional explicitly-tagged element
+ * from |cbs| tagged with |tag| and sets |*out| to its contents. If
+ * present, it sets |*out_present| to one, otherwise zero. It returns
+ * one on success, whether or not the element was present, and zero on
+ * decode failure. */
+OPENSSL_EXPORT int CBS_get_optional_asn1(CBS *cbs, CBS *out, int *out_present,
+                                         unsigned tag);
+
+/* CBS_get_optional_asn1_octet_string gets an optional
+ * explicitly-tagged OCTET STRING from |cbs|. If present, it sets
+ * |*out| to the string and |*out_present| to one. Otherwise, it sets
+ * |*out| to empty and |*out_present| to zero. |out_present| may be
+ * NULL. It returns one on success, whether or not the element was
+ * present, and zero on decode failure. */
+OPENSSL_EXPORT int CBS_get_optional_asn1_octet_string(CBS *cbs, CBS *out,
+                                                      int *out_present,
+                                                      unsigned tag);
+
+/* CBS_get_optional_asn1_uint64 gets an optional explicitly-tagged
+ * INTEGER from |cbs|. If present, it sets |*out| to the
+ * value. Otherwise, it sets |*out| to |default_value|. It returns one
+ * on success, whether or not the element was present, and zero on
+ * decode failure. */
+OPENSSL_EXPORT int CBS_get_optional_asn1_uint64(CBS *cbs, uint64_t *out,
+                                                unsigned tag,
+                                                uint64_t default_value);
+
+/* CBS_get_optional_asn1_bool gets an optional, explicitly-tagged BOOLEAN from
+ * |cbs|. If present, it sets |*out| to either zero or one, based on the
+ * boolean. Otherwise, it sets |*out| to |default_value|. It returns one on
+ * success, whether or not the element was present, and zero on decode
+ * failure. */
+OPENSSL_EXPORT int CBS_get_optional_asn1_bool(CBS *cbs, int *out, unsigned tag,
+                                              int default_value);
+
 
 /* CRYPTO ByteBuilder.
  *
@@ -277,6 +312,10 @@
  * returns one on success and zero otherwise. */
 OPENSSL_EXPORT int CBB_add_u24(CBB *cbb, uint32_t value);
 
+/* CBB_add_asn1_uint64 writes an ASN.1 INTEGER into |cbb| using |CBB_add_asn1|
+ * and writes |value| in its contents. It returns one on success and zero on
+ * error. */
+OPENSSL_EXPORT int CBB_add_asn1_uint64(CBB *cbb, uint64_t value);
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index e3922a3..1f60145 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -290,10 +290,7 @@
 
 /* EVP_DigestVerifyFinal verifies that |sig_len| bytes of |sig| are a valid
  * signature for the data that has been included by one or more calls to
- * |EVP_DigestVerifyUpdate|.
- *
- * It returns one on success and <= 0 on error. WARNING: this differs from the
- * usual return value convention. */
+ * |EVP_DigestVerifyUpdate|. It returns one on success and zero otherwise. */
 OPENSSL_EXPORT int EVP_DigestVerifyFinal(EVP_MD_CTX *ctx, const uint8_t *sig,
                                          size_t sig_len);
 
@@ -462,8 +459,8 @@
  * space available at |sig|. If sufficient, the signature will be written to
  * |sig| and |*sig_len| updated with the true length.
  *
- * WARNING: Setting |out| to NULL only gives the maximum size of the
- * plaintext. The actual plaintext may be smaller.
+ * WARNING: Setting |sig| to NULL only gives the maximum size of the
+ * signature. The actual signature may be smaller.
  *
  * It returns one on success or zero on error. (Note: this differs from
  * OpenSSL, which can also return negative values to indicate an error. ) */
diff --git a/include/openssl/srtp.h b/include/openssl/srtp.h
index c11608e..860362b 100644
--- a/include/openssl/srtp.h
+++ b/include/openssl/srtp.h
@@ -115,14 +115,15 @@
   Copyright (C) 2011, RTFM, Inc.
 */
 
-#ifndef HEADER_D1_SRTP_H
-#define HEADER_D1_SRTP_H
+#ifndef OPENSSL_HEADER_SRTP_H
+#define OPENSSL_HEADER_SRTP_H
 
 #ifdef  __cplusplus
 extern "C" {
 #endif
 
-     
+
+/* Constants for SRTP profiles */
 #define SRTP_AES128_CM_SHA1_80 0x0001
 #define SRTP_AES128_CM_SHA1_32 0x0002
 #define SRTP_AES128_F8_SHA1_80 0x0003
@@ -130,32 +131,47 @@
 #define SRTP_NULL_SHA1_80      0x0005
 #define SRTP_NULL_SHA1_32      0x0006
 
-/* SSL_CTX_set_tlsext_use_srtp enables SRTP for all SSL objects
- * created from |ctx|. |profile| contains a colon-separated list of
- * profile names. It returns zero on success and one on failure.
+/* SSL_CTX_set_srtp_profiles enables SRTP for all SSL objects created from
+ * |ctx|. |profile| contains a colon-separated list of profile names. It returns
+ * one on success and zero on failure. */
+OPENSSL_EXPORT int SSL_CTX_set_srtp_profiles(SSL_CTX *ctx,
+                                             const char *profiles);
+
+/* SSL_set_srtp_profiles enables SRTP for |ssl|.  |profile| contains a
+ * colon-separated list of profile names. It returns one on success and zero on
+ * failure. */
+OPENSSL_EXPORT int SSL_set_srtp_profiles(SSL *ctx, const char *profiles);
+
+/* SSL_get_srtp_profiles returns the SRTP profiles supported by |ssl|. */
+OPENSSL_EXPORT STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(
+    SSL *ssl);
+
+/* SSL_get_selected_srtp_profile returns the selected SRTP profile, or NULL if
+ * SRTP was not negotiated. */
+OPENSSL_EXPORT SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *s);
+
+
+/* Deprecated functions */
+
+/* SSL_CTX_set_tlsext_use_srtp calls SSL_CTX_set_srtp_profiles. It returns zero
+ * on success and one on failure.
  *
- * WARNING: this function is dangerous because it breaks the usual
- * return value convention. */
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use SSL_CTX_set_srtp_profiles instead. */
 OPENSSL_EXPORT int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx,
                                                const char *profiles);
 
-/* SSL_set_tlsext_use_srtp enables SRTP for |ssl| with a profile list.
- * |profile| contains a colon-separated list of profile names. It
- * returns zero on success and one on failure.
+/* SSL_set_tlsext_use_srtp calls SSL_set_srtp_profiles. It returns zero on
+ * success and one on failure.
  *
- * WARNING: this function is dangerous because it breaks the usual
- * return value convention. */
+ * WARNING: this function is dangerous because it breaks the usual return value
+ * convention. Use SSL_set_srtp_profiles instead. */
 OPENSSL_EXPORT int SSL_set_tlsext_use_srtp(SSL *ctx, const char *profiles);
 
-OPENSSL_EXPORT SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *s);
-
-OPENSSL_EXPORT STACK_OF(SRTP_PROTECTION_PROFILE) *
-    SSL_get_srtp_profiles(SSL *ssl);
-OPENSSL_EXPORT SRTP_PROTECTION_PROFILE *SSL_get_selected_srtp_profile(SSL *s);
 
 #ifdef  __cplusplus
-}
+}  /* extern C */
 #endif
 
-#endif
+#endif  /* OPENSSL_HEADER_SRTP_H */
 
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 2168613..37521bd 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -359,34 +359,8 @@
 	long (*ssl_ctx_callback_ctrl)(SSL_CTX *s, int cb_id, void (*fp)(void));
 	};
 
-/* Lets make this into an ASN.1 type structure as follows
- * SSL_SESSION_ID ::= SEQUENCE {
- *	version 		INTEGER,	-- structure version number
- *	SSLversion 		INTEGER,	-- SSL version number
- *	Cipher 			OCTET STRING,	-- the 3 byte cipher ID
- *	Session_ID 		OCTET STRING,	-- the Session ID
- *	Master_key 		OCTET STRING,	-- the master key
- *	Key_Arg [ 0 ] IMPLICIT	OCTET STRING,	-- the optional Key argument
- *	Time [ 1 ] EXPLICIT	INTEGER,	-- optional Start Time
- *	Timeout [ 2 ] EXPLICIT	INTEGER,	-- optional Timeout ins seconds
- *	Peer [ 3 ] EXPLICIT	X509,		-- optional Peer Certificate
- *	Session_ID_context [ 4 ] EXPLICIT OCTET STRING,   -- the Session ID context
- *	Verify_result [ 5 ] EXPLICIT INTEGER,   -- X509_V_... code for `Peer'
- *	HostName [ 6 ] EXPLICIT OCTET STRING,   -- optional HostName from servername TLS extension 
- *	PSK_identity_hint [ 7 ] EXPLICIT OCTET STRING, -- optional PSK identity hint
- *	PSK_identity [ 8 ] EXPLICIT OCTET STRING,  -- optional PSK identity
- *	Ticket_lifetime_hint [9] EXPLICIT INTEGER, -- server's lifetime hint for session ticket
- *	Ticket [10]             EXPLICIT OCTET STRING, -- session ticket (clients only)
- *	Compression_meth [11]   EXPLICIT OCTET STRING, -- optional compression method
- *	SRP_username [ 12 ] EXPLICIT OCTET STRING -- optional SRP username
- *	Peer SHA256 [13]        EXPLICIT OCTET STRING, -- optional SHA256 hash of Peer certifiate
- *	original handshake hash [14] EXPLICIT OCTET STRING, -- optional original handshake hash
- *	tlsext_signed_cert_timestamp_list [15] EXPLICIT OCTET STRING, -- optional signed cert timestamp list extension
- *	ocsp_response [16] EXPLICIT OCTET STRING, -- optional saved OCSP response from the server
- *	}
- * Look in ssl/ssl_asn1.c for more details
- * I'm using EXPLICIT tags so I can read the damn things using asn1parse :-).
- */
+/* An SSL_SESSION represents an SSL session that may be resumed in an
+ * abbreviated handshake. */
 struct ssl_session_st
 	{
 	int ssl_version;	/* what ssl version session info is
@@ -458,6 +432,11 @@
 	 * resumption. */
 	unsigned char original_handshake_hash[EVP_MAX_MD_SIZE];
 	unsigned int original_handshake_hash_len;
+
+	/* extended_master_secret is true if the master secret in this session
+	 * was generated using EMS and thus isn't vulnerable to the Triple
+	 * Handshake attack. */
+	char extended_master_secret;
 	};
 
 #endif
@@ -1978,18 +1957,52 @@
 OPENSSL_EXPORT int	SSL_SESSION_print(BIO *fp,const SSL_SESSION *ses);
 #endif
 OPENSSL_EXPORT void	SSL_SESSION_free(SSL_SESSION *ses);
-OPENSSL_EXPORT int	i2d_SSL_SESSION(SSL_SESSION *in,unsigned char **pp);
 OPENSSL_EXPORT int	SSL_set_session(SSL *to, SSL_SESSION *session);
 OPENSSL_EXPORT int	SSL_CTX_add_session(SSL_CTX *s, SSL_SESSION *c);
 OPENSSL_EXPORT int	SSL_CTX_remove_session(SSL_CTX *,SSL_SESSION *c);
 OPENSSL_EXPORT int	SSL_CTX_set_generate_session_id(SSL_CTX *, GEN_SESSION_CB);
 OPENSSL_EXPORT int	SSL_set_generate_session_id(SSL *, GEN_SESSION_CB);
 OPENSSL_EXPORT int	SSL_has_matching_session_id(const SSL *ssl, const unsigned char *id, unsigned int id_len);
-OPENSSL_EXPORT SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a,const unsigned char **pp, long length);
 
-#ifdef HEADER_X509_H
+/* SSL_SESSION_to_bytes serializes |in| into a newly allocated buffer
+ * and sets |*out_data| to that buffer and |*out_len| to its
+ * length. The caller takes ownership of the buffer and must call
+ * |OPENSSL_free| when done. It returns one on success and zero on
+ * error. */
+OPENSSL_EXPORT int SSL_SESSION_to_bytes(SSL_SESSION *in, uint8_t **out_data,
+                                        size_t *out_len);
+
+/* SSL_SESSION_to_bytes_for_ticket serializes |in|, but excludes the
+ * session ID which is not necessary in a session ticket. */
+OPENSSL_EXPORT int SSL_SESSION_to_bytes_for_ticket(SSL_SESSION *in,
+                                                   uint8_t **out_data,
+                                                   size_t *out_len);
+
+/* Deprecated: i2d_SSL_SESSION serializes |in| to the bytes pointed to
+ * by |*pp|. On success, it returns the number of bytes written and
+ * advances |*pp| by that many bytes. On failure, it returns -1. If
+ * |pp| is NULL, no bytes are written and only the length is
+ * returned.
+ *
+ * Use SSL_SESSION_to_bytes instead. */
+OPENSSL_EXPORT int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp);
+
+/* d2i_SSL_SESSION deserializes a serialized buffer contained in the
+ * |length| bytes pointed to by |*pp|. It returns the new SSL_SESSION
+ * and advances |*pp| by the number of bytes consumed on success and
+ * NULL on failure. If |a| is NULL, the caller takes ownership of the
+ * new session and must call |SSL_SESSION_free| when done.
+ *
+ * If |a| and |*a| are not NULL, the SSL_SESSION at |*a| is overridden
+ * with the deserialized session rather than allocating a new one. In
+ * addition, |a| is not NULL, but |*a| is, |*a| is set to the new
+ * SSL_SESSION.
+ *
+ * Passing a value other than NULL to |a| is deprecated. */
+OPENSSL_EXPORT SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp,
+                                            long length);
+
 OPENSSL_EXPORT X509 *	SSL_get_peer_certificate(const SSL *s);
-#endif
 
 OPENSSL_EXPORT STACK_OF(X509) *SSL_get_peer_cert_chain(const SSL *s);
 
@@ -2461,6 +2474,12 @@
 #define SSL_F_ssl3_cert_verify_hash 284
 #define SSL_F_ssl_ctx_log_rsa_client_key_exchange 285
 #define SSL_F_ssl_ctx_log_master_secret 286
+#define SSL_F_d2i_SSL_SESSION 287
+#define SSL_F_i2d_SSL_SESSION 288
+#define SSL_F_d2i_SSL_SESSION_get_octet_string 289
+#define SSL_F_d2i_SSL_SESSION_get_string 290
+#define SSL_F_ssl3_send_new_session_ticket 291
+#define SSL_F_SSL_SESSION_to_bytes_full 292
 #define SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS 100
 #define SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC 101
 #define SSL_R_INVALID_NULL_CMD_NAME 102
@@ -2774,6 +2793,7 @@
 #define SSL_R_UNPROCESSED_HANDSHAKE_DATA 440
 #define SSL_R_HANDSHAKE_RECORD_BEFORE_CCS 441
 #define SSL_R_SESSION_MAY_NOT_BE_CREATED 442
+#define SSL_R_INVALID_SSL_SESSION 443
 #define SSL_R_SSLV3_ALERT_CLOSE_NOTIFY 1000
 #define SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE 1010
 #define SSL_R_SSLV3_ALERT_BAD_RECORD_MAC 1020
diff --git a/include/openssl/ssl3.h b/include/openssl/ssl3.h
index 2a201aa..3d62763 100644
--- a/include/openssl/ssl3.h
+++ b/include/openssl/ssl3.h
@@ -485,6 +485,15 @@
 		 * this extension to the client. */
 		uint16_t *peer_ellipticcurvelist;
 		size_t peer_ellipticcurvelist_length;
+
+		/* extended_master_secret indicates whether the extended master
+		 * secret computation is used in this handshake. Note that this
+		 * is different from whether it was used for the current
+		 * session. If this is a resumption handshake then EMS might be
+		 * negotiated in the client and server hello messages, but it
+		 * doesn't matter if the session that's being resumed didn't
+		 * use it to create the master secret initially. */
+		char extended_master_secret;
 		} tmp;
 
         /* Connection binding to prevent renegotiation attacks */
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 08ad8e8..d2682dd 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -240,6 +240,9 @@
  */
 #define TLSEXT_TYPE_padding	21
 
+/* https://tools.ietf.org/html/draft-ietf-tls-session-hash-01 */
+#define TLSEXT_TYPE_extended_master_secret	23
+
 /* ExtensionType value from RFC4507 */
 #define TLSEXT_TYPE_session_ticket		35
 
@@ -704,6 +707,8 @@
 #define TLS_MD_IV_BLOCK_CONST_SIZE		8
 #define TLS_MD_MASTER_SECRET_CONST		"master secret"
 #define TLS_MD_MASTER_SECRET_CONST_SIZE		13
+#define TLS_MD_EXTENDED_MASTER_SECRET_CONST	"extended master secret"
+#define TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE	22
 
 
 /* TLS Session Ticket extension struct */
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index 398bec7..9766f74 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -143,7 +143,7 @@
 DECLARE_ASN1_SET_OF(X509_NAME_ENTRY)
 
 /* we always keep X509_NAMEs in 2 forms. */
-struct X509_name_st
+typedef struct X509_name_st
 	{
 	STACK_OF(X509_NAME_ENTRY) *entries;
 	int modified;	/* true if 'bytes' needs to be built */
@@ -155,7 +155,7 @@
 /*	unsigned long hash; Keep the hash around for lookups */
 	unsigned char *canon_enc;
 	int canon_enclen;
-	} /* X509_NAME */;
+	} X509_NAME;
 
 DECLARE_STACK_OF(X509_NAME)
 
diff --git a/ssl/d1_both.c b/ssl/d1_both.c
index 2d944d8..8b225e5 100644
--- a/ssl/d1_both.c
+++ b/ssl/d1_both.c
@@ -229,7 +229,7 @@
 	}
 
 /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */
-int dtls1_do_write(SSL *s, int type)
+int dtls1_do_write(SSL *s, int type, enum should_add_to_finished_hash should_add_to_finished_hash)
 	{
 	int ret;
 	int curr_mtu;
@@ -365,7 +365,8 @@
 			 * message got sent.  but why would this happen? */
 			assert(len == (unsigned int)ret);
 
-			if (type == SSL3_RT_HANDSHAKE && ! s->d1->retransmitting)
+			if (type == SSL3_RT_HANDSHAKE && !s->d1->retransmitting &&
+			    should_add_to_finished_hash == add_to_finished_hash)
 				{
 				/* should not be done for 'Hello Request's, but in that case
 				 * we'll ignore the result anyway */
@@ -967,7 +968,7 @@
 		}
 
 	/* SSL3_ST_CW_CHANGE_B */
-	return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC));
+	return(dtls1_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC, dont_add_to_finished_hash));
 	}
 
 int dtls1_read_failed(SSL *s, int code)
@@ -1181,7 +1182,7 @@
 	}
 	
 	ret = dtls1_do_write(s, frag->msg_header.is_ccs ? 
-						 SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE);
+						 SSL3_RT_CHANGE_CIPHER_SPEC : SSL3_RT_HANDSHAKE, add_to_finished_hash);
 	
 	/* restore current state */
 	s->enc_write_ctx = saved_state.enc_write_ctx;
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 96ce496..650d8e7 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -74,8 +74,9 @@
 static void get_current_time(OPENSSL_timeval *t);
 static OPENSSL_timeval* dtls1_get_timeout(SSL *s, OPENSSL_timeval* timeleft);
 static void dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
-static int dtls1_handshake_write(SSL *s);
+static int dtls1_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash);
 int dtls1_listen(SSL *s, struct sockaddr *client);
+static void dtls1_add_to_finished_hash(SSL *s);
 
 SSL3_ENC_METHOD DTLSv1_enc_data={
     	tls1_enc,
@@ -93,7 +94,8 @@
 	SSL_ENC_FLAG_DTLS|SSL_ENC_FLAG_EXPLICIT_IV,
 	DTLS1_HM_HEADER_LENGTH,
 	dtls1_set_handshake_header,
-	dtls1_handshake_write	
+	dtls1_handshake_write,
+	dtls1_add_to_finished_hash,
 	};
 
 SSL3_ENC_METHOD DTLSv1_2_enc_data={
@@ -113,7 +115,8 @@
 		|SSL_ENC_FLAG_SHA256_PRF|SSL_ENC_FLAG_TLS1_2_CIPHERS,
 	DTLS1_HM_HEADER_LENGTH,
 	dtls1_set_handshake_header,
-	dtls1_handshake_write	
+	dtls1_handshake_write,
+	dtls1_add_to_finished_hash,
 	};
 
 int dtls1_new(SSL *s)
@@ -502,7 +505,25 @@
 	dtls1_buffer_message(s, 0);
 	}
 
-static int dtls1_handshake_write(SSL *s)
+static int dtls1_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash)
 	{
-	return dtls1_do_write(s, SSL3_RT_HANDSHAKE);
+	return dtls1_do_write(s, SSL3_RT_HANDSHAKE, should_add_to_finished_hash);
+	}
+
+static void dtls1_add_to_finished_hash(SSL *s)
+	{
+	uint8_t *record = (uint8_t *) &s->init_buf->data[s->init_off];
+	const struct hm_header_st *msg_hdr = &s->d1->w_msg_hdr;
+	uint8_t serialised_header[DTLS1_HM_HEADER_LENGTH];
+	uint8_t *p = serialised_header;
+
+	/* Construct the message header as if it were a single fragment. */
+	*p++ = msg_hdr->type;
+	l2n3(msg_hdr->msg_len, p);
+	s2n (msg_hdr->seq, p);
+	l2n3(0, p);
+	l2n3(msg_hdr->msg_len, p);
+	ssl3_finish_mac(s, serialised_header, sizeof(serialised_header));
+	ssl3_finish_mac(s, record + DTLS1_HM_HEADER_LENGTH,
+			s->init_num - DTLS1_HM_HEADER_LENGTH);
 	}
diff --git a/ssl/d1_srtp.c b/ssl/d1_srtp.c
index bc278c3..69b11ad 100644
--- a/ssl/d1_srtp.c
+++ b/ssl/d1_srtp.c
@@ -114,8 +114,6 @@
   Copyright (C) 2011, RTFM, Inc.
 */
 
-#ifndef OPENSSL_NO_SRTP
-
 #include <stdio.h>
 
 #include <openssl/bytestring.h>
@@ -226,20 +224,17 @@
     
 	return 1;
 	}
-    
-int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx,const char *profiles)
+
+int SSL_CTX_set_srtp_profiles(SSL_CTX *ctx, const char *profiles)
 	{
-	/* This API inverts its return value. */
-	return !ssl_ctx_make_profiles(profiles,&ctx->srtp_profiles);
+	return ssl_ctx_make_profiles(profiles, &ctx->srtp_profiles);
 	}
 
-int SSL_set_tlsext_use_srtp(SSL *s,const char *profiles)
+int SSL_set_srtp_profiles(SSL *s, const char *profiles)
 	{
-	/* This API inverts its return value. */
-	return !ssl_ctx_make_profiles(profiles,&s->srtp_profiles);
+	return ssl_ctx_make_profiles(profiles, &s->srtp_profiles);
 	}
 
-
 STACK_OF(SRTP_PROTECTION_PROFILE) *SSL_get_srtp_profiles(SSL *s)
 	{
 	if(s != NULL)
@@ -263,6 +258,18 @@
 	return s->srtp_profile;
 	}
 
+int SSL_CTX_set_tlsext_use_srtp(SSL_CTX *ctx, const char *profiles)
+	{
+	/* This API inverts its return value. */
+	return !SSL_CTX_set_srtp_profiles(ctx, profiles);
+	}
+
+int SSL_set_tlsext_use_srtp(SSL *s, const char *profiles)
+	{
+	/* This API inverts its return value. */
+	return !SSL_set_srtp_profiles(s, profiles);
+	}
+
 /* Note: this function returns 0 length if there are no 
    profiles specified */
 int ssl_add_clienthello_use_srtp_ext(SSL *s, unsigned char *p, int *len, int maxlen)
@@ -464,6 +471,3 @@
 	*out_alert = SSL_AD_ILLEGAL_PARAMETER;
 	return 0;
 	}
-
-
-#endif
diff --git a/ssl/d1_srvr.c b/ssl/d1_srvr.c
index 79da484..e1c5616 100644
--- a/ssl/d1_srvr.c
+++ b/ssl/d1_srvr.c
@@ -687,5 +687,5 @@
 		}
 
 	/* s->state = DTLS1_ST_SW_HELLO_VERIFY_REQUEST_B */
-	return(dtls1_do_write(s,SSL3_RT_HANDSHAKE));
+	return(dtls1_do_write(s,SSL3_RT_HANDSHAKE, add_to_finished_hash));
 	}
diff --git a/ssl/s3_both.c b/ssl/s3_both.c
index 6604fc7..bbdeadb 100644
--- a/ssl/s3_both.c
+++ b/ssl/s3_both.c
@@ -127,17 +127,19 @@
 #include "ssl_locl.h"
 
 /* send s->init_buf in records of type 'type' (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC) */
-int ssl3_do_write(SSL *s, int type)
+int ssl3_do_write(SSL *s, int type, enum should_add_to_finished_hash should_add_to_finished_hash)
 	{
 	int ret;
 
 	ret=ssl3_write_bytes(s,type,&s->init_buf->data[s->init_off],
 	                     s->init_num);
 	if (ret < 0) return(-1);
-	if (type == SSL3_RT_HANDSHAKE)
+	if (type == SSL3_RT_HANDSHAKE && should_add_to_finished_hash == add_to_finished_hash)
+		{
 		/* should not be done for 'Hello Request's, but in that case
 		 * we'll ignore the result anyway */
 		ssl3_finish_mac(s,(unsigned char *)&s->init_buf->data[s->init_off],ret);
+		}
 	
 	if (ret == s->init_num)
 		{
@@ -320,7 +322,7 @@
 		}
 
 	/* SSL3_ST_CW_CHANGE_B */
-	return(ssl3_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC));
+	return(ssl3_do_write(s,SSL3_RT_CHANGE_CIPHER_SPEC, dont_add_to_finished_hash));
 	}
 
 unsigned long ssl3_output_cert_chain(SSL *s, CERT_PKEY *cpk)
diff --git a/ssl/s3_clnt.c b/ssl/s3_clnt.c
index 6574f5a..355cb0e 100644
--- a/ssl/s3_clnt.c
+++ b/ssl/s3_clnt.c
@@ -982,7 +982,7 @@
 	/* Don't digest cached records if no sigalgs: we may need them for
 	 * client authentication.
 	 */
-	if (!SSL_USE_SIGALGS(s) && !ssl3_digest_cached_records(s))
+	if (!SSL_USE_SIGALGS(s) && !ssl3_digest_cached_records(s, free_handshake_buffer))
 		goto f_err;
 
 	/* Only the NULL compression algorithm is supported. */
@@ -1637,7 +1637,7 @@
 		 */
 		if (s->s3->handshake_buffer)
 			{
-			if (!ssl3_digest_cached_records(s))
+			if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 				goto err;
 			}
 		return(1);
@@ -1909,6 +1909,8 @@
 	BN_CTX * bn_ctx = NULL;
 	unsigned int psk_len = 0;
 	unsigned char psk[PSK_MAX_PSK_LEN];
+	uint8_t *pms = NULL;
+	size_t pms_len = 0;
 
 	if (s->state == SSL3_ST_CW_KEY_EXCH_A)
 		{
@@ -1917,16 +1919,12 @@
 		alg_k=s->s3->tmp.new_cipher->algorithm_mkey;
 		alg_a=s->s3->tmp.new_cipher->algorithm_auth;
 
+		/* If using a PSK key exchange, prepare the pre-shared key. */
 		if (alg_a & SSL_aPSK)
 			{
 			char identity[PSK_MAX_IDENTITY_LEN + 1];
 			size_t identity_len;
-			unsigned char *t = NULL;
-			unsigned char pre_ms[PSK_MAX_PSK_LEN*2+4];
-			unsigned int pre_ms_len = 0;
-			int psk_err = 1;
 
-			n = 0;
 			if (s->psk_client_callback == NULL)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, SSL_R_PSK_NO_CLIENT_CB);
@@ -1939,40 +1937,19 @@
 			if (psk_len > PSK_MAX_PSK_LEN)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_INTERNAL_ERROR);
-				goto psk_err;
+				goto err;
 				}
 			else if (psk_len == 0)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, SSL_R_PSK_IDENTITY_NOT_FOUND);
-				goto psk_err;
+				ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
+				goto err;
 				}
 			identity_len = OPENSSL_strnlen(identity, sizeof(identity));
 			if (identity_len > PSK_MAX_IDENTITY_LEN)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_INTERNAL_ERROR);
-				goto psk_err;
-				}
-
-			if (!(alg_k & SSL_kEECDH))
-				{
-				/* Create the shared secret now if we're not using ECDHE-PSK.
-				 * TODO(davidben): Refactor this logic similarly
-				 * to ssl3_get_client_key_exchange. */
-				pre_ms_len = 2+psk_len+2+psk_len;
-				t = pre_ms;
-				s2n(psk_len, t);
-				memset(t, 0, psk_len);
-				t+=psk_len;
-				s2n(psk_len, t);
-				memcpy(t, psk, psk_len);
-
-				s->session->master_key_length =
-					s->method->ssl3_enc->generate_master_secret(s,
-						s->session->master_key,
-						pre_ms, pre_ms_len);
-				s2n(identity_len, p);
-				memcpy(p, identity, identity_len);
-				n = 2 + identity_len;
+				goto err;
 				}
 
 			if (s->session->psk_identity != NULL)
@@ -1981,23 +1958,30 @@
 			if (s->session->psk_identity == NULL)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
-				goto psk_err;
-				}
-			psk_err = 0;
-		psk_err:
-			OPENSSL_cleanse(identity, sizeof(identity));
-			OPENSSL_cleanse(pre_ms, sizeof(pre_ms));
-			if (psk_err != 0)
-				{
-				ssl3_send_alert(s, SSL3_AL_FATAL, SSL_AD_HANDSHAKE_FAILURE);
 				goto err;
 				}
+
+			/* Write out psk_identity. */
+			s2n(identity_len, p);
+			memcpy(p, identity, identity_len);
+			p += identity_len;
+			n = 2 + identity_len;
 			}
 
+		/* Depending on the key exchange method, compute |pms|
+		 * and |pms_len|. */
 		if (alg_k & SSL_kRSA)
 			{
 			RSA *rsa;
-			unsigned char tmp_buf[SSL_MAX_MASTER_KEY_LENGTH];
+			size_t enc_pms_len;
+
+			pms_len = SSL_MAX_MASTER_KEY_LENGTH;
+			pms = OPENSSL_malloc(pms_len);
+			if (pms == NULL)
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
 
 			if (s->session->sess_cert == NULL)
 				{
@@ -2022,49 +2006,47 @@
 				EVP_PKEY_free(pkey);
 				}
 				
-			tmp_buf[0]=s->client_version>>8;
-			tmp_buf[1]=s->client_version&0xff;
-			if (RAND_bytes(&(tmp_buf[2]),sizeof tmp_buf-2) <= 0)
+			pms[0]=s->client_version>>8;
+			pms[1]=s->client_version&0xff;
+			if (RAND_bytes(&pms[2],SSL_MAX_MASTER_KEY_LENGTH-2) <= 0)
 					goto err;
 
-			s->session->master_key_length=sizeof tmp_buf;
+			s->session->master_key_length=SSL_MAX_MASTER_KEY_LENGTH;
 
 			q=p;
-			/* Fix buf for TLS and beyond */
+			/* In TLS and beyond, reserve space for the length prefix. */
 			if (s->version > SSL3_VERSION)
-				p+=2;
-			n=RSA_public_encrypt(sizeof tmp_buf,
-				tmp_buf,p,rsa,RSA_PKCS1_PADDING);
-			if (n <= 0)
+				{
+				p += 2;
+				n += 2;
+				}
+			if (!RSA_encrypt(rsa, &enc_pms_len, p, RSA_size(rsa),
+					pms, pms_len, RSA_PKCS1_PADDING))
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, SSL_R_BAD_RSA_ENCRYPT);
 				goto err;
 				}
+			n += enc_pms_len;
 
 			/* Log the premaster secret, if logging is enabled. */
 			if (!ssl_ctx_log_rsa_client_key_exchange(s->ctx,
-					p, n, tmp_buf, sizeof(tmp_buf)))
+					p, enc_pms_len, pms, pms_len))
 				{
 				goto err;
 				}
 
-			/* Fix buf for TLS and beyond */
+			/* Fill in the length prefix. */
 			if (s->version > SSL3_VERSION)
 				{
-				s2n(n,q);
-				n+=2;
+				s2n(enc_pms_len, q);
 				}
-
-			s->session->master_key_length=
-				s->method->ssl3_enc->generate_master_secret(s,
-					s->session->master_key,
-					tmp_buf,sizeof tmp_buf);
-			OPENSSL_cleanse(tmp_buf,sizeof tmp_buf);
 			}
 		else if (alg_k & SSL_kEDH)
 			{
-			DH *dh_srvr,*dh_clnt;
+			DH *dh_srvr, *dh_clnt;
 			SESS_CERT *scert = s->session->sess_cert;
+			int dh_len;
+			size_t pub_len;
 
 			if (scert == NULL) 
 				{
@@ -2093,44 +2075,38 @@
 				goto err;
 				}
 
-			/* use the 'p' output buffer for the DH key, but
-			 * make sure to clear it out afterwards */
+			pms_len = DH_size(dh_clnt);
+			pms = OPENSSL_malloc(pms_len);
+			if (pms == NULL)
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
+				DH_free(dh_clnt);
+				goto err;
+				}
 
-			n=DH_compute_key(p,dh_srvr->pub_key,dh_clnt);
-			if (n <= 0)
+			dh_len = DH_compute_key(pms, dh_srvr->pub_key, dh_clnt);
+			if (dh_len <= 0)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_DH_LIB);
 				DH_free(dh_clnt);
 				goto err;
 				}
-
-			/* generate master key from the result */
-			s->session->master_key_length=
-				s->method->ssl3_enc->generate_master_secret(s,
-					s->session->master_key,p,n);
-			/* clean up */
-			memset(p,0,n);
+			pms_len = dh_len;
 
 			/* send off the data */
-			n=BN_num_bytes(dh_clnt->pub_key);
-			s2n(n,p);
-			BN_bn2bin(dh_clnt->pub_key,p);
-			n+=2;
+			pub_len = BN_num_bytes(dh_clnt->pub_key);
+			s2n(pub_len, p);
+			BN_bn2bin(dh_clnt->pub_key, p);
+			n += 2 + pub_len;
 
 			DH_free(dh_clnt);
-
-			/* perhaps clean things up a bit EAY EAY EAY EAY*/
 			}
 
 		else if (alg_k & SSL_kEECDH)
 			{
 			const EC_GROUP *srvr_group = NULL;
 			EC_KEY *tkey;
-			int field_size = 0;
-			unsigned char *pre_ms;
-			unsigned char *t;
-			unsigned int pre_ms_len;
-			unsigned int i;
+			int field_size = 0, ecdh_len;
 
 			if (s->session->sess_cert == NULL) 
 				{
@@ -2173,54 +2149,28 @@
 				goto err;
 				}
 
-			/* use the 'p' output buffer for the ECDH key, but
-			 * make sure to clear it out afterwards
-			 */
-
 			field_size = EC_GROUP_get_degree(srvr_group);
 			if (field_size <= 0)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB);
 				goto err;
 				}
-			n=ECDH_compute_key(p, (field_size+7)/8, srvr_ecpoint, clnt_ecdh, NULL);
-			if (n <= 0)
+
+			pms_len = (field_size + 7) / 8;
+			pms = OPENSSL_malloc(pms_len);
+			if (pms == NULL)
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+
+			ecdh_len = ECDH_compute_key(pms, pms_len, srvr_ecpoint, clnt_ecdh, NULL);
+			if (ecdh_len <= 0)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_ECDH_LIB);
 				goto err;
 				}
-
-			/* ECDHE PSK ciphersuites from RFC 5489 */
-			if ((alg_a & SSL_aPSK) && psk_len != 0)
-				{
-				pre_ms_len = 2+psk_len+2+n;
-				pre_ms = OPENSSL_malloc(pre_ms_len);
-				if (pre_ms == NULL)
-					{
-					OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
-					goto err;
-					}
-				memset(pre_ms, 0, pre_ms_len);
-				t = pre_ms;
-				s2n(psk_len, t);
-				memcpy(t, psk, psk_len);
-				t += psk_len;
-				s2n(n, t);
-				memcpy(t, p, n);
-				s->session->master_key_length = s->method->ssl3_enc \
-					-> generate_master_secret(s,
-						s->session->master_key, pre_ms, pre_ms_len);
-				OPENSSL_cleanse(pre_ms, pre_ms_len);
-				OPENSSL_free(pre_ms);
-				}
-			if (!(alg_a & SSL_aPSK))
-				{
-				/* generate master key from the result */
-				s->session->master_key_length = s->method->ssl3_enc \
-					-> generate_master_secret(s,
-						s->session->master_key, p, n);
-				}
-			memset(p, 0, n); /* clean up */
+			pms_len = ecdh_len;
 
 			/* First check the size of encoding and
 			 * allocate memory accordingly.
@@ -2248,32 +2198,39 @@
 				POINT_CONVERSION_UNCOMPRESSED,
 				encodedPoint, encoded_pt_len, bn_ctx);
 
-			n = 0;
-			if ((alg_a & SSL_aPSK) && psk_len != 0)
-				{
-				i = strlen(s->session->psk_identity);
-				s2n(i, p);
-				memcpy(p, s->session->psk_identity, i);
-				p += i;
-				n = i + 2;
-				}
-
 			*p = encoded_pt_len; /* length of encoded point */
 			/* Encoded point will be copied here */
 			p += 1;
 			n += 1;
 			/* copy the point */
-			memcpy((unsigned char *)p, encodedPoint, encoded_pt_len);
+			memcpy(p, encodedPoint, encoded_pt_len);
 			/* increment n to account for length field */
 			n += encoded_pt_len;
 
 			/* Free allocated memory */
 			BN_CTX_free(bn_ctx);
+			bn_ctx = NULL;
 			OPENSSL_free(encodedPoint);
+			encodedPoint = NULL;
 			EC_KEY_free(clnt_ecdh);
+			clnt_ecdh = NULL;
 			EVP_PKEY_free(srvr_pub_pkey);
+			srvr_pub_pkey = NULL;
 			}
-		else if (!(alg_k & SSL_kPSK) || ((alg_k & SSL_kPSK) && !(alg_a & SSL_aPSK)))
+		else if (alg_k & SSL_kPSK)
+			{
+			/* For plain PSK, other_secret is a block of 0s with the same
+			 * length as the pre-shared key. */
+			pms_len = psk_len;
+			pms = OPENSSL_malloc(pms_len);
+			if (pms == NULL)
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			memset(pms, 0, pms_len);
+			}
+		else
 			{
 			ssl3_send_alert(s, SSL3_AL_FATAL,
 			    SSL_AD_HANDSHAKE_FAILURE);
@@ -2281,19 +2238,71 @@
 			goto err;
 			}
 
+		/* For a PSK cipher suite, other_secret is combined
+		 * with the pre-shared key. */
+		if ((alg_a & SSL_aPSK) && psk_len != 0)
+			{
+			CBB cbb, child;
+			uint8_t *new_pms;
+			size_t new_pms_len;
+
+			if (!CBB_init(&cbb, 2 + psk_len + 2 + pms_len))
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_MALLOC_FAILURE);
+				goto err;
+				}
+			if (!CBB_add_u16_length_prefixed(&cbb, &child) ||
+				!CBB_add_bytes(&child, pms, pms_len) ||
+				!CBB_add_u16_length_prefixed(&cbb, &child) ||
+				!CBB_add_bytes(&child, psk, psk_len) ||
+				!CBB_finish(&cbb, &new_pms, &new_pms_len))
+				{
+				CBB_cleanup(&cbb);
+				OPENSSL_PUT_ERROR(SSL, ssl3_send_client_key_exchange, ERR_R_INTERNAL_ERROR);
+				goto err;
+				}
+			OPENSSL_cleanse(pms, pms_len);
+			OPENSSL_free(pms);
+			pms = new_pms;
+			pms_len = new_pms_len;
+			}
+
 		ssl_set_handshake_header(s, SSL3_MT_CLIENT_KEY_EXCHANGE, n);
 		s->state=SSL3_ST_CW_KEY_EXCH_B;
+
+		/* The message must be added to the finished hash before
+		 * calculating the master secret. */
+		s->method->ssl3_enc->add_to_finished_hash(s);
+
+		s->session->master_key_length =
+			s->method->ssl3_enc->generate_master_secret(s,
+				s->session->master_key,
+				pms, pms_len);
+		if (s->session->master_key_length == 0)
+			{
+			goto err;
+			}
+		s->session->extended_master_secret = s->s3->tmp.extended_master_secret;
+		OPENSSL_cleanse(pms, pms_len);
+		OPENSSL_free(pms);
 		}
 
 	/* SSL3_ST_CW_KEY_EXCH_B */
-	return ssl_do_write(s);
+	/* The message has already been added to the finished hash. */
+	return s->method->ssl3_enc->do_write(s, dont_add_to_finished_hash);
+
 err:
 	BN_CTX_free(bn_ctx);
 	if (encodedPoint != NULL) OPENSSL_free(encodedPoint);
 	if (clnt_ecdh != NULL) 
 		EC_KEY_free(clnt_ecdh);
 	EVP_PKEY_free(srvr_pub_pkey);
-	return(-1);
+	if (pms)
+		{
+		OPENSSL_cleanse(pms, pms_len);
+		OPENSSL_free(pms);
+		}
+	return -1;
 	}
 
 int ssl3_send_cert_verify(SSL *s)
@@ -2332,7 +2341,7 @@
 			goto err;
 
 		/* The handshake buffer is no longer necessary. */
-		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s))
+		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer))
 			goto err;
 
 		/* Sign the digest. */
@@ -2583,7 +2592,7 @@
 		s->init_off = 0;
 		}
 
-	return ssl3_do_write(s, SSL3_RT_HANDSHAKE);
+	return ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash);
 }
 
 
@@ -2597,7 +2606,7 @@
 	unsigned char *public_key = NULL, *derp, *der_sig = NULL;
 
 	if (s->state != SSL3_ST_CW_CHANNEL_ID_A)
-		return ssl3_do_write(s, SSL3_RT_HANDSHAKE);
+		return ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash);
 
 	if (!s->tlsext_channel_id_private && s->ctx->channel_id_cb)
 		{
@@ -2700,7 +2709,7 @@
 	s->init_num = 4 + 2 + 2 + TLSEXT_CHANNEL_ID_SIZE;
 	s->init_off = 0;
 
-	ret = ssl3_do_write(s, SSL3_RT_HANDSHAKE);
+	ret = ssl3_do_write(s, SSL3_RT_HANDSHAKE, add_to_finished_hash);
 
 err:
 	EVP_MD_CTX_cleanup(&md_ctx);
diff --git a/ssl/s3_enc.c b/ssl/s3_enc.c
index b0ca507..fc94a94 100644
--- a/ssl/s3_enc.c
+++ b/ssl/s3_enc.c
@@ -510,7 +510,7 @@
 		}	
 	}
 
-int ssl3_digest_cached_records(SSL *s)
+int ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t should_free_handshake_buffer)
 	{
 	int i;
 	long mask;
@@ -542,9 +542,13 @@
 			s->s3->handshake_dgst[i]=NULL;
 			}
 		}
-	/* Free handshake_buffer BIO */
-	BIO_free(s->s3->handshake_buffer);
-	s->s3->handshake_buffer = NULL;
+
+	if (should_free_handshake_buffer == free_handshake_buffer)
+		{
+		/* Free handshake_buffer BIO */
+		BIO_free(s->s3->handshake_buffer);
+		s->s3->handshake_buffer = NULL;
+		}
 
 	return 1;
 	}
@@ -581,7 +585,7 @@
 	EVP_MD_CTX ctx,*d=NULL;
 
 	if (s->s3->handshake_buffer) 
-		if (!ssl3_digest_cached_records(s))
+		if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 			return 0;
 
 	/* Search for digest of specified type in the handshake_dgst
diff --git a/ssl/s3_lib.c b/ssl/s3_lib.c
index 215b3f6..7721dec 100644
--- a/ssl/s3_lib.c
+++ b/ssl/s3_lib.c
@@ -942,7 +942,8 @@
 	0,
 	SSL3_HM_HEADER_LENGTH,
 	ssl3_set_handshake_header,
-	ssl3_handshake_write
+	ssl3_handshake_write,
+	ssl3_add_to_finished_hash,
 	};
 
 int ssl3_num_ciphers(void)
@@ -975,9 +976,14 @@
 	s->init_off = 0;
 	}
 
-int ssl3_handshake_write(SSL *s)
+int ssl3_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash)
 	{
-	return ssl3_do_write(s, SSL3_RT_HANDSHAKE);
+	return ssl3_do_write(s, SSL3_RT_HANDSHAKE, should_add_to_finished_hash);
+	}
+
+void ssl3_add_to_finished_hash(SSL *s)
+	{
+	ssl3_finish_mac(s, (uint8_t*) s->init_buf->data, s->init_num);
 	}
 
 int ssl3_new(SSL *s)
@@ -1385,9 +1391,9 @@
 			}
 	case SSL_CTRL_GET_EC_POINT_FORMATS:
 		{
+		const uint8_t **pformat = parg;
 		if (!s->s3->tmp.peer_ecpointformatlist)
 			return 0;
-		const uint8_t **pformat = parg;
 		*pformat = s->s3->tmp.peer_ecpointformatlist;
 		return (int)s->s3->tmp.peer_ecpointformatlist_length;
 		}
diff --git a/ssl/s3_srvr.c b/ssl/s3_srvr.c
index 149d9e7..a212efe 100644
--- a/ssl/s3_srvr.c
+++ b/ssl/s3_srvr.c
@@ -444,7 +444,7 @@
 				s->s3->tmp.cert_request=0;
 				s->state=SSL3_ST_SW_SRVR_DONE_A;
 				if (s->s3->handshake_buffer)
-					if (!ssl3_digest_cached_records(s))
+					if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 						return -1;
 				}
 			else
@@ -993,7 +993,8 @@
 		goto f_err;
 		}
 
-	if (ssl_bytes_to_cipher_list(s, &cipher_suites, &ciphers) == NULL)
+	ciphers = ssl_bytes_to_cipher_list(s, &cipher_suites);
+	if (ciphers == NULL)
 		{
 		goto err;
 		}
@@ -1143,7 +1144,7 @@
 
 	if (!SSL_USE_SIGALGS(s) || !(s->verify_mode & SSL_VERIFY_PEER))
 		{
-		if (!ssl3_digest_cached_records(s))
+		if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 			goto f_err;
 		}
 	
@@ -2140,9 +2141,13 @@
 			}
 
 		EVP_PKEY_free(clnt_pub_pkey);
+		clnt_pub_pkey = NULL;
 		EC_POINT_free(clnt_ecpoint);
+		clnt_ecpoint = NULL;
 		EC_KEY_free(srvr_ecdh);
+		srvr_ecdh = NULL;
 		BN_CTX_free(bn_ctx);
+		bn_ctx = NULL;
 		EC_KEY_free(s->s3->tmp.ecdh);
 		s->s3->tmp.ecdh = NULL;
 
@@ -2202,6 +2207,9 @@
 	s->session->master_key_length = s->method->ssl3_enc
 		->generate_master_secret(s,
 			s->session->master_key, premaster_secret, premaster_secret_len);
+	if (s->session->master_key_length == 0)
+		goto err;
+	s->session->extended_master_secret = s->s3->tmp.extended_master_secret;
 
 	OPENSSL_cleanse(premaster_secret, premaster_secret_len);
 	OPENSSL_free(premaster_secret);
@@ -2242,7 +2250,7 @@
 	 * client certificate. */
 	if (peer == NULL)
 		{
-		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s))
+		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer))
 			return -1;
 		return 1;
 		}
@@ -2283,7 +2291,7 @@
 
 	/* The handshake buffer is no longer necessary, and we may hash the
 	 * current message.*/
-	if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s))
+	if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer))
 		goto err;
 	ssl3_hash_current_message(s);
 
@@ -2452,7 +2460,7 @@
 			goto f_err;
 			}
 		/* No client certificate so digest cached records */
-		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s))
+		if (s->s3->handshake_buffer && !ssl3_digest_cached_records(s, free_handshake_buffer))
 			{
 			al=SSL_AD_INTERNAL_ERROR;
 			goto f_err;
@@ -2531,61 +2539,62 @@
 	{
 	if (s->state == SSL3_ST_SW_SESSION_TICKET_A)
 		{
-		unsigned char *p, *senc, *macstart;
-		const unsigned char *const_p;
-		int len, slen_full, slen;
-		SSL_SESSION *sess;
+		uint8_t *session;
+		size_t session_len;
+		uint8_t *p, *macstart;
+		int len;
 		unsigned int hlen;
 		EVP_CIPHER_CTX ctx;
 		HMAC_CTX hctx;
 		SSL_CTX *tctx = s->initial_ctx;
 		unsigned char iv[EVP_MAX_IV_LENGTH];
 		unsigned char key_name[16];
+		/* The maximum overhead of encrypting the session is 16 (key
+		 * name) + IV + one block of encryption overhead + HMAC.  */
+		const size_t max_ticket_overhead = 16 + EVP_MAX_IV_LENGTH +
+			EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE;
 
-		/* get session encoding length */
-		slen_full = i2d_SSL_SESSION(s->session, NULL);
-		/* Some length values are 16 bits, so forget it if session is
- 		 * too long
- 		 */
-		if (slen_full > 0xFF00)
-			return -1;
-		senc = OPENSSL_malloc(slen_full);
-		if (!senc)
-			return -1;
-		p = senc;
-		i2d_SSL_SESSION(s->session, &p);
-
-		/* create a fresh copy (not shared with other threads) to clean up */
-		const_p = senc;
-		sess = d2i_SSL_SESSION(NULL, &const_p, slen_full);
-		if (sess == NULL)
+		/* Serialize the SSL_SESSION to be encoded into the ticket. */
+		if (!SSL_SESSION_to_bytes_for_ticket(s->session, &session,
+				&session_len))
 			{
-			OPENSSL_free(senc);
 			return -1;
 			}
-		sess->session_id_length = 0; /* ID is irrelevant for the ticket */
 
-		slen = i2d_SSL_SESSION(sess, NULL);
-		if (slen > slen_full) /* shouldn't ever happen */
+		/* If the session is too long, emit a dummy value rather than
+		 * abort the connection. */
+		if (session_len > 0xFFFF - max_ticket_overhead)
 			{
-			OPENSSL_free(senc);
-			return -1;
+			const char kTicketPlaceholder[] = "TICKET TOO LARGE";
+			size_t placeholder_len = strlen(kTicketPlaceholder);
+
+			OPENSSL_free(session);
+
+			p = ssl_handshake_start(s);
+			/* Emit ticket_lifetime_hint. */
+			l2n(0, p);
+			/* Emit ticket. */
+			s2n(placeholder_len, p);
+			memcpy(p, kTicketPlaceholder, placeholder_len);
+			p += placeholder_len;
+
+			len = p - ssl_handshake_start(s);
+			ssl_set_handshake_header(s, SSL3_MT_NEWSESSION_TICKET, len);
+			s->state = SSL3_ST_SW_SESSION_TICKET_B;
+			return ssl_do_write(s);
 			}
-		p = senc;
-		i2d_SSL_SESSION(sess, &p);
-		SSL_SESSION_free(sess);
 
 		/* Grow buffer if need be: the length calculation is as
- 		 * follows handshake_header_length +
+		 * follows: handshake_header_length +
  		 * 4 (ticket lifetime hint) + 2 (ticket length) +
- 		 * 16 (key name) + max_iv_len (iv length) +
- 		 * session_length + max_enc_block_size (max encrypted session
- 		 * length) + max_md_size (HMAC).
- 		 */
+		 * max_ticket_overhead + * session_length */
 		if (!BUF_MEM_grow(s->init_buf,
-			SSL_HM_HEADER_LENGTH(s) + 22 + EVP_MAX_IV_LENGTH +
-			EVP_MAX_BLOCK_LENGTH + EVP_MAX_MD_SIZE + slen))
+				SSL_HM_HEADER_LENGTH(s) + 6 +
+				max_ticket_overhead + session_len))
+			{
+			OPENSSL_free(session);
 			return -1;
+			}
 		p = ssl_handshake_start(s);
 		EVP_CIPHER_CTX_init(&ctx);
 		HMAC_CTX_init(&hctx);
@@ -2598,7 +2607,7 @@
 			if (tctx->tlsext_ticket_key_cb(s, key_name, iv, &ctx,
 							 &hctx, 1) < 0)
 				{
-				OPENSSL_free(senc);
+				OPENSSL_free(session);
 				return -1;
 				}
 			}
@@ -2628,7 +2637,7 @@
 		memcpy(p, iv, EVP_CIPHER_CTX_iv_length(&ctx));
 		p += EVP_CIPHER_CTX_iv_length(&ctx);
 		/* Encrypt session data */
-		EVP_EncryptUpdate(&ctx, p, &len, senc, slen);
+		EVP_EncryptUpdate(&ctx, p, &len, session, session_len);
 		p += len;
 		EVP_EncryptFinal_ex(&ctx, p, &len);
 		p += len;
@@ -2647,7 +2656,7 @@
 		p = ssl_handshake_start(s) + 4;
 		s2n(len - 6, p);
 		s->state=SSL3_ST_SW_SESSION_TICKET_B;
-		OPENSSL_free(senc);
+		OPENSSL_free(session);
 		}
 
 	/* SSL3_ST_SW_SESSION_TICKET_B */
diff --git a/ssl/ssl_asn1.c b/ssl/ssl_asn1.c
index 5e86017..ef7ebdc 100644
--- a/ssl/ssl_asn1.c
+++ b/ssl/ssl_asn1.c
@@ -80,538 +80,530 @@
  * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
  * OTHERWISE. */
 
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <limits.h>
+#include <string.h>
 
-#include <openssl/asn1.h>
-#include <openssl/asn1_mac.h>
+#include <openssl/bytestring.h>
 #include <openssl/err.h>
-#include <openssl/mem.h>
-#include <openssl/obj.h>
 #include <openssl/x509.h>
 
 #include "ssl_locl.h"
 
-OPENSSL_DECLARE_ERROR_REASON(SSL, CIPHER_CODE_WRONG_LENGTH);
-OPENSSL_DECLARE_ERROR_REASON(SSL, UNKNOWN_SSL_VERSION);
-OPENSSL_DECLARE_ERROR_REASON(SSL, BAD_LENGTH);
-OPENSSL_DECLARE_ERROR_FUNCTION(SSL, D2I_SSL_SESSION);
 
+/* An SSL_SESSION is serialized as the following ASN.1 structure:
+ *
+ * SSLSession ::= SEQUENCE {
+ *     version                     INTEGER (1),  -- ignored
+ *     sslVersion                  INTEGER,      -- protocol version number
+ *     cipher                      OCTET STRING, -- two bytes long
+ *     sessionID                   OCTET STRING,
+ *     masterKey                   OCTET STRING,
+ *     time                    [1] INTEGER OPTIONAL, -- seconds since UNIX epoch
+ *     timeout                 [2] INTEGER OPTIONAL, -- in seconds
+ *     peer                    [3] Certificate OPTIONAL,
+ *     sessionIDContext        [4] OCTET STRING OPTIONAL,
+ *     verifyResult            [5] INTEGER OPTIONAL,  -- one of X509_V_* codes
+ *     hostName                [6] OCTET STRING OPTIONAL,
+ *                                 -- from server_name extension
+ *     pskIdentityHint         [7] OCTET STRING OPTIONAL,
+ *     pskIdentity             [8] OCTET STRING OPTIONAL,
+ *     ticketLifetimeHint      [9] INTEGER OPTIONAL,       -- client-only
+ *     ticket                  [10] OCTET STRING OPTIONAL, -- client-only
+ *     peerSHA256              [13] OCTET STRING OPTIONAL,
+ *     originalHandshakeHash   [14] OCTET STRING OPTIONAL,
+ *     signedCertTimestampList [15] OCTET STRING OPTIONAL,
+ *                                  -- contents of SCT extension
+ *     ocspResponse            [16] OCTET STRING OPTIONAL,
+ *                                   -- stapled OCSP response from the server
+ *     extendedMasterSecret    [17] BOOLEAN OPTIONAL,
+ * }
+ *
+ * Note: When the relevant features were #ifdef'd out, support for
+ * parsing compressionMethod [11] and srpUsername [12] was lost. */
 
-typedef struct ssl_session_asn1_st
-	{
-	ASN1_INTEGER version;
-	ASN1_INTEGER ssl_version;
-	ASN1_OCTET_STRING cipher;
-	ASN1_OCTET_STRING comp_id;
-	ASN1_OCTET_STRING master_key;
-	ASN1_OCTET_STRING session_id;
-	ASN1_OCTET_STRING session_id_context;
-	ASN1_INTEGER time;
-	ASN1_INTEGER timeout;
-	ASN1_INTEGER verify_result;
-	ASN1_OCTET_STRING tlsext_hostname;
-	ASN1_INTEGER tlsext_tick_lifetime;
-	ASN1_OCTET_STRING tlsext_tick;
-	ASN1_OCTET_STRING psk_identity_hint;
-	ASN1_OCTET_STRING psk_identity;
-	ASN1_OCTET_STRING peer_sha256;
-	ASN1_OCTET_STRING original_handshake_hash;
-	ASN1_OCTET_STRING tlsext_signed_cert_timestamp_list;
-	ASN1_OCTET_STRING ocsp_response;
-	} SSL_SESSION_ASN1;
+static const int kTimeTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
+static const int kTimeoutTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2;
+static const int kPeerTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 3;
+ static const int kSessionIDContextTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 4;
+static const int kVerifyResultTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 5;
+static const int kHostNameTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 6;
+static const int kPSKIdentityHintTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 7;
+static const int kPSKIdentityTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 8;
+static const int kTicketLifetimeHintTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 9;
+static const int kTicketTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 10;
+static const int kPeerSHA256Tag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 13;
+static const int kOriginalHandshakeHashTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 14;
+static const int kSignedCertTimestampListTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 15;
+static const int kOCSPResponseTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 16;
+static const int kExtendedMasterSecretTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 17;
 
-int i2d_SSL_SESSION(SSL_SESSION *in, unsigned char **pp)
-	{
-#define LSIZE2 (sizeof(long)*2)
-	int v1=0,v2=0,v3=0,v4=0,v5=0,v7=0,v8=0,v13=0,v14=0,v15=0,v16=0;
-	unsigned char buf[4],ibuf1[LSIZE2],ibuf2[LSIZE2];
-	unsigned char ibuf3[LSIZE2],ibuf4[LSIZE2],ibuf5[LSIZE2];
-	int v6=0,v9=0,v10=0;
-	unsigned char ibuf6[LSIZE2];
-	long l;
-	SSL_SESSION_ASN1 a;
-	M_ASN1_I2D_vars(in);
+static int SSL_SESSION_to_bytes_full(SSL_SESSION *in, uint8_t **out_data,
+                                     size_t *out_len, int for_ticket) {
+  CBB cbb, session, child, child2;
+  uint16_t cipher_id;
 
-	if ((in == NULL) || ((in->cipher == NULL) && (in->cipher_id == 0)))
-		return(0);
+  if (in == NULL || (in->cipher == NULL && in->cipher_id == 0)) {
+    return 0;
+  }
 
-	/* Note that I cheat in the following 2 assignments.  I know
-	 * that if the ASN1_INTEGER passed to ASN1_INTEGER_set
-	 * is > sizeof(long)+1, the buffer will not be re-OPENSSL_malloc()ed.
-	 * This is a bit evil but makes things simple, no dynamic allocation
-	 * to clean up :-) */
-	a.version.length=LSIZE2;
-	a.version.type=V_ASN1_INTEGER;
-	a.version.data=ibuf1;
-	ASN1_INTEGER_set(&(a.version),SSL_SESSION_ASN1_VERSION);
+  if (!CBB_init(&cbb, 0)) {
+    return 0;
+  }
 
-	a.ssl_version.length=LSIZE2;
-	a.ssl_version.type=V_ASN1_INTEGER;
-	a.ssl_version.data=ibuf2;
-	ASN1_INTEGER_set(&(a.ssl_version),in->ssl_version);
+  if (in->cipher == NULL) {
+    cipher_id = in->cipher_id & 0xffff;
+  } else {
+    cipher_id = in->cipher->id & 0xffff;
+  }
 
-	a.cipher.type=V_ASN1_OCTET_STRING;
-	a.cipher.data=buf;
+  if (!CBB_add_asn1(&cbb, &session, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1_uint64(&session, SSL_SESSION_ASN1_VERSION) ||
+      !CBB_add_asn1_uint64(&session, in->ssl_version) ||
+      !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_u16(&child, cipher_id) ||
+      !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) ||
+      /* The session ID is irrelevant for a session ticket. */
+      !CBB_add_bytes(&child, in->session_id,
+                     for_ticket ? 0 : in->session_id_length) ||
+      !CBB_add_asn1(&session, &child, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_bytes(&child, in->master_key, in->master_key_length)) {
+    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
 
-	if (in->cipher == NULL)
-		l=in->cipher_id;
-	else
-		l=in->cipher->id;
-	if (in->ssl_version == SSL2_VERSION)
-		{
-		a.cipher.length=3;
-		buf[0]=((unsigned char)(l>>16L))&0xff;
-		buf[1]=((unsigned char)(l>> 8L))&0xff;
-		buf[2]=((unsigned char)(l     ))&0xff;
-		}
-	else
-		{
-		a.cipher.length=2;
-		buf[0]=((unsigned char)(l>>8L))&0xff;
-		buf[1]=((unsigned char)(l    ))&0xff;
-		}
+  if (in->time != 0) {
+    if (!CBB_add_asn1(&session, &child, kTimeTag) ||
+        !CBB_add_asn1_uint64(&child, in->time)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
+  if (in->timeout != 0) {
+    if (!CBB_add_asn1(&session, &child, kTimeoutTag) ||
+        !CBB_add_asn1_uint64(&child, in->timeout)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	a.master_key.length=in->master_key_length;
-	a.master_key.type=V_ASN1_OCTET_STRING;
-	a.master_key.data=in->master_key;
+  /* The peer certificate is only serialized if the SHA-256 isn't
+   * serialized instead. */
+  if (in->peer && !in->peer_sha256_valid) {
+    uint8_t *buf;
+    int len = i2d_X509(in->peer, NULL);
+    if (len < 0) {
+      goto err;
+    }
+    if (!CBB_add_asn1(&session, &child, kPeerTag) ||
+        !CBB_add_space(&child, &buf, len)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    if (buf != NULL && i2d_X509(in->peer, &buf) < 0) {
+      goto err;
+    }
+  }
 
-	a.session_id.length=in->session_id_length;
-	a.session_id.type=V_ASN1_OCTET_STRING;
-	a.session_id.data=in->session_id;
+  /* Although it is OPTIONAL and usually empty, OpenSSL has
+   * historically always encoded the sid_ctx. */
+  if (!CBB_add_asn1(&session, &child, kSessionIDContextTag) ||
+      !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_bytes(&child2, in->sid_ctx, in->sid_ctx_length)) {
+    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
 
-	a.session_id_context.length=in->sid_ctx_length;
-	a.session_id_context.type=V_ASN1_OCTET_STRING;
-	a.session_id_context.data=in->sid_ctx;
+  if (in->verify_result != X509_V_OK) {
+    if (!CBB_add_asn1(&session, &child, kVerifyResultTag) ||
+        !CBB_add_asn1_uint64(&child, in->verify_result)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->time != 0L)
-		{
-		a.time.length=LSIZE2;
-		a.time.type=V_ASN1_INTEGER;
-		a.time.data=ibuf3;
-		ASN1_INTEGER_set(&(a.time),in->time);
-		}
+  if (in->tlsext_hostname) {
+    if (!CBB_add_asn1(&session, &child, kHostNameTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, (const uint8_t *)in->tlsext_hostname,
+                       strlen(in->tlsext_hostname))) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->timeout != 0L)
-		{
-		a.timeout.length=LSIZE2;
-		a.timeout.type=V_ASN1_INTEGER;
-		a.timeout.data=ibuf4;
-		ASN1_INTEGER_set(&(a.timeout),in->timeout);
-		}
+  if (in->psk_identity_hint) {
+    if (!CBB_add_asn1(&session, &child, kPSKIdentityHintTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity_hint,
+                       strlen(in->psk_identity_hint))) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->verify_result != X509_V_OK)
-		{
-		a.verify_result.length=LSIZE2;
-		a.verify_result.type=V_ASN1_INTEGER;
-		a.verify_result.data=ibuf5;
-		ASN1_INTEGER_set(&a.verify_result,in->verify_result);
-		}
+  if (in->psk_identity) {
+    if (!CBB_add_asn1(&session, &child, kPSKIdentityTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, (const uint8_t *)in->psk_identity,
+                       strlen(in->psk_identity))) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->tlsext_hostname)
-                {
-                a.tlsext_hostname.length=strlen(in->tlsext_hostname);
-                a.tlsext_hostname.type=V_ASN1_OCTET_STRING;
-                a.tlsext_hostname.data=(unsigned char *)in->tlsext_hostname;
-                }
-	if (in->tlsext_tick)
-                {
-                a.tlsext_tick.length= in->tlsext_ticklen;
-                a.tlsext_tick.type=V_ASN1_OCTET_STRING;
-                a.tlsext_tick.data=(unsigned char *)in->tlsext_tick;
-                }
-	if (in->tlsext_tick_lifetime_hint > 0)
-		{
-		a.tlsext_tick_lifetime.length=LSIZE2;
-		a.tlsext_tick_lifetime.type=V_ASN1_INTEGER;
-		a.tlsext_tick_lifetime.data=ibuf6;
-		ASN1_INTEGER_set(&a.tlsext_tick_lifetime,in->tlsext_tick_lifetime_hint);
-		}
-	if (in->psk_identity_hint)
-		{
-		a.psk_identity_hint.length=strlen(in->psk_identity_hint);
-		a.psk_identity_hint.type=V_ASN1_OCTET_STRING;
-		a.psk_identity_hint.data=(unsigned char *)(in->psk_identity_hint);
-		}
-	if (in->psk_identity)
-		{
-		a.psk_identity.length=strlen(in->psk_identity);
-		a.psk_identity.type=V_ASN1_OCTET_STRING;
-		a.psk_identity.data=(unsigned char *)(in->psk_identity);
-		}
+  if (in->tlsext_tick_lifetime_hint > 0) {
+    if (!CBB_add_asn1(&session, &child, kTicketLifetimeHintTag) ||
+        !CBB_add_asn1_uint64(&child, in->tlsext_tick_lifetime_hint)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->peer_sha256_valid)
-		{
-		a.peer_sha256.length = sizeof(in->peer_sha256);
-		a.peer_sha256.type = V_ASN1_OCTET_STRING;
-		a.peer_sha256.data = in->peer_sha256;
-		}
+  if (in->tlsext_tick) {
+    if (!CBB_add_asn1(&session, &child, kTicketTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, in->tlsext_tick, in->tlsext_ticklen)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->original_handshake_hash_len > 0)
-		{
-		a.original_handshake_hash.length = in->original_handshake_hash_len;
-		a.original_handshake_hash.type = V_ASN1_OCTET_STRING;
-		a.original_handshake_hash.data = in->original_handshake_hash;
-		}
+  if (in->peer_sha256_valid) {
+    if (!CBB_add_asn1(&session, &child, kPeerSHA256Tag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, in->peer_sha256, sizeof(in->peer_sha256))) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->tlsext_signed_cert_timestamp_list_length > 0)
-		{
-		a.tlsext_signed_cert_timestamp_list.length =
-				in->tlsext_signed_cert_timestamp_list_length;
-		a.tlsext_signed_cert_timestamp_list.type = V_ASN1_OCTET_STRING;
-		a.tlsext_signed_cert_timestamp_list.data =
-				in->tlsext_signed_cert_timestamp_list;
-		}
+  if (in->original_handshake_hash_len > 0) {
+    if (!CBB_add_asn1(&session, &child, kOriginalHandshakeHashTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, in->original_handshake_hash,
+                       in->original_handshake_hash_len)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->ocsp_response_length > 0)
-		{
-		a.ocsp_response.length = in->ocsp_response_length;
-		a.ocsp_response.type = V_ASN1_OCTET_STRING;
-		a.ocsp_response.data = in->ocsp_response;
-		}
+  if (in->tlsext_signed_cert_timestamp_list_length > 0) {
+    if (!CBB_add_asn1(&session, &child, kSignedCertTimestampListTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, in->tlsext_signed_cert_timestamp_list,
+                       in->tlsext_signed_cert_timestamp_list_length)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	M_ASN1_I2D_len(&(a.version),		i2d_ASN1_INTEGER);
-	M_ASN1_I2D_len(&(a.ssl_version),	i2d_ASN1_INTEGER);
-	M_ASN1_I2D_len(&(a.cipher),		i2d_ASN1_OCTET_STRING);
-	M_ASN1_I2D_len(&(a.session_id),		i2d_ASN1_OCTET_STRING);
-	M_ASN1_I2D_len(&(a.master_key),		i2d_ASN1_OCTET_STRING);
-	if (in->time != 0L)
-		M_ASN1_I2D_len_EXP_opt(&(a.time),i2d_ASN1_INTEGER,1,v1);
-	if (in->timeout != 0L)
-		M_ASN1_I2D_len_EXP_opt(&(a.timeout),i2d_ASN1_INTEGER,2,v2);
-	if (in->peer != NULL && in->peer_sha256_valid == 0)
-		M_ASN1_I2D_len_EXP_opt(in->peer,i2d_X509,3,v3);
-	M_ASN1_I2D_len_EXP_opt(&a.session_id_context,i2d_ASN1_OCTET_STRING,4,v4);
-	if (in->verify_result != X509_V_OK)
-		M_ASN1_I2D_len_EXP_opt(&(a.verify_result),i2d_ASN1_INTEGER,5,v5);
+  if (in->ocsp_response_length > 0) {
+    if (!CBB_add_asn1(&session, &child, kOCSPResponseTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_OCTETSTRING) ||
+        !CBB_add_bytes(&child2, in->ocsp_response, in->ocsp_response_length)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	if (in->tlsext_tick_lifetime_hint > 0)
-      	 	M_ASN1_I2D_len_EXP_opt(&a.tlsext_tick_lifetime, i2d_ASN1_INTEGER,9,v9);
-	if (in->tlsext_tick)
-        	M_ASN1_I2D_len_EXP_opt(&(a.tlsext_tick), i2d_ASN1_OCTET_STRING,10,v10);
-	if (in->tlsext_hostname)
-        	M_ASN1_I2D_len_EXP_opt(&(a.tlsext_hostname), i2d_ASN1_OCTET_STRING,6,v6);
-	if (in->psk_identity_hint)
-        	M_ASN1_I2D_len_EXP_opt(&(a.psk_identity_hint), i2d_ASN1_OCTET_STRING,7,v7);
-	if (in->psk_identity)
-        	M_ASN1_I2D_len_EXP_opt(&(a.psk_identity), i2d_ASN1_OCTET_STRING,8,v8);
-	if (in->peer_sha256_valid)
-		M_ASN1_I2D_len_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13);
-	if (in->original_handshake_hash_len > 0)
-		M_ASN1_I2D_len_EXP_opt(&(a.original_handshake_hash),i2d_ASN1_OCTET_STRING,14,v14);
-	if (in->tlsext_signed_cert_timestamp_list_length > 0)
-		M_ASN1_I2D_len_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
-				i2d_ASN1_OCTET_STRING, 15, v15);
-	if (in->ocsp_response_length > 0)
-		M_ASN1_I2D_len_EXP_opt(&(a.ocsp_response), i2d_ASN1_OCTET_STRING, 16, v16);
+  if (in->extended_master_secret) {
+    if (!CBB_add_asn1(&session, &child, kExtendedMasterSecretTag) ||
+        !CBB_add_asn1(&child, &child2, CBS_ASN1_BOOLEAN) ||
+        !CBB_add_u8(&child2, 0xff)) {
+      OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+  }
 
-	M_ASN1_I2D_seq_total();
+  if (!CBB_finish(&cbb, out_data, out_len)) {
+    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  return 1;
 
-	M_ASN1_I2D_put(&(a.version),		i2d_ASN1_INTEGER);
-	M_ASN1_I2D_put(&(a.ssl_version),	i2d_ASN1_INTEGER);
-	M_ASN1_I2D_put(&(a.cipher),		i2d_ASN1_OCTET_STRING);
-	M_ASN1_I2D_put(&(a.session_id),		i2d_ASN1_OCTET_STRING);
-	M_ASN1_I2D_put(&(a.master_key),		i2d_ASN1_OCTET_STRING);
-	if (in->time != 0L)
-		M_ASN1_I2D_put_EXP_opt(&(a.time),i2d_ASN1_INTEGER,1,v1);
-	if (in->timeout != 0L)
-		M_ASN1_I2D_put_EXP_opt(&(a.timeout),i2d_ASN1_INTEGER,2,v2);
-	if (in->peer != NULL && in->peer_sha256_valid == 0)
-		M_ASN1_I2D_put_EXP_opt(in->peer,i2d_X509,3,v3);
-	M_ASN1_I2D_put_EXP_opt(&a.session_id_context,i2d_ASN1_OCTET_STRING,4,
-			       v4);
-	if (in->verify_result != X509_V_OK)
-		M_ASN1_I2D_put_EXP_opt(&a.verify_result,i2d_ASN1_INTEGER,5,v5);
-	if (in->tlsext_hostname)
-        	M_ASN1_I2D_put_EXP_opt(&(a.tlsext_hostname), i2d_ASN1_OCTET_STRING,6,v6);
-	if (in->psk_identity_hint)
-		M_ASN1_I2D_put_EXP_opt(&(a.psk_identity_hint), i2d_ASN1_OCTET_STRING,7,v7);
-	if (in->psk_identity)
-		M_ASN1_I2D_put_EXP_opt(&(a.psk_identity), i2d_ASN1_OCTET_STRING,8,v8);
-	if (in->tlsext_tick_lifetime_hint > 0)
-      	 	M_ASN1_I2D_put_EXP_opt(&a.tlsext_tick_lifetime, i2d_ASN1_INTEGER,9,v9);
-	if (in->tlsext_tick)
-        	M_ASN1_I2D_put_EXP_opt(&(a.tlsext_tick), i2d_ASN1_OCTET_STRING,10,v10);
-	if (in->peer_sha256_valid)
-		M_ASN1_I2D_put_EXP_opt(&(a.peer_sha256),i2d_ASN1_OCTET_STRING,13,v13);
-	if (in->original_handshake_hash_len > 0)
-		M_ASN1_I2D_put_EXP_opt(&(a.original_handshake_hash),i2d_ASN1_OCTET_STRING,14,v14);
-	if (in->tlsext_signed_cert_timestamp_list_length > 0)
-		M_ASN1_I2D_put_EXP_opt(&(a.tlsext_signed_cert_timestamp_list),
-				i2d_ASN1_OCTET_STRING, 15, v15);
-	if (in->ocsp_response > 0)
-		M_ASN1_I2D_put_EXP_opt(&(a.ocsp_response), i2d_ASN1_OCTET_STRING, 16, v16);
+ err:
+  CBB_cleanup(&cbb);
+  return 0;
+}
 
-	M_ASN1_I2D_finish();
-	}
+int SSL_SESSION_to_bytes(SSL_SESSION *in, uint8_t **out_data, size_t *out_len) {
+  return SSL_SESSION_to_bytes_full(in, out_data, out_len, 0);
+}
 
-SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const unsigned char **pp,
-			     long length)
-	{
-	int ssl_version=0,i;
-	long id;
-	ASN1_INTEGER ai,*aip;
-	ASN1_OCTET_STRING os,*osp;
-	M_ASN1_D2I_vars(a,SSL_SESSION *,SSL_SESSION_new);
+int SSL_SESSION_to_bytes_for_ticket(SSL_SESSION *in, uint8_t **out_data,
+                                    size_t *out_len) {
+  return SSL_SESSION_to_bytes_full(in, out_data, out_len, 1);
+}
 
-	aip= &ai;
-	osp= &os;
+int i2d_SSL_SESSION(SSL_SESSION *in, uint8_t **pp) {
+  uint8_t *out;
+  size_t len;
 
-	M_ASN1_D2I_Init();
-	M_ASN1_D2I_start_sequence();
+  if (!SSL_SESSION_to_bytes(in, &out, &len)) {
+    return -1;
+  }
 
-	ai.data=NULL; ai.length=0;
-	M_ASN1_D2I_get_x(ASN1_INTEGER,aip,d2i_ASN1_INTEGER);
-	if (ai.data != NULL) { OPENSSL_free(ai.data); ai.data=NULL; ai.length=0; }
+  if (len > INT_MAX) {
+    OPENSSL_free(out);
+    OPENSSL_PUT_ERROR(SSL, i2d_SSL_SESSION, ERR_R_OVERFLOW);
+    return -1;
+  }
 
-	/* we don't care about the version right now :-) */
-	M_ASN1_D2I_get_x(ASN1_INTEGER,aip,d2i_ASN1_INTEGER);
-	ssl_version=(int)ASN1_INTEGER_get(aip);
-	ret->ssl_version=ssl_version;
-	if (ai.data != NULL) { OPENSSL_free(ai.data); ai.data=NULL; ai.length=0; }
+  if (pp) {
+    memcpy(*pp, out, len);
+    *pp += len;
+  }
+  OPENSSL_free(out);
 
-	os.data=NULL; os.length=0;
-	M_ASN1_D2I_get_x(ASN1_OCTET_STRING,osp,d2i_ASN1_OCTET_STRING);
-	if (ssl_version == SSL2_VERSION)
-		{
-		if (os.length != 3)
-			{
-			c.error=SSL_R_CIPHER_CODE_WRONG_LENGTH;
-			c.line=__LINE__;
-			goto err;
-			}
-		id=0x02000000L|
-			((unsigned long)os.data[0]<<16L)|
-			((unsigned long)os.data[1]<< 8L)|
-			 (unsigned long)os.data[2];
-		}
-	else if ((ssl_version>>8) >= SSL3_VERSION_MAJOR)
-		{
-		if (os.length != 2)
-			{
-			c.error=SSL_R_CIPHER_CODE_WRONG_LENGTH;
-			c.line=__LINE__;
-			goto err;
-			}
-		id=0x03000000L|
-			((unsigned long)os.data[0]<<8L)|
-			 (unsigned long)os.data[1];
-		}
-	else
-		{
-		c.error=SSL_R_UNKNOWN_SSL_VERSION;
-		c.line=__LINE__;
-		goto err;
-		}
-	
-	ret->cipher_id=id;
-	ret->cipher = ssl3_get_cipher_by_value(ret->cipher_id & 0xffff);
-	if (ret->cipher == NULL)
-		{
-		c.error=SSL_R_UNSUPPORTED_CIPHER;
-		c.line = __LINE__;
-		goto err;
-		}
+  return len;
+}
 
-	M_ASN1_D2I_get_x(ASN1_OCTET_STRING,osp,d2i_ASN1_OCTET_STRING);
-	if ((ssl_version>>8) >= SSL3_VERSION_MAJOR)
-		i=SSL3_MAX_SSL_SESSION_ID_LENGTH;
-	else /* if (ssl_version>>8 == SSL2_VERSION_MAJOR) */
-		i=SSL2_MAX_SSL_SESSION_ID_LENGTH;
+/* d2i_SSL_SESSION_get_string gets an optional ASN.1 OCTET STRING
+ * explicitly tagged with |tag| from |cbs| and saves it in |*out|. On
+ * entry, if |*out| is not NULL, it frees the existing contents. If
+ * the element was not found, it sets |*out| to NULL. It returns one
+ * on success, whether or not the element was found, and zero on
+ * decode error. */
+static int d2i_SSL_SESSION_get_string(CBS *cbs, char **out, unsigned tag) {
+  CBS value;
+  int present;
+  if (!CBS_get_optional_asn1_octet_string(cbs, &value, &present, tag)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    return 0;
+  }
+  if (present) {
+    if (CBS_contains_zero_byte(&value)) {
+      OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+      return 0;
+    }
+    if (!CBS_strdup(&value, out)) {
+      OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+      return 0;
+    }
+  } else if (*out) {
+    OPENSSL_free(*out);
+    *out = NULL;
+  }
+  return 1;
+}
 
-	if (os.length > i)
-		os.length = i;
-	if (os.length > (int)sizeof(ret->session_id)) /* can't happen */
-		os.length = sizeof(ret->session_id);
+/* d2i_SSL_SESSION_get_string gets an optional ASN.1 OCTET STRING
+ * explicitly tagged with |tag| from |cbs| and stows it in |*out_ptr|
+ * and |*out_len|. If |*out_ptr| is not NULL, it frees the existing
+ * contents. On entry, if the element was not found, it sets
+ * |*out_ptr| to NULL. It returns one on success, whether or not the
+ * element was found, and zero on decode error. */
+static int d2i_SSL_SESSION_get_octet_string(CBS *cbs, uint8_t **out_ptr,
+                                            size_t *out_len, unsigned tag) {
+  CBS value;
+  if (!CBS_get_optional_asn1_octet_string(cbs, &value, NULL, tag)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    return 0;
+  }
+  if (!CBS_stow(&value, out_ptr, out_len)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  return 1;
+}
 
-	ret->session_id_length=os.length;
-	assert(os.length <= (int)sizeof(ret->session_id));
-	memcpy(ret->session_id,os.data,os.length);
+SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a, const uint8_t **pp, long length) {
+  SSL_SESSION *ret = NULL;
+  CBS cbs, session, cipher, session_id, master_key;
+  CBS peer, sid_ctx, peer_sha256, original_handshake_hash;
+  int has_peer, has_peer_sha256, extended_master_secret;
+  uint64_t version, ssl_version;
+  uint64_t session_time, timeout, verify_result, ticket_lifetime_hint;
 
-	M_ASN1_D2I_get_x(ASN1_OCTET_STRING,osp,d2i_ASN1_OCTET_STRING);
-	if (os.length > SSL_MAX_MASTER_KEY_LENGTH)
-		ret->master_key_length=SSL_MAX_MASTER_KEY_LENGTH;
-	else
-		ret->master_key_length=os.length;
-	memcpy(ret->master_key,os.data,ret->master_key_length);
+  if (a && *a) {
+    ret = *a;
+  } else {
+    ret = SSL_SESSION_new();
+    if (ret == NULL) {
+      goto err;
+    }
+  }
 
-	os.length=0;
+  CBS_init(&cbs, *pp, length);
+  if (!CBS_get_asn1(&cbs, &session, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1_uint64(&session, &version) ||
+      !CBS_get_asn1_uint64(&session, &ssl_version) ||
+      !CBS_get_asn1(&session, &cipher, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1(&session, &session_id, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1(&session, &master_key, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_optional_asn1_uint64(&session, &session_time, kTimeTag,
+                                    time(NULL)) ||
+      !CBS_get_optional_asn1_uint64(&session, &timeout, kTimeoutTag, 3) ||
+      !CBS_get_optional_asn1(&session, &peer, &has_peer, kPeerTag) ||
+      !CBS_get_optional_asn1_octet_string(&session, &sid_ctx, NULL,
+                                          kSessionIDContextTag) ||
+      !CBS_get_optional_asn1_uint64(&session, &verify_result, kVerifyResultTag,
+                                    X509_V_OK)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  if (!d2i_SSL_SESSION_get_string(&session, &ret->tlsext_hostname,
+                                  kHostNameTag) ||
+      !d2i_SSL_SESSION_get_string(&session, &ret->psk_identity_hint,
+                                  kPSKIdentityHintTag) ||
+      !d2i_SSL_SESSION_get_string(&session, &ret->psk_identity,
+                                  kPSKIdentityTag)) {
+    goto err;
+  }
+  if (!CBS_get_optional_asn1_uint64(&session, &ticket_lifetime_hint,
+                                    kTicketLifetimeHintTag, 0)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  if (!d2i_SSL_SESSION_get_octet_string(&session, &ret->tlsext_tick,
+                                        &ret->tlsext_ticklen, kTicketTag)) {
+    goto err;
+  }
+  if (!CBS_get_optional_asn1_octet_string(&session, &peer_sha256,
+                                          &has_peer_sha256, kPeerSHA256Tag) ||
+      !CBS_get_optional_asn1_octet_string(&session, &original_handshake_hash,
+                                          NULL, kOriginalHandshakeHashTag)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  if (!d2i_SSL_SESSION_get_octet_string(
+          &session, &ret->tlsext_signed_cert_timestamp_list,
+          &ret->tlsext_signed_cert_timestamp_list_length,
+          kSignedCertTimestampListTag) ||
+      !d2i_SSL_SESSION_get_octet_string(
+          &session, &ret->ocsp_response, &ret->ocsp_response_length,
+          kOCSPResponseTag)) {
+    goto err;
+  }
+  if (!CBS_get_optional_asn1_bool(&session, &extended_master_secret,
+                                  kExtendedMasterSecretTag,
+                                  0 /* default to false */)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  ret->extended_master_secret = extended_master_secret;
 
-	/* [0] is the tag for key_arg, a no longer used remnant of
-	 * SSLv2. */
-	M_ASN1_D2I_get_IMP_opt(osp,d2i_ASN1_OCTET_STRING,0,V_ASN1_OCTET_STRING);
-	if (os.data != NULL) OPENSSL_free(os.data);
+  /* Ignore |version|. The structure version number is ignored. */
 
-	ai.length=0;
-	M_ASN1_D2I_get_EXP_opt(aip,d2i_ASN1_INTEGER,1);
-	if (ai.data != NULL)
-		{
-		ret->time=ASN1_INTEGER_get(aip);
-		OPENSSL_free(ai.data); ai.data=NULL; ai.length=0;
-		}
-	else
-		ret->time=(unsigned long)time(NULL);
+  /* Only support SSLv3/TLS and DTLS. */
+  if ((ssl_version >> 8) != SSL3_VERSION_MAJOR &&
+      (ssl_version >> 8) != (DTLS1_VERSION >> 8)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNKNOWN_SSL_VERSION);
+    goto err;
+  }
+  ret->ssl_version = ssl_version;
 
-	ai.length=0;
-	M_ASN1_D2I_get_EXP_opt(aip,d2i_ASN1_INTEGER,2);
-	if (ai.data != NULL)
-		{
-		ret->timeout=ASN1_INTEGER_get(aip);
-		OPENSSL_free(ai.data); ai.data=NULL; ai.length=0;
-		}
-	else
-		ret->timeout=3;
+  if (CBS_len(&cipher) != 2) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_CIPHER_CODE_WRONG_LENGTH);
+    goto err;
+  }
+  ret->cipher_id =
+      0x03000000L | (CBS_data(&cipher)[0] << 8L) | CBS_data(&cipher)[1];
+  ret->cipher = ssl3_get_cipher_by_value(ret->cipher_id & 0xffff);
+  if (ret->cipher == NULL) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_UNSUPPORTED_CIPHER);
+    goto err;
+  }
 
-	if (ret->peer != NULL)
-		{
-		X509_free(ret->peer);
-		ret->peer=NULL;
-		}
-	M_ASN1_D2I_get_EXP_opt(ret->peer,d2i_X509,3);
+  if (CBS_len(&session_id) > SSL3_MAX_SSL_SESSION_ID_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  memcpy(ret->session_id, CBS_data(&session_id), CBS_len(&session_id));
+  ret->session_id_length = CBS_len(&session_id);
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,4);
+  if (CBS_len(&master_key) > SSL_MAX_MASTER_KEY_LENGTH) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  memcpy(ret->master_key, CBS_data(&master_key), CBS_len(&master_key));
+  ret->master_key_length = CBS_len(&master_key);
 
-	if(os.data != NULL)
-	    {
-	    if (os.length > SSL_MAX_SID_CTX_LENGTH)
-		{
-		c.error=SSL_R_BAD_LENGTH;
-		c.line=__LINE__;
-		goto err;
-		}
-	    else
-		{
-		ret->sid_ctx_length=os.length;
-		memcpy(ret->sid_ctx,os.data,os.length);
-		}
-	    OPENSSL_free(os.data); os.data=NULL; os.length=0;
-	    }
-	else
-	    ret->sid_ctx_length=0;
+  if (session_time > LONG_MAX ||
+      timeout > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  ret->time = session_time;
+  ret->timeout = timeout;
 
-	ai.length=0;
-	M_ASN1_D2I_get_EXP_opt(aip,d2i_ASN1_INTEGER,5);
-	if (ai.data != NULL)
-		{
-		ret->verify_result=ASN1_INTEGER_get(aip);
-		OPENSSL_free(ai.data); ai.data=NULL; ai.length=0;
-		}
-	else
-		ret->verify_result=X509_V_OK;
+  if (ret->peer != NULL) {
+    X509_free(ret->peer);
+    ret->peer = NULL;
+  }
+  if (has_peer) {
+    const uint8_t *ptr;
+    ptr = CBS_data(&peer);
+    ret->peer = d2i_X509(NULL, &ptr, CBS_len(&peer));
+    if (ret->peer == NULL) {
+      goto err;
+    }
+    if (ptr != CBS_data(&peer) + CBS_len(&peer)) {
+      OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+      goto err;
+    }
+  }
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,6);
-	if (os.data)
-		{
-		ret->tlsext_hostname = BUF_strndup((char *)os.data, os.length);
-		OPENSSL_free(os.data);
-		os.data = NULL;
-		os.length = 0;
-		}
-	else
-		ret->tlsext_hostname=NULL;
+  if (CBS_len(&sid_ctx) > sizeof(ret->sid_ctx)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  memcpy(ret->sid_ctx, CBS_data(&sid_ctx), CBS_len(&sid_ctx));
+  ret->sid_ctx_length = CBS_len(&sid_ctx);
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,7);
-	if (os.data)
-		{
-		ret->psk_identity_hint = BUF_strndup((char *)os.data, os.length);
-		OPENSSL_free(os.data);
-		os.data = NULL;
-		os.length = 0;
-		}
-	else
-		ret->psk_identity_hint=NULL;
+  if (verify_result > LONG_MAX ||
+      ticket_lifetime_hint > 0xffffffff) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  ret->verify_result = verify_result;
+  ret->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,8);
-	if (os.data)
-		{
-		ret->psk_identity = BUF_strndup((char *)os.data, os.length);
-		OPENSSL_free(os.data);
-		os.data = NULL;
-		os.length = 0;
-		}
-	else
-		ret->psk_identity=NULL;
+  if (has_peer_sha256) {
+    if (CBS_len(&peer_sha256) != sizeof(ret->peer_sha256)) {
+      OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+      goto err;
+    }
+    memcpy(ret->peer_sha256, CBS_data(&peer_sha256), sizeof(ret->peer_sha256));
+    ret->peer_sha256_valid = 1;
+  } else {
+    ret->peer_sha256_valid = 0;
+  }
 
-	ai.length=0;
-	M_ASN1_D2I_get_EXP_opt(aip,d2i_ASN1_INTEGER,9);
-	if (ai.data != NULL)
-		{
-		ret->tlsext_tick_lifetime_hint=ASN1_INTEGER_get(aip);
-		OPENSSL_free(ai.data); ai.data=NULL; ai.length=0;
-		}
-	else if (ret->tlsext_ticklen && ret->session_id_length)
-		ret->tlsext_tick_lifetime_hint = -1;
-	else
-		ret->tlsext_tick_lifetime_hint=0;
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,10);
-	if (os.data)
-		{
-		ret->tlsext_tick = os.data;
-		ret->tlsext_ticklen = os.length;
-		os.data = NULL;
-		os.length = 0;
-		}
-	else
-		ret->tlsext_tick=NULL;
+  if (CBS_len(&original_handshake_hash) >
+      sizeof(ret->original_handshake_hash)) {
+    OPENSSL_PUT_ERROR(SSL, d2i_SSL_SESSION, SSL_R_INVALID_SSL_SESSION);
+    goto err;
+  }
+  memcpy(ret->original_handshake_hash, CBS_data(&original_handshake_hash),
+         CBS_len(&original_handshake_hash));
+  ret->original_handshake_hash_len = CBS_len(&original_handshake_hash);
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,13);
-	if (os.data && os.length == sizeof(ret->peer_sha256))
-		{
-		memcpy(ret->peer_sha256, os.data, sizeof(ret->peer_sha256));
-		ret->peer_sha256_valid = 1;
-		OPENSSL_free(os.data);
-		os.data = NULL;
-		}
+  if (a) {
+    *a = ret;
+  }
+  *pp = CBS_data(&cbs);
+  return ret;
 
-	os.length=0;
-	os.data=NULL;
-	M_ASN1_D2I_get_EXP_opt(osp,d2i_ASN1_OCTET_STRING,14);
-	if (os.data && os.length < (int)sizeof(ret->original_handshake_hash))
-		{
-		memcpy(ret->original_handshake_hash, os.data, os.length);
-		ret->original_handshake_hash_len = os.length;
-		OPENSSL_free(os.data);
-		os.data = NULL;
-		}
-
-	os.length = 0;
-	os.data = NULL;
-	M_ASN1_D2I_get_EXP_opt(osp, d2i_ASN1_OCTET_STRING, 15);
-	if (os.data)
-		{
-		if (ret->tlsext_signed_cert_timestamp_list)
-			OPENSSL_free(ret->tlsext_signed_cert_timestamp_list);
-		ret->tlsext_signed_cert_timestamp_list = os.data;
-		ret->tlsext_signed_cert_timestamp_list_length = os.length;
-		os.data = NULL;
-		}
-
-	os.length = 0;
-	os.data = NULL;
-	M_ASN1_D2I_get_EXP_opt(osp, d2i_ASN1_OCTET_STRING, 16);
-	if (os.data)
-		{
-		if (ret->ocsp_response)
-			OPENSSL_free(ret->ocsp_response);
-		ret->ocsp_response = os.data;
-		ret->ocsp_response_length = os.length;
-		os.data = NULL;
-		}
-
-
-	M_ASN1_D2I_Finish(a,SSL_SESSION_free,SSL_F_D2I_SSL_SESSION);
-	}
+err:
+  if (a && *a != ret) {
+    SSL_SESSION_free(ret);
+  }
+  return NULL;
+}
diff --git a/ssl/ssl_error.c b/ssl/ssl_error.c
index 0ba125b..b070b5f 100644
--- a/ssl/ssl_error.c
+++ b/ssl/ssl_error.c
@@ -39,6 +39,7 @@
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_new, 0), "SSL_SESSION_new"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_print_fp, 0), "SSL_SESSION_print_fp"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_set1_id_context, 0), "SSL_SESSION_set1_id_context"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_SESSION_to_bytes_full, 0), "SSL_SESSION_to_bytes_full"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_dir_cert_subjects_to_stack, 0), "SSL_add_dir_cert_subjects_to_stack"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_add_file_cert_subjects_to_stack, 0), "SSL_add_file_cert_subjects_to_stack"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_check_private_key, 0), "SSL_check_private_key"},
@@ -70,6 +71,9 @@
   {ERR_PACK(ERR_LIB_SSL, SSL_F_SSL_write, 0), "SSL_write"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_authz_find_data, 0), "authz_find_data"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_check_suiteb_cipher_list, 0), "check_suiteb_cipher_list"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION, 0), "d2i_SSL_SESSION"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_octet_string, 0), "d2i_SSL_SESSION_get_octet_string"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_d2i_SSL_SESSION_get_string, 0), "d2i_SSL_SESSION_get_string"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_do_dtls1_write, 0), "do_dtls1_write"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_do_ssl3_write, 0), "do_ssl3_write"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_accept, 0), "dtls1_accept"},
@@ -87,6 +91,7 @@
   {ERR_PACK(ERR_LIB_SSL, SSL_F_dtls1_write_app_data_bytes, 0), "dtls1_write_app_data_bytes"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_fclose, 0), "fclose"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_fprintf, 0), "fprintf"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_i2d_SSL_SESSION, 0), "i2d_SSL_SESSION"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_printf, 0), "printf"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_read_authz, 0), "read_authz"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl23_accept, 0), "ssl23_accept"},
@@ -136,6 +141,7 @@
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_certificate, 0), "ssl3_send_client_certificate"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_hello, 0), "ssl3_send_client_hello"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_client_key_exchange, 0), "ssl3_send_client_key_exchange"},
+  {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_new_session_ticket, 0), "ssl3_send_new_session_ticket"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_certificate, 0), "ssl3_send_server_certificate"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_hello, 0), "ssl3_send_server_hello"},
   {ERR_PACK(ERR_LIB_SSL, SSL_F_ssl3_send_server_key_exchange, 0), "ssl3_send_server_key_exchange"},
@@ -320,6 +326,7 @@
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_PURPOSE), "INVALID_PURPOSE"},
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SERVERINFO_DATA), "INVALID_SERVERINFO_DATA"},
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SRP_USERNAME), "INVALID_SRP_USERNAME"},
+  {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_SSL_SESSION), "INVALID_SSL_SESSION"},
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_STATUS_RESPONSE), "INVALID_STATUS_RESPONSE"},
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TICKET_KEYS_LENGTH), "INVALID_TICKET_KEYS_LENGTH"},
   {ERR_PACK(ERR_LIB_SSL, 0, SSL_R_INVALID_TRUST), "INVALID_TRUST"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index f7818ed..8357ff9 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1495,8 +1495,7 @@
 	return(p-q);
 	}
 
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs,
-					       STACK_OF(SSL_CIPHER) **skp)
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs)
 	{
 	CBS cipher_suites = *cbs;
 	const SSL_CIPHER *c;
@@ -1508,14 +1507,14 @@
 	if (CBS_len(&cipher_suites) % 2 != 0)
 		{
 		OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST);
-		return(NULL);
+		return NULL;
 		}
-	if ((skp == NULL) || (*skp == NULL))
-		sk=sk_SSL_CIPHER_new_null(); /* change perhaps later */
-	else
+
+	sk = sk_SSL_CIPHER_new_null();
+	if (sk == NULL)
 		{
-		sk= *skp;
-		sk_SSL_CIPHER_zero(sk);
+		OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE);
+		goto err;
 		}
 
 	if (!CBS_stow(&cipher_suites,
@@ -1535,10 +1534,10 @@
 			goto err;
 			}
 
-		/* Check for SCSV */
+		/* Check for SCSV. */
 		if (s->s3 && cipher_suite == (SSL3_CK_SCSV & 0xffff))
 			{
-			/* SCSV fatal if renegotiating */
+			/* SCSV is fatal if renegotiating. */
 			if (s->renegotiate)
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING);
@@ -1546,25 +1545,25 @@
 				goto err;
 				}
 			s->s3->send_connection_binding = 1;
-#ifdef OPENSSL_RI_DEBUG
-			fprintf(stderr, "SCSV received by server\n");
-#endif
 			continue;
 			}
 
-		/* Check for FALLBACK_SCSV */
-		if (s->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff) &&
-			s->version < ssl_get_max_version(s))
+		/* Check for FALLBACK_SCSV. */
+		if (s->s3 && cipher_suite == (SSL3_CK_FALLBACK_SCSV & 0xffff))
 			{
-			OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, SSL_R_INAPPROPRIATE_FALLBACK);
-			ssl3_send_alert(s,SSL3_AL_FATAL,SSL3_AD_INAPPROPRIATE_FALLBACK);
-			goto err;
+			if (s->version < ssl_get_max_version(s))
+				{
+				OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, SSL_R_INAPPROPRIATE_FALLBACK);
+				ssl3_send_alert(s, SSL3_AL_FATAL, SSL3_AD_INAPPROPRIATE_FALLBACK);
+				goto err;
+				}
+			continue;
 			}
 
 		c = ssl3_get_cipher_by_value(cipher_suite);
 		if (c != NULL)
 			{
-			if (!sk_SSL_CIPHER_push(sk,c))
+			if (!sk_SSL_CIPHER_push(sk, c))
 				{
 				OPENSSL_PUT_ERROR(SSL, ssl_bytes_to_cipher_list, ERR_R_MALLOC_FAILURE);
 				goto err;
@@ -1572,13 +1571,12 @@
 			}
 		}
 
-	if (skp != NULL)
-		*skp=sk;
-	return(sk);
+	return sk;
+
 err:
-	if ((skp == NULL) || (*skp == NULL))
+	if (sk != NULL)
 		sk_SSL_CIPHER_free(sk);
-	return(NULL);
+	return NULL;
 	}
 
 
diff --git a/ssl/ssl_locl.h b/ssl/ssl_locl.h
index 75c0ba8..c214b91 100644
--- a/ssl/ssl_locl.h
+++ b/ssl/ssl_locl.h
@@ -568,6 +568,11 @@
 
 #define FP_ICC  (int (*)(const void *,const void *))
 
+enum should_add_to_finished_hash {
+  add_to_finished_hash,
+  dont_add_to_finished_hash,
+};
+
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff
  * It is a bit of a mess of functions, but hell, think of it as
  * an opaque structure :-) */
@@ -597,7 +602,9 @@
 	/* Set the handshake header */
 	void (*set_handshake_header)(SSL *s, int type, unsigned long len);
 	/* Write out handshake message */
-	int (*do_write)(SSL *s);
+	int (*do_write)(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash);
+	/* Add the current handshake message to the finished hash. */
+	void (*add_to_finished_hash)(SSL *s);
 	} SSL3_ENC_METHOD;
 
 #define SSL_HM_HEADER_LENGTH(s)	s->method->ssl3_enc->hhlen
@@ -605,7 +612,7 @@
 	(((unsigned char *)s->init_buf->data) + s->method->ssl3_enc->hhlen)
 #define ssl_set_handshake_header(s, htype, len) \
 	s->method->ssl3_enc->set_handshake_header(s, htype, len)
-#define ssl_do_write(s)  s->method->ssl3_enc->do_write(s)
+#define ssl_do_write(s)  s->method->ssl3_enc->do_write(s, add_to_finished_hash)
 
 /* Values for enc_flags */
 
@@ -768,8 +775,7 @@
 int ssl_get_prev_session(SSL *s, const struct ssl_early_callback_ctx *ctx);
 int ssl_cipher_id_cmp(const void *in_a, const void *in_b);
 int ssl_cipher_ptr_id_cmp(const SSL_CIPHER **ap, const SSL_CIPHER **bp);
-STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs,
-					       STACK_OF(SSL_CIPHER) **skp);
+STACK_OF(SSL_CIPHER) *ssl_bytes_to_cipher_list(SSL *s, const CBS *cbs);
 int ssl_cipher_list_to_bytes(SSL *s, STACK_OF(SSL_CIPHER) *sk, uint8_t *p);
 STACK_OF(SSL_CIPHER) *ssl_create_cipher_list(const SSL_METHOD *meth,
 					     struct ssl_cipher_preference_list_st **pref,
@@ -824,7 +830,7 @@
 int ssl3_send_change_cipher_spec(SSL *s,int state_a,int state_b);
 int ssl3_change_cipher_state(SSL *s,int which);
 void ssl3_cleanup_key_block(SSL *s);
-int ssl3_do_write(SSL *s,int type);
+int ssl3_do_write(SSL *s,int type, enum should_add_to_finished_hash should_add_to_finished_hash);
 int ssl3_send_alert(SSL *s,int level, int desc);
 int ssl3_generate_master_secret(SSL *s, unsigned char *out,
 	unsigned char *p, int len);
@@ -866,7 +872,13 @@
 int	ssl3_setup_write_buffer(SSL *s);
 int	ssl3_release_read_buffer(SSL *s);
 int	ssl3_release_write_buffer(SSL *s);
-int	ssl3_digest_cached_records(SSL *s);
+
+enum should_free_handshake_buffer_t {
+	free_handshake_buffer,
+	dont_free_handshake_buffer,
+};
+int	ssl3_digest_cached_records(SSL *s, enum should_free_handshake_buffer_t);
+
 int	ssl3_new(SSL *s);
 void	ssl3_free(SSL *s);
 int	ssl3_accept(SSL *s);
@@ -886,13 +898,14 @@
 int ssl3_do_change_cipher_spec(SSL *ssl);
 
 void ssl3_set_handshake_header(SSL *s, int htype, unsigned long len);
-int ssl3_handshake_write(SSL *s);
+int ssl3_handshake_write(SSL *s, enum should_add_to_finished_hash should_add_to_finished_hash);
+void ssl3_add_to_finished_hash(SSL *s);
 
 int ssl23_read(SSL *s, void *buf, int len);
 int ssl23_peek(SSL *s, void *buf, int len);
 int ssl23_write(SSL *s, const void *buf, int len);
 
-int dtls1_do_write(SSL *s,int type);
+int dtls1_do_write(SSL *s,int type, enum should_add_to_finished_hash should_add_to_finished_hash);
 int ssl3_read_n(SSL *s, int n, int max, int extend);
 int dtls1_read_bytes(SSL *s, int type, unsigned char *buf, int len, int peek);
 int ssl3_write_pending(SSL *s, int type, const unsigned char *buf,
diff --git a/ssl/ssl_test.c b/ssl/ssl_test.c
index a202273..da9ba4b 100644
--- a/ssl/ssl_test.c
+++ b/ssl/ssl_test.c
@@ -307,20 +307,6 @@
  * filling in missing fields from |kOpenSSLSession|. This includes
  * providing |peer_sha256|, so |peer| is not serialized. */
 static const char kCustomSession[] =
-  "MIIBggIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
-  "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
-  "IWoJgAEBoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29t"
-  "pwcEBWhlbGxvqAcEBXdvcmxkqQUCAwGJwKqBpwSBpBwUQvoeOk0Kg36SYTcLEkXq"
-  "KwOBfF9vE4KX0NxeLwjcDTpsuh3qXEaZ992r1N38VDcyS6P7I6HBYN9BsNHM362z"
-  "ZnY27GpTw+Kwd751CLoXFPoaMOe57dbBpXoro6Pd3BTbf/Tzr88K06yEOTDKPNj3"
-  "+inbMaVigtK4PLyPq+Topyzvx9USFgRvyuoxn0Hgb+R0A3j6SLRuyOdAi4gv7Y5o"
-  "liynrSIEIAYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGBgYGrgMEAQevAwQB"
-  "BLADBAEF";
-
-/* kCustomSession2 is kCustomSession with the old SSLv2-only key_arg
- * field removed. Encoding the decoded version of kCustomSession
- * should not preserve key_arg. */
-static const char kCustomSession2[] =
   "MIIBfwIBAQICAwMEAsAvBCAG5Q1ndq4Yfmbeo1zwLkNRKmCXGdNgWvGT3cskV0yQ"
   "kAQwJlrlzkAWBOWiLj/jJ76D7l+UXoizP2KI2C7I2FccqMmIfFmmkUy32nIJ0mZH"
   "IWoJoQYCBFRDO46iBAICASykAwQBAqUDAgEUphAEDnd3dy5nb29nbGUuY29tpwcE"
@@ -355,18 +341,16 @@
   return 1;
 }
 
-static int test_ssl_session_asn1(const char *input_b64,
-                                 const char *expected_b64) {
+static int test_ssl_session_asn1(const char *input_b64) {
   int ret = 0, len;
-  size_t input_len, expected_len;
-  uint8_t *input = NULL, *expected = NULL, *encoded = NULL;
+  size_t input_len, encoded_len;
+  uint8_t *input = NULL, *encoded = NULL;
   const uint8_t *cptr;
   uint8_t *ptr;
   SSL_SESSION *session = NULL;
 
   /* Decode the input. */
-  if (!decode_base64(&input, &input_len, input_b64) ||
-      !decode_base64(&expected, &expected_len, expected_b64)) {
+  if (!decode_base64(&input, &input_len, input_b64)) {
     goto done;
   }
 
@@ -379,28 +363,41 @@
   }
 
   /* Verify the SSL_SESSION encoding round-trips. */
+  if (!SSL_SESSION_to_bytes(session, &encoded, &encoded_len)) {
+    fprintf(stderr, "SSL_SESSION_to_bytes failed\n");
+    goto done;
+  }
+  if (encoded_len != input_len ||
+      memcmp(input, encoded, input_len) != 0) {
+    fprintf(stderr, "SSL_SESSION_to_bytes did not round-trip\n");
+    goto done;
+  }
+  OPENSSL_free(encoded);
+  encoded = NULL;
+
+  /* Verify the SSL_SESSION encoding round-trips via the legacy API. */
   len = i2d_SSL_SESSION(session, NULL);
-  if (len < 0 || (size_t)len != expected_len) {
+  if (len < 0 || (size_t)len != input_len) {
     fprintf(stderr, "i2d_SSL_SESSION(NULL) returned invalid length\n");
     goto done;
   }
 
-  encoded = OPENSSL_malloc(expected_len);
+  encoded = OPENSSL_malloc(input_len);
   if (encoded == NULL) {
     fprintf(stderr, "malloc failed\n");
     goto done;
   }
   ptr = encoded;
   len = i2d_SSL_SESSION(session, &ptr);
-  if (len < 0 || (size_t)len != expected_len) {
+  if (len < 0 || (size_t)len != input_len) {
     fprintf(stderr, "i2d_SSL_SESSION returned invalid length\n");
     goto done;
   }
-  if (ptr != encoded + expected_len) {
+  if (ptr != encoded + input_len) {
     fprintf(stderr, "i2d_SSL_SESSION did not advance ptr correctly\n");
     goto done;
   }
-  if (memcmp(expected, encoded, expected_len) != 0) {
+  if (memcmp(input, encoded, input_len) != 0) {
     fprintf(stderr, "i2d_SSL_SESSION did not round-trip\n");
     goto done;
   }
@@ -418,9 +415,6 @@
   if (input) {
     OPENSSL_free(input);
   }
-  if (expected) {
-    OPENSSL_free(expected);
-  }
   if (encoded) {
     OPENSSL_free(encoded);
   }
@@ -431,9 +425,8 @@
   SSL_library_init();
 
   if (!test_cipher_rules() ||
-      !test_ssl_session_asn1(kOpenSSLSession, kOpenSSLSession) ||
-      !test_ssl_session_asn1(kCustomSession, kCustomSession2) ||
-      !test_ssl_session_asn1(kCustomSession2, kCustomSession2)) {
+      !test_ssl_session_asn1(kOpenSSLSession) ||
+      !test_ssl_session_asn1(kCustomSession)) {
     return 1;
   }
 
diff --git a/ssl/t1_enc.c b/ssl/t1_enc.c
index dd00d0a..0a4e088 100644
--- a/ssl/t1_enc.c
+++ b/ssl/t1_enc.c
@@ -152,8 +152,6 @@
 			const void *seed1, int seed1_len,
 			const void *seed2, int seed2_len,
 			const void *seed3, int seed3_len,
-			const void *seed4, int seed4_len,
-			const void *seed5, int seed5_len,
 			unsigned char *out, int olen)
 	{
 	int chunk;
@@ -182,10 +180,6 @@
 		goto err;
 	if (seed3 && !EVP_DigestSignUpdate(&ctx,seed3,seed3_len))
 		goto err;
-	if (seed4 && !EVP_DigestSignUpdate(&ctx,seed4,seed4_len))
-		goto err;
-	if (seed5 && !EVP_DigestSignUpdate(&ctx,seed5,seed5_len))
-		goto err;
 	A1_len = EVP_MAX_MD_SIZE;
 	if (!EVP_DigestSignFinal(&ctx,A1,&A1_len))
 		goto err;
@@ -205,10 +199,6 @@
 			goto err;
 		if (seed3 && !EVP_DigestSignUpdate(&ctx,seed3,seed3_len))
 			goto err;
-		if (seed4 && !EVP_DigestSignUpdate(&ctx,seed4,seed4_len))
-			goto err;
-		if (seed5 && !EVP_DigestSignUpdate(&ctx,seed5,seed5_len))
-			goto err;
 
 		if (olen > chunk)
 			{
@@ -246,8 +236,6 @@
 		     const void *seed1, int seed1_len,
 		     const void *seed2, int seed2_len,
 		     const void *seed3, int seed3_len,
-		     const void *seed4, int seed4_len,
-		     const void *seed5, int seed5_len,
 		     const unsigned char *sec, int slen,
 		     unsigned char *out1,
 		     unsigned char *out2, int olen)
@@ -275,7 +263,7 @@
 				goto err;				
 			}
 			if (!tls1_P_hash(md ,S1,len+(slen&1),
-					seed1,seed1_len,seed2,seed2_len,seed3,seed3_len,seed4,seed4_len,seed5,seed5_len,
+					seed1,seed1_len,seed2,seed2_len,seed3,seed3_len,
 					out2,olen))
 				goto err;
 			S1+=len;
@@ -298,7 +286,6 @@
 		 TLS_MD_KEY_EXPANSION_CONST,TLS_MD_KEY_EXPANSION_CONST_SIZE,
 		 s->s3->server_random,SSL3_RANDOM_SIZE,
 		 s->s3->client_random,SSL3_RANDOM_SIZE,
-		 NULL,0,NULL,0,
 		 s->session->master_key,s->session->master_key_length,
 		 km,tmp,num);
 #ifdef KSSL_DEBUG
@@ -1011,8 +998,8 @@
 	EVP_MD_CTX ctx, *d=NULL;
 	int i;
 
-	if (s->s3->handshake_buffer) 
-		if (!ssl3_digest_cached_records(s))
+	if (s->s3->handshake_buffer)
+		if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 			return 0;
 
 	for (i=0;i<SSL_MAX_DIGEST;i++) 
@@ -1093,7 +1080,7 @@
 	int digests_len;
 
 	if (s->s3->handshake_buffer)
-		if (!ssl3_digest_cached_records(s))
+		if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 			return 0;
 
 	digests_len = tls1_handshake_digest(s, buf, sizeof(buf));
@@ -1104,7 +1091,7 @@
 		}
 		
 	if (!tls1_PRF(ssl_get_algorithm2(s),
-			str,slen, buf, digests_len, NULL,0, NULL,0, NULL,0,
+			str,slen, buf, digests_len, NULL,0,
 			s->session->master_key,s->session->master_key_length,
 			out,buf2,sizeof buf2))
 		err = 1;
@@ -1212,22 +1199,57 @@
 	     int len)
 	{
 	unsigned char buff[SSL_MAX_MASTER_KEY_LENGTH];
-	const void *co = NULL, *so = NULL;
-	int col = 0, sol = 0;
-
 
 #ifdef KSSL_DEBUG
 	printf ("tls1_generate_master_secret(%p,%p, %p, %d)\n", s,out, p,len);
 #endif	/* KSSL_DEBUG */
 
-	tls1_PRF(ssl_get_algorithm2(s),
-		TLS_MD_MASTER_SECRET_CONST,TLS_MD_MASTER_SECRET_CONST_SIZE,
-		s->s3->client_random,SSL3_RANDOM_SIZE,
-		co, col,
-		s->s3->server_random,SSL3_RANDOM_SIZE,
-		so, sol,
-		p,len,
-		s->session->master_key,buff,sizeof buff);
+	if (s->s3->tmp.extended_master_secret)
+		{
+		uint8_t digests[2*EVP_MAX_MD_SIZE];
+		int digests_len;
+
+		if (s->s3->handshake_buffer)
+			{
+			/* The master secret is based on the handshake hash
+			 * just after sending the ClientKeyExchange. However,
+			 * we might have a client certificate to send, in which
+			 * case we might need different hashes for the
+			 * verification and thus still need the handshake
+			 * buffer around. Keeping both a handshake buffer *and*
+			 * running hashes isn't yet supported so, when it comes
+			 * to calculating the Finished hash, we'll have to hash
+			 * the handshake buffer again. */
+			if (!ssl3_digest_cached_records(s, dont_free_handshake_buffer))
+				return 0;
+			}
+
+		digests_len = tls1_handshake_digest(s, digests, sizeof(digests));
+
+		if (digests_len == -1)
+			{
+			return 0;
+			}
+
+		tls1_PRF(ssl_get_algorithm2(s),
+			TLS_MD_EXTENDED_MASTER_SECRET_CONST,
+			TLS_MD_EXTENDED_MASTER_SECRET_CONST_SIZE,
+			digests, digests_len,
+			NULL, 0,
+			p, len,
+			s->session->master_key,
+			buff, sizeof(buff));
+		}
+	else
+		{
+		tls1_PRF(ssl_get_algorithm2(s),
+			TLS_MD_MASTER_SECRET_CONST,TLS_MD_MASTER_SECRET_CONST_SIZE,
+			s->s3->client_random,SSL3_RANDOM_SIZE,
+			s->s3->server_random,SSL3_RANDOM_SIZE,
+			p, len,
+			s->session->master_key,buff,sizeof buff);
+		}
+
 #ifdef SSL_DEBUG
 	fprintf(stderr, "Premaster Secret:\n");
 	BIO_dump_fp(stderr, (char *)p, len);
@@ -1330,8 +1352,6 @@
 		      val, vallen,
 		      NULL, 0,
 		      NULL, 0,
-		      NULL, 0,
-		      NULL, 0,
 		      s->session->master_key,s->session->master_key_length,
 		      out,buff,olen);
 
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 4b13cfe..0972515 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -140,7 +140,8 @@
 	0,
 	SSL3_HM_HEADER_LENGTH,
 	ssl3_set_handshake_header,
-	ssl3_handshake_write
+	ssl3_handshake_write,
+	ssl3_add_to_finished_hash,
 	};
 
 SSL3_ENC_METHOD TLSv1_1_enc_data={
@@ -159,7 +160,8 @@
 	SSL_ENC_FLAG_EXPLICIT_IV,
 	SSL3_HM_HEADER_LENGTH,
 	ssl3_set_handshake_header,
-	ssl3_handshake_write
+	ssl3_handshake_write,
+	ssl3_add_to_finished_hash,
 	};
 
 SSL3_ENC_METHOD TLSv1_2_enc_data={
@@ -179,7 +181,8 @@
 		|SSL_ENC_FLAG_TLS1_2_CIPHERS,
 	SSL3_HM_HEADER_LENGTH,
 	ssl3_set_handshake_header,
-	ssl3_handshake_write
+	ssl3_handshake_write,
+	ssl3_add_to_finished_hash,
 	};
 
 static int compare_uint16_t(const void *p1, const void *p2)
@@ -978,6 +981,15 @@
           ret += el;
         }
 
+	/* Add extended master secret. */
+	if (s->version != SSL3_VERSION)
+		{
+		if (limit - ret - 4 < 0)
+			return NULL;
+		s2n(TLSEXT_TYPE_extended_master_secret,ret);
+		s2n(0,ret);
+		}
+
 	if (!(SSL_get_options(s) & SSL_OP_NO_TICKET))
 		{
 		int ticklen;
@@ -1246,6 +1258,14 @@
           ret += el;
         }
 
+	if (s->s3->tmp.extended_master_secret)
+		{
+		if ((long)(limit - ret - 4) < 0) return NULL;
+
+		s2n(TLSEXT_TYPE_extended_master_secret,ret);
+		s2n(0,ret);
+		}
+
 	if (using_ecc)
 		{
 		const unsigned char *plist;
@@ -1423,6 +1443,7 @@
 	s->should_ack_sni = 0;
 	s->s3->next_proto_neg_seen = 0;
 	s->s3->tmp.certificate_status_expected = 0;
+	s->s3->tmp.extended_master_secret = 0;
 
 	if (s->s3->alpn_selected)
 		{
@@ -1782,6 +1803,18 @@
 			if (!ssl_parse_clienthello_use_srtp_ext(s, &extension, out_alert))
 				return 0;
                         }
+
+		else if (type == TLSEXT_TYPE_extended_master_secret &&
+			 s->version != SSL3_VERSION)
+			{
+			if (CBS_len(&extension) != 0)
+				{
+				*out_alert = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+
+			s->s3->tmp.extended_master_secret = 1;
+			}
 		}
 
 	ri_check:
@@ -1851,6 +1884,7 @@
 
 	s->tlsext_ticket_expected = 0;
 	s->s3->tmp.certificate_status_expected = 0;
+	s->s3->tmp.extended_master_secret = 0;
 
 	if (s->s3->alpn_selected)
 		{
@@ -2086,6 +2120,20 @@
                         if (!ssl_parse_serverhello_use_srtp_ext(s, &extension, out_alert))
                                 return 0;
                         }
+
+		else if (type == TLSEXT_TYPE_extended_master_secret)
+			{
+			if (/* It is invalid for the server to select EMS and
+			       SSLv3. */
+			    s->version == SSL3_VERSION ||
+			    CBS_len(&extension) != 0)
+				{
+				*out_alert = SSL_AD_DECODE_ERROR;
+				return 0;
+				}
+
+			s->s3->tmp.extended_master_secret = 1;
+			}
 		}
 
 	if (!s->hit && tlsext_servername == 1)
@@ -2779,7 +2827,7 @@
 	static const char kClientIDMagic[] = "TLS Channel ID signature";
 
 	if (s->s3->handshake_buffer)
-		if (!ssl3_digest_cached_records(s))
+		if (!ssl3_digest_cached_records(s, free_handshake_buffer))
 			return 0;
 
 	EVP_DigestUpdate(md, kClientIDMagic, sizeof(kClientIDMagic));
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 6b27e26..1cf81a7 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 
 #include <openssl/bio.h>
+#include <openssl/buf.h>
 #include <openssl/bytestring.h>
 #include <openssl/ssl.h>
 
@@ -180,6 +181,48 @@
   return 1;
 }
 
+static unsigned psk_client_callback(SSL *ssl, const char *hint,
+                                    char *out_identity,
+                                    unsigned max_identity_len,
+                                    uint8_t *out_psk, unsigned max_psk_len) {
+  const TestConfig *config = GetConfigPtr(ssl);
+
+  if (strcmp(hint ? hint : "", config->psk_identity.c_str()) != 0) {
+    fprintf(stderr, "Server PSK hint did not match.\n");
+    return 0;
+  }
+
+  // Account for the trailing '\0' for the identity.
+  if (config->psk_identity.size() >= max_identity_len ||
+      config->psk.size() > max_psk_len) {
+    fprintf(stderr, "PSK buffers too small\n");
+    return 0;
+  }
+
+  BUF_strlcpy(out_identity, config->psk_identity.c_str(),
+              max_identity_len);
+  memcpy(out_psk, config->psk.data(), config->psk.size());
+  return config->psk.size();
+}
+
+static unsigned psk_server_callback(SSL *ssl, const char *identity,
+                                    uint8_t *out_psk, unsigned max_psk_len) {
+  const TestConfig *config = GetConfigPtr(ssl);
+
+  if (strcmp(identity, config->psk_identity.c_str()) != 0) {
+    fprintf(stderr, "Client PSK identity did not match.\n");
+    return 0;
+  }
+
+  if (config->psk.size() > max_psk_len) {
+    fprintf(stderr, "PSK buffers too small\n");
+    return 0;
+  }
+
+  memcpy(out_psk, config->psk.data(), config->psk.size());
+  return config->psk.size();
+}
+
 static SSL_CTX *setup_ctx(const TestConfig *config) {
   SSL_CTX *ssl_ctx = NULL;
   DH *dh = NULL;
@@ -369,6 +412,16 @@
     SSL_set_alpn_protos(ssl, (const uint8_t *)config->advertise_alpn.data(),
                         config->advertise_alpn.size());
   }
+  if (!config->psk.empty()) {
+    SSL_set_psk_client_callback(ssl, psk_client_callback);
+    SSL_set_psk_server_callback(ssl, psk_server_callback);
+  }
+  if (!config->psk_identity.empty()) {
+    if (!SSL_use_psk_identity_hint(ssl, config->psk_identity.c_str())) {
+      BIO_print_errors_fp(stdout);
+      return 1;
+    }
+  }
 
   BIO *bio = BIO_new_fd(fd, 1 /* take ownership */);
   if (bio == NULL) {
@@ -482,6 +535,13 @@
     }
   }
 
+  if (config->expect_extended_master_secret) {
+    if (!ssl->session->extended_master_secret) {
+      fprintf(stderr, "No EMS for session when expected");
+      return 2;
+    }
+  }
+
   if (config->write_different_record_sizes) {
     if (config->is_dtls) {
       fprintf(stderr, "write_different_record_sizes not supported for DTLS\n");
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index 6cd0de9..5a3ac80 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/ssl/test/runner/cipher_suites.go
@@ -57,6 +57,9 @@
 	// suiteNoDTLS indicates that the cipher suite cannot be used
 	// in DTLS.
 	suiteNoDTLS
+	// suitePSK indicates that the cipher suite authenticates with
+	// a pre-shared key rather than a server private key.
+	suitePSK
 )
 
 // A cipherSuite is a specific combination of key agreement, cipher and MAC
@@ -109,6 +112,10 @@
 	{TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, ecdheRSAKA, suiteECDHE, cipher3DES, macSHA1, nil},
 	{TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, dheRSAKA, 0, cipher3DES, macSHA1, nil},
 	{TLS_RSA_WITH_3DES_EDE_CBC_SHA, 24, 20, 8, rsaKA, 0, cipher3DES, macSHA1, nil},
+	{TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdhePSKKA, suiteECDHE | suiteTLS12 | suitePSK, nil, nil, aeadAESGCM},
+	{TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil},
+	{TLS_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
+	{TLS_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, pskKA, suitePSK, cipherAES, macSHA1, nil},
 }
 
 func cipherRC4(key, iv []byte, isRead bool) interface{} {
@@ -287,7 +294,7 @@
 
 func ecdheECDSAKA(version uint16) keyAgreement {
 	return &ecdheKeyAgreement{
-		signedKeyAgreement: signedKeyAgreement{
+		auth: &signedKeyAgreement{
 			sigType: signatureECDSA,
 			version: version,
 		},
@@ -296,7 +303,7 @@
 
 func ecdheRSAKA(version uint16) keyAgreement {
 	return &ecdheKeyAgreement{
-		signedKeyAgreement: signedKeyAgreement{
+		auth: &signedKeyAgreement{
 			sigType: signatureRSA,
 			version: version,
 		},
@@ -305,13 +312,27 @@
 
 func dheRSAKA(version uint16) keyAgreement {
 	return &dheKeyAgreement{
-		signedKeyAgreement: signedKeyAgreement{
+		auth: &signedKeyAgreement{
 			sigType: signatureRSA,
 			version: version,
 		},
 	}
 }
 
+func pskKA(version uint16) keyAgreement {
+	return &pskKeyAgreement{
+		base: &nilKeyAgreement{},
+	}
+}
+
+func ecdhePSKKA(version uint16) keyAgreement {
+	return &pskKeyAgreement{
+		base: &ecdheKeyAgreement{
+			auth: &nilKeyAgreementAuthentication{},
+		},
+	}
+}
+
 // mutualCipherSuite returns a cipherSuite given a list of supported
 // ciphersuites and the id requested by the peer.
 func mutualCipherSuite(have []uint16, want uint16) *cipherSuite {
@@ -343,6 +364,9 @@
 	TLS_RSA_WITH_AES_256_CBC_SHA256         uint16 = 0x003d
 	TLS_DHE_RSA_WITH_AES_128_CBC_SHA256     uint16 = 0x0067
 	TLS_DHE_RSA_WITH_AES_256_CBC_SHA256     uint16 = 0x006b
+	TLS_PSK_WITH_RC4_128_SHA                uint16 = 0x008a
+	TLS_PSK_WITH_AES_128_CBC_SHA            uint16 = 0x008c
+	TLS_PSK_WITH_AES_256_CBC_SHA            uint16 = 0x008d
 	TLS_RSA_WITH_AES_128_GCM_SHA256         uint16 = 0x009c
 	TLS_RSA_WITH_AES_256_GCM_SHA384         uint16 = 0x009d
 	TLS_DHE_RSA_WITH_AES_128_GCM_SHA256     uint16 = 0x009e
@@ -364,3 +388,8 @@
 	TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384   uint16 = 0xc030
 	fallbackSCSV                            uint16 = 0x5600
 )
+
+// Additional cipher suite IDs, not IANA-assigned.
+const (
+	TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256 uint16 = 0xcafe
+)
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 8b2c750..4aa21bb 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -71,16 +71,17 @@
 
 // TLS extension numbers
 const (
-	extensionServerName          uint16 = 0
-	extensionStatusRequest       uint16 = 5
-	extensionSupportedCurves     uint16 = 10
-	extensionSupportedPoints     uint16 = 11
-	extensionSignatureAlgorithms uint16 = 13
-	extensionALPN                uint16 = 16
-	extensionSessionTicket       uint16 = 35
-	extensionNextProtoNeg        uint16 = 13172 // not IANA assigned
-	extensionRenegotiationInfo   uint16 = 0xff01
-	extensionChannelID           uint16 = 30032 // not IANA assigned
+	extensionServerName           uint16 = 0
+	extensionStatusRequest        uint16 = 5
+	extensionSupportedCurves      uint16 = 10
+	extensionSupportedPoints      uint16 = 11
+	extensionSignatureAlgorithms  uint16 = 13
+	extensionALPN                 uint16 = 16
+	extensionExtendedMasterSecret uint16 = 23
+	extensionSessionTicket        uint16 = 35
+	extensionNextProtoNeg         uint16 = 13172 // not IANA assigned
+	extensionRenegotiationInfo    uint16 = 0xff01
+	extensionChannelID            uint16 = 30032 // not IANA assigned
 )
 
 // TLS signaling cipher suite values
@@ -189,12 +190,13 @@
 // ClientSessionState contains the state needed by clients to resume TLS
 // sessions.
 type ClientSessionState struct {
-	sessionTicket      []uint8             // Encrypted ticket used for session resumption with server
-	vers               uint16              // SSL/TLS version negotiated for the session
-	cipherSuite        uint16              // Ciphersuite negotiated for the session
-	masterSecret       []byte              // MasterSecret generated by client on a full handshake
-	handshakeHash      []byte              // Handshake hash for Channel ID purposes.
-	serverCertificates []*x509.Certificate // Certificate chain presented by the server
+	sessionTicket        []uint8             // Encrypted ticket used for session resumption with server
+	vers                 uint16              // SSL/TLS version negotiated for the session
+	cipherSuite          uint16              // Ciphersuite negotiated for the session
+	masterSecret         []byte              // MasterSecret generated by client on a full handshake
+	handshakeHash        []byte              // Handshake hash for Channel ID purposes.
+	serverCertificates   []*x509.Certificate // Certificate chain presented by the server
+	extendedMasterSecret bool                // Whether an extended master secret was used to generate the session
 }
 
 // ClientSessionCache is a cache of ClientSessionState objects that can be used
@@ -323,6 +325,14 @@
 	// returned in the ConnectionState.
 	RequestChannelID bool
 
+	// PreSharedKey, if not nil, is the pre-shared key to use with
+	// the PSK cipher suites.
+	PreSharedKey []byte
+
+	// PreSharedKeyIdentity, if not empty, is the identity to use
+	// with the PSK cipher suites.
+	PreSharedKeyIdentity string
+
 	// Bugs specifies optional misbehaviour to be used for testing other
 	// implementations.
 	Bugs ProtocolBugs
@@ -472,6 +482,14 @@
 	// OversizedSessionId causes the session id that is sent with a ticket
 	// resumption attempt to be too large (33 bytes).
 	OversizedSessionId bool
+
+	// RequireExtendedMasterSecret, if true, requires that the peer support
+	// the extended master secret option.
+	RequireExtendedMasterSecret bool
+
+	// NoExtendedMasterSecret causes the client and server to behave is if
+	// they didn't support an extended master secret.
+	NoExtendedMasterSecret bool
 }
 
 func (c *Config) serverInit() {
@@ -727,9 +745,10 @@
 }
 
 func initDefaultCipherSuites() {
-	varDefaultCipherSuites = make([]uint16, len(cipherSuites))
-	for i, suite := range cipherSuites {
-		varDefaultCipherSuites[i] = suite.id
+	for _, suite := range cipherSuites {
+		if suite.flags&suitePSK == 0 {
+			varDefaultCipherSuites = append(varDefaultCipherSuites, suite.id)
+		}
 	}
 }
 
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 9f0c328..3ce6c76 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -29,16 +29,17 @@
 	isClient bool
 
 	// constant after handshake; protected by handshakeMutex
-	handshakeMutex    sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
-	handshakeErr      error      // error resulting from handshake
-	vers              uint16     // TLS version
-	haveVers          bool       // version has been negotiated
-	config            *Config    // configuration passed to constructor
-	handshakeComplete bool
-	didResume         bool // whether this connection was a session resumption
-	cipherSuite       uint16
-	ocspResponse      []byte // stapled OCSP response
-	peerCertificates  []*x509.Certificate
+	handshakeMutex       sync.Mutex // handshakeMutex < in.Mutex, out.Mutex, errMutex
+	handshakeErr         error      // error resulting from handshake
+	vers                 uint16     // TLS version
+	haveVers             bool       // version has been negotiated
+	config               *Config    // configuration passed to constructor
+	handshakeComplete    bool
+	didResume            bool // whether this connection was a session resumption
+	extendedMasterSecret bool // whether this session used an extended master secret
+	cipherSuite          uint16
+	ocspResponse         []byte // stapled OCSP response
+	peerCertificates     []*x509.Certificate
 	// verifiedChains contains the certificate chains that we built, as
 	// opposed to the ones presented by the server.
 	verifiedChains [][]*x509.Certificate
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index f4cadc2..11a1ed3 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -56,26 +56,31 @@
 	}
 
 	hello := &clientHelloMsg{
-		isDTLS:              c.isDTLS,
-		vers:                c.config.maxVersion(),
-		compressionMethods:  []uint8{compressionNone},
-		random:              make([]byte, 32),
-		ocspStapling:        true,
-		serverName:          c.config.ServerName,
-		supportedCurves:     c.config.curvePreferences(),
-		supportedPoints:     []uint8{pointFormatUncompressed},
-		nextProtoNeg:        len(c.config.NextProtos) > 0,
-		secureRenegotiation: true,
-		alpnProtocols:       c.config.NextProtos,
-		duplicateExtension:  c.config.Bugs.DuplicateExtension,
-		channelIDSupported:  c.config.ChannelID != nil,
-		npnLast:             c.config.Bugs.SwapNPNAndALPN,
+		isDTLS:               c.isDTLS,
+		vers:                 c.config.maxVersion(),
+		compressionMethods:   []uint8{compressionNone},
+		random:               make([]byte, 32),
+		ocspStapling:         true,
+		serverName:           c.config.ServerName,
+		supportedCurves:      c.config.curvePreferences(),
+		supportedPoints:      []uint8{pointFormatUncompressed},
+		nextProtoNeg:         len(c.config.NextProtos) > 0,
+		secureRenegotiation:  true,
+		alpnProtocols:        c.config.NextProtos,
+		duplicateExtension:   c.config.Bugs.DuplicateExtension,
+		channelIDSupported:   c.config.ChannelID != nil,
+		npnLast:              c.config.Bugs.SwapNPNAndALPN,
+		extendedMasterSecret: c.config.maxVersion() >= VersionTLS10,
 	}
 
 	if c.config.Bugs.SendClientVersion != 0 {
 		hello.vers = c.config.Bugs.SendClientVersion
 	}
 
+	if c.config.Bugs.NoExtendedMasterSecret {
+		hello.extendedMasterSecret = false
+	}
+
 	possibleCipherSuites := c.config.cipherSuites()
 	hello.cipherSuites = make([]uint16, 0, len(possibleCipherSuites))
 
@@ -303,60 +308,65 @@
 func (hs *clientHandshakeState) doFullHandshake() error {
 	c := hs.c
 
-	msg, err := c.readHandshake()
-	if err != nil {
-		return err
-	}
-	certMsg, ok := msg.(*certificateMsg)
-	if !ok || len(certMsg.certificates) == 0 {
-		c.sendAlert(alertUnexpectedMessage)
-		return unexpectedMessageError(certMsg, msg)
-	}
-	hs.writeServerHash(certMsg.marshal())
-
-	certs := make([]*x509.Certificate, len(certMsg.certificates))
-	for i, asn1Data := range certMsg.certificates {
-		cert, err := x509.ParseCertificate(asn1Data)
+	var leaf *x509.Certificate
+	if hs.suite.flags&suitePSK == 0 {
+		msg, err := c.readHandshake()
 		if err != nil {
-			c.sendAlert(alertBadCertificate)
-			return errors.New("tls: failed to parse certificate from server: " + err.Error())
-		}
-		certs[i] = cert
-	}
-
-	if !c.config.InsecureSkipVerify {
-		opts := x509.VerifyOptions{
-			Roots:         c.config.RootCAs,
-			CurrentTime:   c.config.time(),
-			DNSName:       c.config.ServerName,
-			Intermediates: x509.NewCertPool(),
-		}
-
-		for i, cert := range certs {
-			if i == 0 {
-				continue
-			}
-			opts.Intermediates.AddCert(cert)
-		}
-		c.verifiedChains, err = certs[0].Verify(opts)
-		if err != nil {
-			c.sendAlert(alertBadCertificate)
 			return err
 		}
-	}
 
-	switch certs[0].PublicKey.(type) {
-	case *rsa.PublicKey, *ecdsa.PublicKey:
-		break
-	default:
-		c.sendAlert(alertUnsupportedCertificate)
-		return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", certs[0].PublicKey)
-	}
+		certMsg, ok := msg.(*certificateMsg)
+		if !ok || len(certMsg.certificates) == 0 {
+			c.sendAlert(alertUnexpectedMessage)
+			return unexpectedMessageError(certMsg, msg)
+		}
+		hs.writeServerHash(certMsg.marshal())
 
-	c.peerCertificates = certs
+		certs := make([]*x509.Certificate, len(certMsg.certificates))
+		for i, asn1Data := range certMsg.certificates {
+			cert, err := x509.ParseCertificate(asn1Data)
+			if err != nil {
+				c.sendAlert(alertBadCertificate)
+				return errors.New("tls: failed to parse certificate from server: " + err.Error())
+			}
+			certs[i] = cert
+		}
+		leaf = certs[0]
+
+		if !c.config.InsecureSkipVerify {
+			opts := x509.VerifyOptions{
+				Roots:         c.config.RootCAs,
+				CurrentTime:   c.config.time(),
+				DNSName:       c.config.ServerName,
+				Intermediates: x509.NewCertPool(),
+			}
+
+			for i, cert := range certs {
+				if i == 0 {
+					continue
+				}
+				opts.Intermediates.AddCert(cert)
+			}
+			c.verifiedChains, err = leaf.Verify(opts)
+			if err != nil {
+				c.sendAlert(alertBadCertificate)
+				return err
+			}
+		}
+
+		switch leaf.PublicKey.(type) {
+		case *rsa.PublicKey, *ecdsa.PublicKey:
+			break
+		default:
+			c.sendAlert(alertUnsupportedCertificate)
+			return fmt.Errorf("tls: server's certificate contains an unsupported type of public key: %T", leaf.PublicKey)
+		}
+
+		c.peerCertificates = certs
+	}
 
 	if hs.serverHello.ocspStapling {
-		msg, err = c.readHandshake()
+		msg, err := c.readHandshake()
 		if err != nil {
 			return err
 		}
@@ -372,7 +382,7 @@
 		}
 	}
 
-	msg, err = c.readHandshake()
+	msg, err := c.readHandshake()
 	if err != nil {
 		return err
 	}
@@ -382,7 +392,7 @@
 	skx, ok := msg.(*serverKeyExchangeMsg)
 	if ok {
 		hs.writeServerHash(skx.marshal())
-		err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, certs[0], skx)
+		err = keyAgreement.processServerKeyExchange(c.config, hs.hello, hs.serverHello, leaf, skx)
 		if err != nil {
 			c.sendAlert(alertUnexpectedMessage)
 			return err
@@ -483,7 +493,7 @@
 	// Certificate message, even if it's empty because we don't have a
 	// certificate to send.
 	if certRequested {
-		certMsg = new(certificateMsg)
+		certMsg := new(certificateMsg)
 		if chainToSend != nil {
 			certMsg.certificates = chainToSend.Certificate
 		}
@@ -491,7 +501,7 @@
 		c.writeRecord(recordTypeHandshake, certMsg.marshal())
 	}
 
-	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, certs[0])
+	preMasterSecret, ckx, err := keyAgreement.generateClientKeyExchange(c.config, hs.hello, leaf)
 	if err != nil {
 		c.sendAlert(alertInternalError)
 		return err
@@ -503,7 +513,15 @@
 		c.writeRecord(recordTypeHandshake, ckx.marshal())
 	}
 
-	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+	if hs.serverHello.extendedMasterSecret && c.vers >= VersionTLS10 {
+		hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash)
+		c.extendedMasterSecret = true
+	} else {
+		if c.config.Bugs.RequireExtendedMasterSecret {
+			return errors.New("tls: extended master secret required but not supported by peer")
+		}
+		hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.hello.random, hs.serverHello.random)
+	}
 
 	if chainToSend != nil {
 		var signed []byte
@@ -629,6 +647,7 @@
 		// Restore masterSecret and peerCerts from previous state
 		hs.masterSecret = hs.session.masterSecret
 		c.peerCertificates = hs.session.serverCertificates
+		c.extendedMasterSecret = hs.session.extendedMasterSecret
 		hs.finishedHash.discardHandshakeBuffer()
 		return true, nil
 	}
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 136360d..1114a6f 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -7,27 +7,28 @@
 import "bytes"
 
 type clientHelloMsg struct {
-	raw                 []byte
-	isDTLS              bool
-	vers                uint16
-	random              []byte
-	sessionId           []byte
-	cookie              []byte
-	cipherSuites        []uint16
-	compressionMethods  []uint8
-	nextProtoNeg        bool
-	serverName          string
-	ocspStapling        bool
-	supportedCurves     []CurveID
-	supportedPoints     []uint8
-	ticketSupported     bool
-	sessionTicket       []uint8
-	signatureAndHashes  []signatureAndHash
-	secureRenegotiation bool
-	alpnProtocols       []string
-	duplicateExtension  bool
-	channelIDSupported  bool
-	npnLast             bool
+	raw                  []byte
+	isDTLS               bool
+	vers                 uint16
+	random               []byte
+	sessionId            []byte
+	cookie               []byte
+	cipherSuites         []uint16
+	compressionMethods   []uint8
+	nextProtoNeg         bool
+	serverName           string
+	ocspStapling         bool
+	supportedCurves      []CurveID
+	supportedPoints      []uint8
+	ticketSupported      bool
+	sessionTicket        []uint8
+	signatureAndHashes   []signatureAndHash
+	secureRenegotiation  bool
+	alpnProtocols        []string
+	duplicateExtension   bool
+	channelIDSupported   bool
+	npnLast              bool
+	extendedMasterSecret bool
 }
 
 func (m *clientHelloMsg) equal(i interface{}) bool {
@@ -56,7 +57,8 @@
 		eqStrings(m.alpnProtocols, m1.alpnProtocols) &&
 		m.duplicateExtension == m1.duplicateExtension &&
 		m.channelIDSupported == m1.channelIDSupported &&
-		m.npnLast == m1.npnLast
+		m.npnLast == m1.npnLast &&
+		m.extendedMasterSecret == m1.extendedMasterSecret
 }
 
 func (m *clientHelloMsg) marshal() []byte {
@@ -118,6 +120,9 @@
 		}
 		numExtensions++
 	}
+	if m.extendedMasterSecret {
+		numExtensions++
+	}
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
 		length += 2 + extensionsLength
@@ -319,6 +324,12 @@
 		z[1] = 0xff
 		z = z[4:]
 	}
+	if m.extendedMasterSecret {
+		// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+		z[0] = byte(extensionExtendedMasterSecret >> 8)
+		z[1] = byte(extensionExtendedMasterSecret & 0xff)
+		z = z[4:]
+	}
 
 	m.raw = x
 
@@ -385,6 +396,7 @@
 	m.sessionTicket = nil
 	m.signatureAndHashes = nil
 	m.alpnProtocols = nil
+	m.extendedMasterSecret = false
 
 	if len(data) == 0 {
 		// ClientHello is optionally followed by extension data
@@ -517,6 +529,11 @@
 				return false
 			}
 			m.channelIDSupported = true
+		case extensionExtendedMasterSecret:
+			if length != 0 {
+				return false
+			}
+			m.extendedMasterSecret = true
 		}
 		data = data[length:]
 	}
@@ -525,21 +542,22 @@
 }
 
 type serverHelloMsg struct {
-	raw                 []byte
-	isDTLS              bool
-	vers                uint16
-	random              []byte
-	sessionId           []byte
-	cipherSuite         uint16
-	compressionMethod   uint8
-	nextProtoNeg        bool
-	nextProtos          []string
-	ocspStapling        bool
-	ticketSupported     bool
-	secureRenegotiation bool
-	alpnProtocol        string
-	duplicateExtension  bool
-	channelIDRequested  bool
+	raw                  []byte
+	isDTLS               bool
+	vers                 uint16
+	random               []byte
+	sessionId            []byte
+	cipherSuite          uint16
+	compressionMethod    uint8
+	nextProtoNeg         bool
+	nextProtos           []string
+	ocspStapling         bool
+	ticketSupported      bool
+	secureRenegotiation  bool
+	alpnProtocol         string
+	duplicateExtension   bool
+	channelIDRequested   bool
+	extendedMasterSecret bool
 }
 
 func (m *serverHelloMsg) equal(i interface{}) bool {
@@ -562,7 +580,8 @@
 		m.secureRenegotiation == m1.secureRenegotiation &&
 		m.alpnProtocol == m1.alpnProtocol &&
 		m.duplicateExtension == m1.duplicateExtension &&
-		m.channelIDRequested == m1.channelIDRequested
+		m.channelIDRequested == m1.channelIDRequested &&
+		m.extendedMasterSecret == m1.extendedMasterSecret
 }
 
 func (m *serverHelloMsg) marshal() []byte {
@@ -606,6 +625,9 @@
 		extensionsLength += 2 + 1 + alpnLen
 		numExtensions++
 	}
+	if m.extendedMasterSecret {
+		numExtensions++
+	}
 
 	if numExtensions > 0 {
 		extensionsLength += 4 * numExtensions
@@ -699,6 +721,11 @@
 		z[1] = 0xff
 		z = z[4:]
 	}
+	if m.extendedMasterSecret {
+		z[0] = byte(extensionExtendedMasterSecret >> 8)
+		z[1] = byte(extensionExtendedMasterSecret & 0xff)
+		z = z[4:]
+	}
 
 	m.raw = x
 
@@ -730,6 +757,7 @@
 	m.ocspStapling = false
 	m.ticketSupported = false
 	m.alpnProtocol = ""
+	m.extendedMasterSecret = false
 
 	if len(data) == 0 {
 		// ServerHello is optionally followed by extension data
@@ -805,6 +833,11 @@
 				return false
 			}
 			m.channelIDRequested = true
+		case extensionExtendedMasterSecret:
+			if length != 0 {
+				return false
+			}
+			m.extendedMasterSecret = true
 		}
 		data = data[length:]
 	}
diff --git a/ssl/test/runner/handshake_server.go b/ssl/test/runner/handshake_server.go
index 1eb3f11..4bf8f1c 100644
--- a/ssl/test/runner/handshake_server.go
+++ b/ssl/test/runner/handshake_server.go
@@ -237,6 +237,7 @@
 			hs.hello.nextProtos = config.NextProtos
 		}
 	}
+	hs.hello.extendedMasterSecret = c.vers >= VersionTLS10 && hs.clientHello.extendedMasterSecret && !c.config.Bugs.NoExtendedMasterSecret
 
 	if len(config.Certificates) == 0 {
 		c.sendAlert(alertInternalError)
@@ -373,6 +374,7 @@
 	}
 
 	hs.masterSecret = hs.sessionState.masterSecret
+	c.extendedMasterSecret = hs.sessionState.extendedMasterSecret
 
 	return nil
 }
@@ -381,12 +383,14 @@
 	config := hs.c.config
 	c := hs.c
 
-	if hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
+	isPSK := hs.suite.flags&suitePSK != 0
+	if !isPSK && hs.clientHello.ocspStapling && len(hs.cert.OCSPStaple) > 0 {
 		hs.hello.ocspStapling = true
 	}
 
 	hs.hello.ticketSupported = hs.clientHello.ticketSupported && !config.SessionTicketsDisabled
 	hs.hello.cipherSuite = hs.suite.id
+	c.extendedMasterSecret = hs.hello.extendedMasterSecret
 
 	hs.finishedHash = newFinishedHash(c.vers, hs.suite)
 	hs.writeClientHash(hs.clientHello.marshal())
@@ -394,11 +398,13 @@
 
 	c.writeRecord(recordTypeHandshake, hs.hello.marshal())
 
-	certMsg := new(certificateMsg)
-	certMsg.certificates = hs.cert.Certificate
-	if !config.Bugs.UnauthenticatedECDH {
-		hs.writeServerHash(certMsg.marshal())
-		c.writeRecord(recordTypeHandshake, certMsg.marshal())
+	if !isPSK {
+		certMsg := new(certificateMsg)
+		certMsg.certificates = hs.cert.Certificate
+		if !config.Bugs.UnauthenticatedECDH {
+			hs.writeServerHash(certMsg.marshal())
+			c.writeRecord(recordTypeHandshake, certMsg.marshal())
+		}
 	}
 
 	if hs.hello.ocspStapling {
@@ -463,6 +469,7 @@
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
 	if config.ClientAuth >= RequestClientCert {
+		var certMsg *certificateMsg
 		if certMsg, ok = msg.(*certificateMsg); !ok {
 			c.sendAlert(alertUnexpectedMessage)
 			return unexpectedMessageError(certMsg, msg)
@@ -502,7 +509,14 @@
 		c.sendAlert(alertHandshakeFailure)
 		return err
 	}
-	hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+	if c.extendedMasterSecret {
+		hs.masterSecret = extendedMasterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.finishedHash)
+	} else {
+		if c.config.Bugs.RequireExtendedMasterSecret {
+			return errors.New("tls: extended master secret required but not supported by peer")
+		}
+		hs.masterSecret = masterFromPreMasterSecret(c.vers, hs.suite, preMasterSecret, hs.clientHello.random, hs.hello.random)
+	}
 
 	// If we received a client cert in response to our certificate request message,
 	// the client will send us a certificateVerifyMsg immediately after the
diff --git a/ssl/test/runner/key_agreement.go b/ssl/test/runner/key_agreement.go
index f8ba1f8..af54a8f 100644
--- a/ssl/test/runner/key_agreement.go
+++ b/ssl/test/runner/key_agreement.go
@@ -187,8 +187,29 @@
 
 }
 
-// signedKeyAgreement implements helper functions for key agreement
-// methods that involve signed parameters in the ServerKeyExchange.
+// keyAgreementAuthentication is a helper interface that specifies how
+// to authenticate the ServerKeyExchange parameters.
+type keyAgreementAuthentication interface {
+	signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error)
+	verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error
+}
+
+// nilKeyAgreementAuthentication does not authenticate the key
+// agreement parameters.
+type nilKeyAgreementAuthentication struct{}
+
+func (ka *nilKeyAgreementAuthentication) signParameters(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg, params []byte) (*serverKeyExchangeMsg, error) {
+	skx := new(serverKeyExchangeMsg)
+	skx.key = params
+	return skx, nil
+}
+
+func (ka *nilKeyAgreementAuthentication) verifyParameters(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, params []byte, sig []byte) error {
+	return nil
+}
+
+// signedKeyAgreement signs the ServerKeyExchange parameters with the
+// server's private key.
 type signedKeyAgreement struct {
 	version uint16
 	sigType uint8
@@ -328,7 +349,7 @@
 // pre-master secret is then calculated using ECDH. The signature may
 // either be ECDSA or RSA.
 type ecdheKeyAgreement struct {
-	signedKeyAgreement
+	auth       keyAgreementAuthentication
 	privateKey []byte
 	curve      elliptic.Curve
 	x, y       *big.Int
@@ -394,7 +415,7 @@
 	serverECDHParams[3] = byte(len(ecdhePublic))
 	copy(serverECDHParams[4:], ecdhePublic)
 
-	return ka.signParameters(config, cert, clientHello, hello, serverECDHParams)
+	return ka.auth.signParameters(config, cert, clientHello, hello, serverECDHParams)
 }
 
 func (ka *ecdheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
@@ -438,7 +459,7 @@
 	serverECDHParams := skx.key[:4+publicLen]
 	sig := skx.key[4+publicLen:]
 
-	return ka.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig)
+	return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverECDHParams, sig)
 }
 
 func (ka *ecdheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
@@ -468,7 +489,7 @@
 // an ephemeral Diffie-Hellman public/private key pair and signs it. The
 // pre-master secret is then calculated using Diffie-Hellman.
 type dheKeyAgreement struct {
-	signedKeyAgreement
+	auth    keyAgreementAuthentication
 	p, g    *big.Int
 	yTheirs *big.Int
 	xOurs   *big.Int
@@ -500,7 +521,7 @@
 	serverDHParams = append(serverDHParams, byte(len(yBytes)>>8), byte(len(yBytes)))
 	serverDHParams = append(serverDHParams, yBytes...)
 
-	return ka.signParameters(config, cert, clientHello, hello, serverDHParams)
+	return ka.auth.signParameters(config, cert, clientHello, hello, serverDHParams)
 }
 
 func (ka *dheKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
@@ -562,7 +583,7 @@
 	sig := k
 	serverDHParams := skx.key[:len(skx.key)-len(sig)]
 
-	return ka.verifyParameters(config, clientHello, serverHello, cert, serverDHParams, sig)
+	return ka.auth.verifyParameters(config, clientHello, serverHello, cert, serverDHParams, sig)
 }
 
 func (ka *dheKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
@@ -586,3 +607,164 @@
 
 	return preMasterSecret, ckx, nil
 }
+
+// nilKeyAgreement is a fake key agreement used to implement the plain PSK key
+// exchange.
+type nilKeyAgreement struct{}
+
+func (ka *nilKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+	return nil, nil
+}
+
+func (ka *nilKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+	if len(ckx.ciphertext) != 0 {
+		return nil, errClientKeyExchange
+	}
+
+	// Although in plain PSK, otherSecret is all zeros, the base key
+	// agreement does not access to the length of the pre-shared
+	// key. pskKeyAgreement instead interprets nil to mean to use all zeros
+	// of the appropriate length.
+	return nil, nil
+}
+
+func (ka *nilKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+	if len(skx.key) != 0 {
+		return errServerKeyExchange
+	}
+	return nil
+}
+
+func (ka *nilKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	// Although in plain PSK, otherSecret is all zeros, the base key
+	// agreement does not access to the length of the pre-shared
+	// key. pskKeyAgreement instead interprets nil to mean to use all zeros
+	// of the appropriate length.
+	return nil, &clientKeyExchangeMsg{}, nil
+}
+
+// makePSKPremaster formats a PSK pre-master secret based on otherSecret from
+// the base key exchange and psk.
+func makePSKPremaster(otherSecret, psk []byte) []byte {
+	out := make([]byte, 0, 2+len(otherSecret)+2+len(psk))
+	out = append(out, byte(len(otherSecret)>>8), byte(len(otherSecret)))
+	out = append(out, otherSecret...)
+	out = append(out, byte(len(psk)>>8), byte(len(psk)))
+	out = append(out, psk...)
+	return out
+}
+
+// pskKeyAgreement implements the PSK key agreement.
+type pskKeyAgreement struct {
+	base         keyAgreement
+	identityHint string
+}
+
+func (ka *pskKeyAgreement) generateServerKeyExchange(config *Config, cert *Certificate, clientHello *clientHelloMsg, hello *serverHelloMsg) (*serverKeyExchangeMsg, error) {
+	// Assemble the identity hint.
+	bytes := make([]byte, 2+len(config.PreSharedKeyIdentity))
+	bytes[0] = byte(len(config.PreSharedKeyIdentity) >> 8)
+	bytes[1] = byte(len(config.PreSharedKeyIdentity))
+	copy(bytes[2:], []byte(config.PreSharedKeyIdentity))
+
+	// If there is one, append the base key agreement's
+	// ServerKeyExchange.
+	baseSkx, err := ka.base.generateServerKeyExchange(config, cert, clientHello, hello)
+	if err != nil {
+		return nil, err
+	}
+
+	if baseSkx != nil {
+		bytes = append(bytes, baseSkx.key...)
+	} else if config.PreSharedKeyIdentity == "" {
+		// ServerKeyExchange is optional if the identity hint is empty
+		// and there would otherwise be no ServerKeyExchange.
+		return nil, nil
+	}
+
+	skx := new(serverKeyExchangeMsg)
+	skx.key = bytes
+	return skx, nil
+}
+
+func (ka *pskKeyAgreement) processClientKeyExchange(config *Config, cert *Certificate, ckx *clientKeyExchangeMsg, version uint16) ([]byte, error) {
+	// First, process the PSK identity.
+	if len(ckx.ciphertext) < 2 {
+		return nil, errClientKeyExchange
+	}
+	identityLen := (int(ckx.ciphertext[0]) << 8) | int(ckx.ciphertext[1])
+	if 2+identityLen > len(ckx.ciphertext) {
+		return nil, errClientKeyExchange
+	}
+	identity := string(ckx.ciphertext[2 : 2+identityLen])
+
+	if identity != config.PreSharedKeyIdentity {
+		return nil, errors.New("tls: unexpected identity")
+	}
+
+	if config.PreSharedKey == nil {
+		return nil, errors.New("tls: pre-shared key not configured")
+	}
+
+	// Process the remainder of the ClientKeyExchange to compute the base
+	// pre-master secret.
+	newCkx := new(clientKeyExchangeMsg)
+	newCkx.ciphertext = ckx.ciphertext[2+identityLen:]
+	otherSecret, err := ka.base.processClientKeyExchange(config, cert, newCkx, version)
+	if err != nil {
+		return nil, err
+	}
+
+	if otherSecret == nil {
+		// Special-case for the plain PSK key exchanges.
+		otherSecret = make([]byte, len(config.PreSharedKey))
+	}
+	return makePSKPremaster(otherSecret, config.PreSharedKey), nil
+}
+
+func (ka *pskKeyAgreement) processServerKeyExchange(config *Config, clientHello *clientHelloMsg, serverHello *serverHelloMsg, cert *x509.Certificate, skx *serverKeyExchangeMsg) error {
+	if len(skx.key) < 2 {
+		return errServerKeyExchange
+	}
+	identityLen := (int(skx.key[0]) << 8) | int(skx.key[1])
+	if 2+identityLen > len(skx.key) {
+		return errServerKeyExchange
+	}
+	ka.identityHint = string(skx.key[2 : 2+identityLen])
+
+	// Process the remainder of the ServerKeyExchange.
+	newSkx := new(serverKeyExchangeMsg)
+	newSkx.key = skx.key[2+identityLen:]
+	return ka.base.processServerKeyExchange(config, clientHello, serverHello, cert, newSkx)
+}
+
+func (ka *pskKeyAgreement) generateClientKeyExchange(config *Config, clientHello *clientHelloMsg, cert *x509.Certificate) ([]byte, *clientKeyExchangeMsg, error) {
+	// The server only sends an identity hint but, for purposes of
+	// test code, the server always sends the hint and it is
+	// required to match.
+	if ka.identityHint != config.PreSharedKeyIdentity {
+		return nil, nil, errors.New("tls: unexpected identity")
+	}
+
+	// Serialize the identity.
+	bytes := make([]byte, 2+len(config.PreSharedKeyIdentity))
+	bytes[0] = byte(len(config.PreSharedKeyIdentity) >> 8)
+	bytes[1] = byte(len(config.PreSharedKeyIdentity))
+	copy(bytes[2:], []byte(config.PreSharedKeyIdentity))
+
+	// Append the base key exchange's ClientKeyExchange.
+	otherSecret, baseCkx, err := ka.base.generateClientKeyExchange(config, clientHello, cert)
+	if err != nil {
+		return nil, nil, err
+	}
+	ckx := new(clientKeyExchangeMsg)
+	ckx.ciphertext = append(bytes, baseCkx.ciphertext...)
+
+	if config.PreSharedKey == nil {
+		return nil, nil, errors.New("tls: pre-shared key not configured")
+	}
+	if otherSecret == nil {
+		otherSecret = make([]byte, len(config.PreSharedKey))
+	}
+	return makePSKPremaster(otherSecret, config.PreSharedKey), ckx, nil
+}
diff --git a/ssl/test/runner/prf.go b/ssl/test/runner/prf.go
index 6d0db97..d45c080 100644
--- a/ssl/test/runner/prf.go
+++ b/ssl/test/runner/prf.go
@@ -117,6 +117,7 @@
 )
 
 var masterSecretLabel = []byte("master secret")
+var extendedMasterSecretLabel = []byte("extended master secret")
 var keyExpansionLabel = []byte("key expansion")
 var clientFinishedLabel = []byte("client finished")
 var serverFinishedLabel = []byte("server finished")
@@ -150,6 +151,15 @@
 	return masterSecret
 }
 
+// extendedMasterFromPreMasterSecret generates the master secret from the
+// pre-master secret when the Triple Handshake fix is in effect. See
+// https://tools.ietf.org/html/draft-ietf-tls-session-hash-01
+func extendedMasterFromPreMasterSecret(version uint16, suite *cipherSuite, preMasterSecret []byte, h finishedHash) []byte {
+	masterSecret := make([]byte, masterSecretLength)
+	prfForVersion(version, suite)(masterSecret, preMasterSecret, extendedMasterSecretLabel, h.Sum())
+	return masterSecret
+}
+
 // keysFromMasterSecret generates the connection keys from the master
 // secret, given the lengths of the MAC key, cipher key and IV, as defined in
 // RFC 2246, section 6.3.
@@ -221,6 +231,16 @@
 	return len(msg), nil
 }
 
+func (h finishedHash) Sum() []byte {
+	if h.version >= VersionTLS12 {
+		return h.client.Sum(nil)
+	}
+
+	out := make([]byte, 0, md5.Size+sha1.Size)
+	out = h.clientMD5.Sum(out)
+	return h.client.Sum(out)
+}
+
 // finishedSum30 calculates the contents of the verify_data member of a SSLv3
 // Finished message given the MD5 and SHA1 hashes of a set of handshake
 // messages.
@@ -264,15 +284,7 @@
 	}
 
 	out := make([]byte, finishedVerifyLength)
-	if h.version >= VersionTLS12 {
-		seed := h.client.Sum(nil)
-		h.prf(out, masterSecret, clientFinishedLabel, seed)
-	} else {
-		seed := make([]byte, 0, md5.Size+sha1.Size)
-		seed = h.clientMD5.Sum(seed)
-		seed = h.client.Sum(seed)
-		h.prf(out, masterSecret, clientFinishedLabel, seed)
-	}
+	h.prf(out, masterSecret, clientFinishedLabel, h.Sum())
 	return out
 }
 
@@ -284,15 +296,7 @@
 	}
 
 	out := make([]byte, finishedVerifyLength)
-	if h.version >= VersionTLS12 {
-		seed := h.server.Sum(nil)
-		h.prf(out, masterSecret, serverFinishedLabel, seed)
-	} else {
-		seed := make([]byte, 0, md5.Size+sha1.Size)
-		seed = h.serverMD5.Sum(seed)
-		seed = h.server.Sum(seed)
-		h.prf(out, masterSecret, serverFinishedLabel, seed)
-	}
+	h.prf(out, masterSecret, serverFinishedLabel, h.Sum())
 	return out
 }
 
@@ -334,14 +338,10 @@
 		return digest[:], crypto.SHA256, nil
 	}
 	if signatureAndHash.signature == signatureECDSA {
-		digest := h.server.Sum(nil)
-		return digest, crypto.SHA1, nil
+		return h.server.Sum(nil), crypto.SHA1, nil
 	}
 
-	digest := make([]byte, 0, 36)
-	digest = h.serverMD5.Sum(digest)
-	digest = h.server.Sum(digest)
-	return digest, crypto.MD5SHA1, nil
+	return h.Sum(), crypto.MD5SHA1, nil
 }
 
 // hashForChannelID returns the hash to be signed for TLS Channel
diff --git a/ssl/test/runner/recordingconn.go b/ssl/test/runner/recordingconn.go
new file mode 100644
index 0000000..a67fa48
--- /dev/null
+++ b/ssl/test/runner/recordingconn.go
@@ -0,0 +1,130 @@
+package main
+
+import (
+	"bufio"
+	"encoding/hex"
+	"errors"
+	"fmt"
+	"io"
+	"net"
+	"strconv"
+	"strings"
+	"sync"
+)
+
+// recordingConn is a net.Conn that records the traffic that passes through it.
+// WriteTo can be used to produce output that can be later be loaded with
+// ParseTestData.
+type recordingConn struct {
+	net.Conn
+	sync.Mutex
+	flows   [][]byte
+	reading bool
+}
+
+func (r *recordingConn) Read(b []byte) (n int, err error) {
+	if n, err = r.Conn.Read(b); n == 0 {
+		return
+	}
+	b = b[:n]
+
+	r.Lock()
+	defer r.Unlock()
+
+	if l := len(r.flows); l == 0 || !r.reading {
+		buf := make([]byte, len(b))
+		copy(buf, b)
+		r.flows = append(r.flows, buf)
+	} else {
+		r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+	}
+	r.reading = true
+	return
+}
+
+func (r *recordingConn) Write(b []byte) (n int, err error) {
+	if n, err = r.Conn.Write(b); n == 0 {
+		return
+	}
+	b = b[:n]
+
+	r.Lock()
+	defer r.Unlock()
+
+	if l := len(r.flows); l == 0 || r.reading {
+		buf := make([]byte, len(b))
+		copy(buf, b)
+		r.flows = append(r.flows, buf)
+	} else {
+		r.flows[l-1] = append(r.flows[l-1], b[:n]...)
+	}
+	r.reading = false
+	return
+}
+
+// WriteTo writes hex dumps to w that contains the recorded traffic.
+func (r *recordingConn) WriteTo(w io.Writer) {
+	// TLS always starts with a client to server flow.
+	clientToServer := true
+
+	for i, flow := range r.flows {
+		source, dest := "client", "server"
+		if !clientToServer {
+			source, dest = dest, source
+		}
+		fmt.Fprintf(w, ">>> Flow %d (%s to %s)\n", i+1, source, dest)
+		dumper := hex.Dumper(w)
+		dumper.Write(flow)
+		dumper.Close()
+		clientToServer = !clientToServer
+	}
+}
+
+func parseTestData(r io.Reader) (flows [][]byte, err error) {
+	var currentFlow []byte
+
+	scanner := bufio.NewScanner(r)
+	for scanner.Scan() {
+		line := scanner.Text()
+		// If the line starts with ">>> " then it marks the beginning
+		// of a new flow.
+		if strings.HasPrefix(line, ">>> ") {
+			if len(currentFlow) > 0 || len(flows) > 0 {
+				flows = append(flows, currentFlow)
+				currentFlow = nil
+			}
+			continue
+		}
+
+		// Otherwise the line is a line of hex dump that looks like:
+		// 00000170  fc f5 06 bf (...)  |.....X{&?......!|
+		// (Some bytes have been omitted from the middle section.)
+
+		if i := strings.IndexByte(line, ' '); i >= 0 {
+			line = line[i:]
+		} else {
+			return nil, errors.New("invalid test data")
+		}
+
+		if i := strings.IndexByte(line, '|'); i >= 0 {
+			line = line[:i]
+		} else {
+			return nil, errors.New("invalid test data")
+		}
+
+		hexBytes := strings.Fields(line)
+		for _, hexByte := range hexBytes {
+			val, err := strconv.ParseUint(hexByte, 16, 8)
+			if err != nil {
+				return nil, errors.New("invalid hex byte in test data: " + err.Error())
+			}
+			currentFlow = append(currentFlow, byte(val))
+		}
+	}
+
+	if len(currentFlow) > 0 {
+		flows = append(flows, currentFlow)
+	}
+
+	return flows, nil
+}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index b4c2e61..1b461e2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -22,6 +22,8 @@
 )
 
 var useValgrind = flag.Bool("valgrind", false, "If true, run code under valgrind")
+var useGDB = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+var flagDebug *bool = flag.Bool("debug", false, "Hexdump the contents of the connection")
 
 const (
 	rsaCertificateFile   = "cert.pem"
@@ -693,10 +695,11 @@
 	var shim *exec.Cmd
 	if *useValgrind {
 		shim = valgrindOf(false, shim_path, flags...)
+	} else if *useGDB {
+		shim = gdbOf(shim_path, flags...)
 	} else {
 		shim = exec.Command(shim_path, flags...)
 	}
-	// shim = gdbOf(shim_path, flags...)
 	shim.ExtraFiles = []*os.File{shimEnd, shimEndResume}
 	shim.Stdin = os.Stdin
 	var stdoutBuf, stderrBuf bytes.Buffer
@@ -717,8 +720,19 @@
 		}
 	}
 
+	var connDebug *recordingConn
+	if *flagDebug {
+		connDebug = &recordingConn{Conn: conn}
+		conn = connDebug
+	}
+
 	err := doExchange(test, &config, conn, test.messageLen,
 		false /* not a resumption */)
+
+	if *flagDebug {
+		connDebug.WriteTo(os.Stdout)
+	}
+
 	conn.Close()
 	if err == nil && test.resumeSession {
 		var resumeConfig Config
@@ -814,6 +828,7 @@
 	{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
 	{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
+	{"ECDHE-PSK-WITH-AES-128-GCM-SHA256", TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
 	{"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
@@ -821,6 +836,9 @@
 	{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
 	{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
+	{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
+	{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
+	{"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA},
 	{"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
 	{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},
 }
@@ -833,6 +851,9 @@
 
 func addCipherSuiteTests() {
 	for _, suite := range testCipherSuites {
+		const psk = "12345"
+		const pskIdentity = "luggage combo"
+
 		var cert Certificate
 		var certFile string
 		var keyFile string
@@ -846,6 +867,13 @@
 			keyFile = rsaKeyFile
 		}
 
+		var flags []string
+		if strings.HasPrefix(suite.name, "PSK-") || strings.Contains(suite.name, "-PSK-") {
+			flags = append(flags,
+				"-psk", psk,
+				"-psk-identity", pskIdentity)
+		}
+
 		for _, ver := range tlsVersions {
 			if ver.version < VersionTLS12 && isTLS12Only(suite.name) {
 				continue
@@ -860,11 +888,14 @@
 				testType: clientTest,
 				name:     ver.name + "-" + suite.name + "-client",
 				config: Config{
-					MinVersion:   ver.version,
-					MaxVersion:   ver.version,
-					CipherSuites: []uint16{suite.id},
-					Certificates: []Certificate{cert},
+					MinVersion:           ver.version,
+					MaxVersion:           ver.version,
+					CipherSuites:         []uint16{suite.id},
+					Certificates:         []Certificate{cert},
+					PreSharedKey:         []byte(psk),
+					PreSharedKeyIdentity: pskIdentity,
 				},
+				flags:         flags,
 				resumeSession: resumeSession,
 			})
 
@@ -872,13 +903,16 @@
 				testType: serverTest,
 				name:     ver.name + "-" + suite.name + "-server",
 				config: Config{
-					MinVersion:   ver.version,
-					MaxVersion:   ver.version,
-					CipherSuites: []uint16{suite.id},
-					Certificates: []Certificate{cert},
+					MinVersion:           ver.version,
+					MaxVersion:           ver.version,
+					CipherSuites:         []uint16{suite.id},
+					Certificates:         []Certificate{cert},
+					PreSharedKey:         []byte(psk),
+					PreSharedKeyIdentity: pskIdentity,
 				},
 				certFile:      certFile,
 				keyFile:       keyFile,
+				flags:         flags,
 				resumeSession: resumeSession,
 			})
 
@@ -889,11 +923,14 @@
 					protocol: dtls,
 					name:     "D" + ver.name + "-" + suite.name + "-client",
 					config: Config{
-						MinVersion:   ver.version,
-						MaxVersion:   ver.version,
-						CipherSuites: []uint16{suite.id},
-						Certificates: []Certificate{cert},
+						MinVersion:           ver.version,
+						MaxVersion:           ver.version,
+						CipherSuites:         []uint16{suite.id},
+						Certificates:         []Certificate{cert},
+						PreSharedKey:         []byte(psk),
+						PreSharedKeyIdentity: pskIdentity,
 					},
+					flags:         flags,
 					resumeSession: resumeSession,
 				})
 				testCases = append(testCases, testCase{
@@ -901,13 +938,16 @@
 					protocol: dtls,
 					name:     "D" + ver.name + "-" + suite.name + "-server",
 					config: Config{
-						MinVersion:   ver.version,
-						MaxVersion:   ver.version,
-						CipherSuites: []uint16{suite.id},
-						Certificates: []Certificate{cert},
+						MinVersion:           ver.version,
+						MaxVersion:           ver.version,
+						CipherSuites:         []uint16{suite.id},
+						Certificates:         []Certificate{cert},
+						PreSharedKey:         []byte(psk),
+						PreSharedKeyIdentity: pskIdentity,
 					},
 					certFile:      certFile,
 					keyFile:       keyFile,
+					flags:         flags,
 					resumeSession: resumeSession,
 				})
 			}
@@ -1070,6 +1110,62 @@
 	}
 }
 
+func addExtendedMasterSecretTests() {
+	const expectEMSFlag = "-expect-extended-master-secret"
+
+	for _, with := range []bool{false, true} {
+		prefix := "No"
+		var flags []string
+		if with {
+			prefix = ""
+			flags = []string{expectEMSFlag}
+		}
+
+		for _, isClient := range []bool{false, true} {
+			suffix := "-Server"
+			testType := serverTest
+			if isClient {
+				suffix = "-Client"
+				testType = clientTest
+			}
+
+			for _, ver := range tlsVersions {
+				test := testCase{
+					testType: testType,
+					name:     prefix + "ExtendedMasterSecret-" + ver.name + suffix,
+					config: Config{
+						MinVersion: ver.version,
+						MaxVersion: ver.version,
+						Bugs: ProtocolBugs{
+							NoExtendedMasterSecret:      !with,
+							RequireExtendedMasterSecret: with,
+						},
+					},
+					flags:      flags,
+					shouldFail: ver.version == VersionSSL30 && with,
+				}
+				if test.shouldFail {
+					test.expectedLocalError = "extended master secret required but not supported by peer"
+				}
+				testCases = append(testCases, test)
+			}
+		}
+	}
+
+	// When a session is resumed, it should still be aware that its master
+	// secret was generated via EMS and thus it's safe to use tls-unique.
+	testCases = append(testCases, testCase{
+		name: "ExtendedMasterSecret-Resume",
+		config: Config{
+			Bugs: ProtocolBugs{
+				RequireExtendedMasterSecret: true,
+			},
+		},
+		flags:         []string{expectEMSFlag},
+		resumeSession: true,
+	})
+}
+
 // Adds tests that try to cover the range of the handshake state machine, under
 // various conditions. Some of these are redundant with other tests, but they
 // only cover the synchronous case.
@@ -1178,6 +1274,34 @@
 		flags: flags,
 	})
 
+	// Skip ServerKeyExchange in PSK key exchange if there's no
+	// identity hint.
+	testCases = append(testCases, testCase{
+		protocol: protocol,
+		name:     "EmptyPSKHint-Client" + suffix,
+		config: Config{
+			CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
+			PreSharedKey: []byte("secret"),
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength: maxHandshakeRecordLength,
+			},
+		},
+		flags: append(flags, "-psk", "secret"),
+	})
+	testCases = append(testCases, testCase{
+		protocol: protocol,
+		testType: serverTest,
+		name:     "EmptyPSKHint-Server" + suffix,
+		config: Config{
+			CipherSuites: []uint16{TLS_PSK_WITH_AES_128_CBC_SHA},
+			PreSharedKey: []byte("secret"),
+			Bugs: ProtocolBugs{
+				MaxHandshakeRecordLength: maxHandshakeRecordLength,
+			},
+		},
+		flags: append(flags, "-psk", "secret"),
+	})
+
 	if protocol == tls {
 		// NPN on client and server; results in post-handshake message.
 		testCases = append(testCases, testCase{
@@ -1568,7 +1692,7 @@
 			},
 		},
 		resumeSession: true,
-		shouldFail: true,
+		shouldFail:    true,
 		expectedError: ":DECODE_ERROR:",
 	})
 }
@@ -1690,6 +1814,7 @@
 	addD5BugTests()
 	addExtensionTests()
 	addResumptionVersionTests()
+	addExtendedMasterSecretTests()
 	for _, async := range []bool{false, true} {
 		for _, splitHandshake := range []bool{false, true} {
 			for _, protocol := range []protocol{tls, dtls} {
diff --git a/ssl/test/runner/ticket.go b/ssl/test/runner/ticket.go
index 74791d6..8355822 100644
--- a/ssl/test/runner/ticket.go
+++ b/ssl/test/runner/ticket.go
@@ -18,11 +18,12 @@
 // sessionState contains the information that is serialized into a session
 // ticket in order to later resume a connection.
 type sessionState struct {
-	vers          uint16
-	cipherSuite   uint16
-	masterSecret  []byte
-	handshakeHash []byte
-	certificates  [][]byte
+	vers                 uint16
+	cipherSuite          uint16
+	masterSecret         []byte
+	handshakeHash        []byte
+	certificates         [][]byte
+	extendedMasterSecret bool
 }
 
 func (s *sessionState) equal(i interface{}) bool {
@@ -34,7 +35,8 @@
 	if s.vers != s1.vers ||
 		s.cipherSuite != s1.cipherSuite ||
 		!bytes.Equal(s.masterSecret, s1.masterSecret) ||
-		!bytes.Equal(s.handshakeHash, s1.handshakeHash) {
+		!bytes.Equal(s.handshakeHash, s1.handshakeHash) ||
+		s.extendedMasterSecret != s1.extendedMasterSecret {
 		return false
 	}
 
@@ -56,6 +58,7 @@
 	for _, cert := range s.certificates {
 		length += 4 + len(cert)
 	}
+	length++
 
 	ret := make([]byte, length)
 	x := ret
@@ -88,6 +91,11 @@
 		x = x[4+len(cert):]
 	}
 
+	if s.extendedMasterSecret {
+		x[0] = 1
+	}
+	x = x[1:]
+
 	return ret
 }
 
@@ -144,6 +152,16 @@
 		data = data[certLen:]
 	}
 
+	if len(data) < 1 {
+		return false
+	}
+
+	s.extendedMasterSecret = false
+	if data[0] == 1 {
+		s.extendedMasterSecret = true
+	}
+	data = data[1:]
+
 	if len(data) > 0 {
 		return false
 	}
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 270fbfb..c50d9de 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -57,6 +57,8 @@
   { "-shim-writes-first", &TestConfig::shim_writes_first },
   { "-tls-d5-bug", &TestConfig::tls_d5_bug },
   { "-expect-session-miss", &TestConfig::expect_session_miss },
+  { "-expect-extended-master-secret",
+    &TestConfig::expect_extended_master_secret },
 };
 
 const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]);
@@ -74,6 +76,8 @@
   { "-expect-alpn", &TestConfig::expected_alpn },
   { "-expect-advertised-alpn", &TestConfig::expected_advertised_alpn },
   { "-select-alpn", &TestConfig::select_alpn },
+  { "-psk", &TestConfig::psk },
+  { "-psk-identity", &TestConfig::psk_identity },
 };
 
 const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);
@@ -105,7 +109,8 @@
       cookie_exchange(false),
       shim_writes_first(false),
       tls_d5_bug(false),
-      expect_session_miss(false) {
+      expect_session_miss(false),
+      expect_extended_master_secret(false) {
 }
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config) {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index acce504..e5ff8ad 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -53,6 +53,9 @@
   std::string expected_advertised_alpn;
   std::string select_alpn;
   bool expect_session_miss;
+  bool expect_extended_master_secret;
+  std::string psk;
+  std::string psk_identity;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/tool/client.cc b/tool/client.cc
index d7d9e22..6cc93d6 100644
--- a/tool/client.cc
+++ b/tool/client.cc
@@ -47,6 +47,10 @@
      "The hostname and port of the server to connect to, e.g. foo.com:443",
     },
     {
+     "-cipher", false,
+     "An OpenSSL-style cipher suite string that configures the offered ciphers",
+    },
+    {
      "", false, "",
     },
 };
@@ -265,6 +269,10 @@
     SSL_CTX_set_keylog_bio(ctx, keylog_bio);
   }
 
+  if (args_map.count("-cipher") != 0) {
+    SSL_CTX_set_cipher_list(ctx, args_map["-cipher"].c_str());
+  }
+
   int sock = -1;
   if (!Connect(&sock, args_map["-connect"])) {
     return false;