external/boringssl: Sync to 171b54.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/8ca0b4127da11d766067ea6ec4122017ba0edb0e..171b5403ee767fa0f3aecd377867db6533c3eb8f

This also updates the build file to build as C99, so BoringSSL can use
variables in for loops.

Change-Id: I48ae985fd1bed244f7ed327aefc9a13e5b17b185
diff --git a/Android.mk b/Android.mk
index 3e3ef2a..0533986 100644
--- a/Android.mk
+++ b/Android.mk
@@ -4,6 +4,21 @@
 
 LOCAL_PATH := $(call my-dir)
 
+boringssl_cflags := \
+    -fvisibility=hidden \
+    -DBORINGSSL_SHARED_LIBRARY \
+    -DBORINGSSL_IMPLEMENTATION \
+    -DOPENSSL_SMALL \
+    -D_XOPEN_SOURCE=700 \
+    -Wno-unused-parameter
+
+boringssl_cppflags := \
+    -Wall \
+    -Werror
+
+boringssl_conlyflags := \
+    -std=c99
+
 ## libcrypto
 
 # Target static library
@@ -13,7 +28,9 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
 LOCAL_SDK_VERSION := 9
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 # sha256-armv4.S does not compile with clang.
 LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
 LOCAL_CLANG_ASFLAGS_arm64 += -march=armv8-a+crypto
@@ -26,8 +43,10 @@
 LOCAL_MODULE := libcrypto
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
 LOCAL_SDK_VERSION := 9
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 # sha256-armv4.S does not compile with clang.
 LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
 LOCAL_CLANG_ASFLAGS_arm64 += -march=armv8-a+crypto
@@ -36,12 +55,13 @@
 
 # Target static tool
 include $(CLEAR_VARS)
-LOCAL_CFLAGS += -Wall -Werror -std=c++0x
 LOCAL_CPP_EXTENSION := cc
 LOCAL_MODULE := bssl
 LOCAL_MODULE_TAGS := optional
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_SHARED_LIBRARIES=libcrypto libssl
 include $(LOCAL_PATH)/sources.mk
 LOCAL_SRC_FILES = $(tool_sources)
@@ -54,7 +74,9 @@
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_CXX_STL := none
 # Windows and Macs both have problems with assembly files
 LOCAL_CFLAGS_darwin += -DOPENSSL_NO_ASM
@@ -75,7 +97,9 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_MULTILIB := both
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 # Windows and Macs both have problems with assembly files
 LOCAL_CFLAGS_darwin += -DOPENSSL_NO_ASM
 LOCAL_CFLAGS_windows += -DOPENSSL_NO_ASM
@@ -95,7 +119,9 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/ssl-sources.mk
 LOCAL_SDK_VERSION := 9
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 include $(LOCAL_PATH)/ssl-sources.mk
 include $(BUILD_STATIC_LIBRARY)
 
@@ -105,7 +131,9 @@
 LOCAL_MODULE := libssl
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/ssl-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_SHARED_LIBRARIES=libcrypto
 LOCAL_SDK_VERSION := 9
 include $(LOCAL_PATH)/ssl-sources.mk
@@ -117,7 +145,9 @@
 LOCAL_MODULE := libssl_static-host
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/ssl-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_CXX_STL := none
 # TODO: b/26097626. ASAN breaks use of this library in JVM.
 # Re-enable sanitization when the issue with making clients of this library
@@ -131,12 +161,13 @@
 # Host static tool (for linux only).
 ifeq ($(HOST_OS), linux)
 include $(CLEAR_VARS)
-LOCAL_CFLAGS += -Wall -Werror -std=c++0x
 LOCAL_CPP_EXTENSION := cc
 LOCAL_MODULE := bssl
 LOCAL_MODULE_TAGS := optional
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_SHARED_LIBRARIES=libcrypto-host libssl-host
 # Needed for clock_gettime.
 LOCAL_LDFLAGS := -lrt
@@ -152,7 +183,9 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
 LOCAL_MULTILIB := both
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/ssl-sources.mk
-LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
+LOCAL_CFLAGS := $(boringssl_cflags)
+LOCAL_CPPFLAGS := $(boringssl_cppflags)
+LOCAL_CONLYFLAGS := $(boringssl_conlyflags)
 LOCAL_CXX_STL := none
 LOCAL_SHARED_LIBRARIES += libcrypto-host
 include $(LOCAL_PATH)/ssl-sources.mk
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index 9a5595e..7d2e238 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-8ca0b4127da11d766067ea6ec4122017ba0edb0e
+171b5403ee767fa0f3aecd377867db6533c3eb8f
diff --git a/linux-x86/crypto/chacha/chacha-x86.S b/linux-x86/crypto/chacha/chacha-x86.S
index f28459e..d3c39ac 100644
--- a/linux-x86/crypto/chacha/chacha-x86.S
+++ b/linux-x86/crypto/chacha/chacha-x86.S
@@ -261,13 +261,11 @@
 	xorl	36(%ebx),%esi
 	xorl	48(%ebx),%edx
 	xorl	56(%ebx),%edi
-	movl	%ebp,16(%esp)
-	movl	(%esp),%ebp
-	movl	%ecx,32(%esp)
-	movl	%esi,36(%esp)
-	movl	%edx,48(%esp)
-	movl	%edi,56(%esp)
-	movl	%ebp,(%eax)
+	movl	%ebp,16(%eax)
+	movl	%ecx,32(%eax)
+	movl	%esi,36(%eax)
+	movl	%edx,48(%eax)
+	movl	%edi,56(%eax)
 	movl	4(%esp),%ebp
 	movl	8(%esp),%ecx
 	movl	12(%esp),%esi
@@ -284,42 +282,34 @@
 	xorl	20(%ebx),%edx
 	xorl	24(%ebx),%edi
 	movl	%ebp,4(%eax)
-	movl	16(%esp),%ebp
 	movl	%ecx,8(%eax)
 	movl	%esi,12(%eax)
-	movl	%ebp,16(%eax)
 	movl	%edx,20(%eax)
 	movl	%edi,24(%eax)
-	movl	28(%esp),%ecx
-	movl	32(%esp),%edx
-	movl	36(%esp),%edi
-	addl	92(%esp),%ecx
-	movl	40(%esp),%ebp
-	xorl	28(%ebx),%ecx
+	movl	28(%esp),%ebp
+	movl	40(%esp),%ecx
 	movl	44(%esp),%esi
-	movl	%ecx,28(%eax)
-	movl	%edx,32(%eax)
-	movl	%edi,36(%eax)
-	addl	104(%esp),%ebp
-	addl	108(%esp),%esi
-	xorl	40(%ebx),%ebp
-	xorl	44(%ebx),%esi
-	movl	%ebp,40(%eax)
-	movl	%esi,44(%eax)
-	movl	48(%esp),%ecx
-	movl	56(%esp),%esi
 	movl	52(%esp),%edx
 	movl	60(%esp),%edi
+	addl	92(%esp),%ebp
+	addl	104(%esp),%ecx
+	addl	108(%esp),%esi
 	addl	116(%esp),%edx
 	addl	124(%esp),%edi
+	xorl	28(%ebx),%ebp
+	xorl	40(%ebx),%ecx
+	xorl	44(%ebx),%esi
 	xorl	52(%ebx),%edx
 	xorl	60(%ebx),%edi
 	leal	64(%ebx),%ebx
-	movl	%ecx,48(%eax)
+	movl	%ebp,28(%eax)
+	movl	(%esp),%ebp
+	movl	%ecx,40(%eax)
 	movl	160(%esp),%ecx
+	movl	%esi,44(%eax)
 	movl	%edx,52(%eax)
-	movl	%esi,56(%eax)
 	movl	%edi,60(%eax)
+	movl	%ebp,(%eax)
 	leal	64(%eax),%eax
 	subl	$64,%ecx
 	jnz	.L003outer_loop
@@ -715,14 +705,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,-128(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	-64(%ebx),%xmm0
-	movdqa	%xmm1,-112(%ebx)
-	movdqa	%xmm6,-96(%ebx)
-	movdqa	%xmm3,-80(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	-48(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	-32(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	-16(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	-64(%ebp),%xmm0
 	paddd	-48(%ebp),%xmm1
 	paddd	-32(%ebp),%xmm2
@@ -739,14 +739,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,-64(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	(%ebx),%xmm0
-	movdqa	%xmm1,-48(%ebx)
-	movdqa	%xmm6,-32(%ebx)
-	movdqa	%xmm3,-16(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	16(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	32(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	48(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	(%ebp),%xmm0
 	paddd	16(%ebp),%xmm1
 	paddd	32(%ebp),%xmm2
@@ -763,14 +773,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	64(%ebx),%xmm0
-	movdqa	%xmm1,16(%ebx)
-	movdqa	%xmm6,32(%ebx)
-	movdqa	%xmm3,48(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	80(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	96(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	112(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	64(%ebp),%xmm0
 	paddd	80(%ebp),%xmm1
 	paddd	96(%ebp),%xmm2
@@ -787,60 +807,20 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,64(%ebx)
-	movdqa	%xmm1,80(%ebx)
-	movdqa	%xmm6,96(%ebx)
-	movdqa	%xmm3,112(%ebx)
-	movdqu	-128(%esi),%xmm0
-	movdqu	-112(%esi),%xmm1
-	movdqu	-96(%esi),%xmm2
-	movdqu	-80(%esi),%xmm3
-	pxor	-128(%ebx),%xmm0
-	pxor	-64(%ebx),%xmm1
-	pxor	(%ebx),%xmm2
-	pxor	64(%ebx),%xmm3
-	movdqu	%xmm0,-128(%edi)
-	movdqu	%xmm1,-112(%edi)
-	movdqu	%xmm2,-96(%edi)
-	movdqu	%xmm3,-80(%edi)
-	movdqu	-64(%esi),%xmm0
-	movdqu	-48(%esi),%xmm1
-	movdqu	-32(%esi),%xmm2
-	movdqu	-16(%esi),%xmm3
-	pxor	-112(%ebx),%xmm0
-	pxor	-48(%ebx),%xmm1
-	pxor	16(%ebx),%xmm2
-	pxor	80(%ebx),%xmm3
-	movdqu	%xmm0,-64(%edi)
-	movdqu	%xmm1,-48(%edi)
-	movdqu	%xmm2,-32(%edi)
-	movdqu	%xmm3,-16(%edi)
-	movdqu	(%esi),%xmm0
-	movdqu	16(%esi),%xmm1
-	movdqu	32(%esi),%xmm2
-	movdqu	48(%esi),%xmm3
-	pxor	-96(%ebx),%xmm0
-	pxor	-32(%ebx),%xmm1
-	pxor	32(%ebx),%xmm2
-	pxor	96(%ebx),%xmm3
-	movdqu	%xmm0,(%edi)
-	movdqu	%xmm1,16(%edi)
-	movdqu	%xmm2,32(%edi)
-	movdqu	%xmm3,48(%edi)
-	movdqu	64(%esi),%xmm0
-	movdqu	80(%esi),%xmm1
-	movdqu	96(%esi),%xmm2
-	movdqu	112(%esi),%xmm3
-	pxor	-80(%ebx),%xmm0
-	pxor	-16(%ebx),%xmm1
-	pxor	48(%ebx),%xmm2
-	pxor	112(%ebx),%xmm3
-	movdqu	%xmm0,64(%edi)
-	movdqu	%xmm1,80(%edi)
-	movdqu	%xmm2,96(%edi)
-	movdqu	%xmm3,112(%edi)
-	leal	256(%esi),%esi
-	leal	256(%edi),%edi
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	208(%esi),%esi
+	pxor	%xmm0,%xmm4
+	pxor	%xmm1,%xmm5
+	pxor	%xmm2,%xmm6
+	pxor	%xmm3,%xmm7
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	208(%edi),%edi
 	subl	$256,%ecx
 	jnc	.L009outer_loop
 	addl	$256,%ecx
diff --git a/mac-x86/crypto/chacha/chacha-x86.S b/mac-x86/crypto/chacha/chacha-x86.S
index 9bd31b8..5de98a3 100644
--- a/mac-x86/crypto/chacha/chacha-x86.S
+++ b/mac-x86/crypto/chacha/chacha-x86.S
@@ -260,13 +260,11 @@
 	xorl	36(%ebx),%esi
 	xorl	48(%ebx),%edx
 	xorl	56(%ebx),%edi
-	movl	%ebp,16(%esp)
-	movl	(%esp),%ebp
-	movl	%ecx,32(%esp)
-	movl	%esi,36(%esp)
-	movl	%edx,48(%esp)
-	movl	%edi,56(%esp)
-	movl	%ebp,(%eax)
+	movl	%ebp,16(%eax)
+	movl	%ecx,32(%eax)
+	movl	%esi,36(%eax)
+	movl	%edx,48(%eax)
+	movl	%edi,56(%eax)
 	movl	4(%esp),%ebp
 	movl	8(%esp),%ecx
 	movl	12(%esp),%esi
@@ -283,42 +281,34 @@
 	xorl	20(%ebx),%edx
 	xorl	24(%ebx),%edi
 	movl	%ebp,4(%eax)
-	movl	16(%esp),%ebp
 	movl	%ecx,8(%eax)
 	movl	%esi,12(%eax)
-	movl	%ebp,16(%eax)
 	movl	%edx,20(%eax)
 	movl	%edi,24(%eax)
-	movl	28(%esp),%ecx
-	movl	32(%esp),%edx
-	movl	36(%esp),%edi
-	addl	92(%esp),%ecx
-	movl	40(%esp),%ebp
-	xorl	28(%ebx),%ecx
+	movl	28(%esp),%ebp
+	movl	40(%esp),%ecx
 	movl	44(%esp),%esi
-	movl	%ecx,28(%eax)
-	movl	%edx,32(%eax)
-	movl	%edi,36(%eax)
-	addl	104(%esp),%ebp
-	addl	108(%esp),%esi
-	xorl	40(%ebx),%ebp
-	xorl	44(%ebx),%esi
-	movl	%ebp,40(%eax)
-	movl	%esi,44(%eax)
-	movl	48(%esp),%ecx
-	movl	56(%esp),%esi
 	movl	52(%esp),%edx
 	movl	60(%esp),%edi
+	addl	92(%esp),%ebp
+	addl	104(%esp),%ecx
+	addl	108(%esp),%esi
 	addl	116(%esp),%edx
 	addl	124(%esp),%edi
+	xorl	28(%ebx),%ebp
+	xorl	40(%ebx),%ecx
+	xorl	44(%ebx),%esi
 	xorl	52(%ebx),%edx
 	xorl	60(%ebx),%edi
 	leal	64(%ebx),%ebx
-	movl	%ecx,48(%eax)
+	movl	%ebp,28(%eax)
+	movl	(%esp),%ebp
+	movl	%ecx,40(%eax)
 	movl	160(%esp),%ecx
+	movl	%esi,44(%eax)
 	movl	%edx,52(%eax)
-	movl	%esi,56(%eax)
 	movl	%edi,60(%eax)
+	movl	%ebp,(%eax)
 	leal	64(%eax),%eax
 	subl	$64,%ecx
 	jnz	L003outer_loop
@@ -712,14 +702,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,-128(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	-64(%ebx),%xmm0
-	movdqa	%xmm1,-112(%ebx)
-	movdqa	%xmm6,-96(%ebx)
-	movdqa	%xmm3,-80(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	-48(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	-32(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	-16(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	-64(%ebp),%xmm0
 	paddd	-48(%ebp),%xmm1
 	paddd	-32(%ebp),%xmm2
@@ -736,14 +736,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,-64(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	(%ebx),%xmm0
-	movdqa	%xmm1,-48(%ebx)
-	movdqa	%xmm6,-32(%ebx)
-	movdqa	%xmm3,-16(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	16(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	32(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	48(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	(%ebp),%xmm0
 	paddd	16(%ebp),%xmm1
 	paddd	32(%ebp),%xmm2
@@ -760,14 +770,24 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,(%ebx)
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	16(%esi),%esi
+	pxor	%xmm0,%xmm4
 	movdqa	64(%ebx),%xmm0
-	movdqa	%xmm1,16(%ebx)
-	movdqa	%xmm6,32(%ebx)
-	movdqa	%xmm3,48(%ebx)
+	pxor	%xmm1,%xmm5
 	movdqa	80(%ebx),%xmm1
+	pxor	%xmm2,%xmm6
 	movdqa	96(%ebx),%xmm2
+	pxor	%xmm3,%xmm7
 	movdqa	112(%ebx),%xmm3
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	16(%edi),%edi
 	paddd	64(%ebp),%xmm0
 	paddd	80(%ebp),%xmm1
 	paddd	96(%ebp),%xmm2
@@ -784,60 +804,20 @@
 	punpcklqdq	%xmm7,%xmm6
 	punpckhqdq	%xmm2,%xmm1
 	punpckhqdq	%xmm7,%xmm3
-	movdqa	%xmm0,64(%ebx)
-	movdqa	%xmm1,80(%ebx)
-	movdqa	%xmm6,96(%ebx)
-	movdqa	%xmm3,112(%ebx)
-	movdqu	-128(%esi),%xmm0
-	movdqu	-112(%esi),%xmm1
-	movdqu	-96(%esi),%xmm2
-	movdqu	-80(%esi),%xmm3
-	pxor	-128(%ebx),%xmm0
-	pxor	-64(%ebx),%xmm1
-	pxor	(%ebx),%xmm2
-	pxor	64(%ebx),%xmm3
-	movdqu	%xmm0,-128(%edi)
-	movdqu	%xmm1,-112(%edi)
-	movdqu	%xmm2,-96(%edi)
-	movdqu	%xmm3,-80(%edi)
-	movdqu	-64(%esi),%xmm0
-	movdqu	-48(%esi),%xmm1
-	movdqu	-32(%esi),%xmm2
-	movdqu	-16(%esi),%xmm3
-	pxor	-112(%ebx),%xmm0
-	pxor	-48(%ebx),%xmm1
-	pxor	16(%ebx),%xmm2
-	pxor	80(%ebx),%xmm3
-	movdqu	%xmm0,-64(%edi)
-	movdqu	%xmm1,-48(%edi)
-	movdqu	%xmm2,-32(%edi)
-	movdqu	%xmm3,-16(%edi)
-	movdqu	(%esi),%xmm0
-	movdqu	16(%esi),%xmm1
-	movdqu	32(%esi),%xmm2
-	movdqu	48(%esi),%xmm3
-	pxor	-96(%ebx),%xmm0
-	pxor	-32(%ebx),%xmm1
-	pxor	32(%ebx),%xmm2
-	pxor	96(%ebx),%xmm3
-	movdqu	%xmm0,(%edi)
-	movdqu	%xmm1,16(%edi)
-	movdqu	%xmm2,32(%edi)
-	movdqu	%xmm3,48(%edi)
-	movdqu	64(%esi),%xmm0
-	movdqu	80(%esi),%xmm1
-	movdqu	96(%esi),%xmm2
-	movdqu	112(%esi),%xmm3
-	pxor	-80(%ebx),%xmm0
-	pxor	-16(%ebx),%xmm1
-	pxor	48(%ebx),%xmm2
-	pxor	112(%ebx),%xmm3
-	movdqu	%xmm0,64(%edi)
-	movdqu	%xmm1,80(%edi)
-	movdqu	%xmm2,96(%edi)
-	movdqu	%xmm3,112(%edi)
-	leal	256(%esi),%esi
-	leal	256(%edi),%edi
+	movdqu	-128(%esi),%xmm4
+	movdqu	-64(%esi),%xmm5
+	movdqu	(%esi),%xmm2
+	movdqu	64(%esi),%xmm7
+	leal	208(%esi),%esi
+	pxor	%xmm0,%xmm4
+	pxor	%xmm1,%xmm5
+	pxor	%xmm2,%xmm6
+	pxor	%xmm3,%xmm7
+	movdqu	%xmm4,-128(%edi)
+	movdqu	%xmm5,-64(%edi)
+	movdqu	%xmm6,(%edi)
+	movdqu	%xmm7,64(%edi)
+	leal	208(%edi),%edi
 	subl	$256,%ecx
 	jnc	L009outer_loop
 	addl	$256,%ecx
diff --git a/sources.mk b/sources.mk
index 7ed59a1..14bf8f1 100644
--- a/sources.mk
+++ b/sources.mk
@@ -293,21 +293,19 @@
 ssl_sources := \
   src/ssl/custom_extensions.c\
   src/ssl/d1_both.c\
-  src/ssl/d1_clnt.c\
   src/ssl/d1_lib.c\
   src/ssl/d1_meth.c\
   src/ssl/d1_pkt.c\
   src/ssl/d1_srtp.c\
-  src/ssl/d1_srvr.c\
   src/ssl/dtls_record.c\
+  src/ssl/handshake_client.c\
+  src/ssl/handshake_server.c\
   src/ssl/pqueue/pqueue.c\
   src/ssl/s3_both.c\
-  src/ssl/s3_clnt.c\
   src/ssl/s3_enc.c\
   src/ssl/s3_lib.c\
   src/ssl/s3_meth.c\
   src/ssl/s3_pkt.c\
-  src/ssl/s3_srvr.c\
   src/ssl/ssl_aead_ctx.c\
   src/ssl/ssl_asn1.c\
   src/ssl/ssl_buffer.c\
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 3c5db63..bd7f432 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -98,9 +98,18 @@
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshadow")
 endif()
 
-if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.8.99") OR
-   CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11 -D_XOPEN_SOURCE=700")
+if(CMAKE_COMPILER_IS_GNUCXX)
+  if ((CMAKE_C_COMPILER_VERSION VERSION_GREATER "4.8.99") OR
+      CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
+  else()
+    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
+  endif()
+endif()
+
+# pthread_rwlock_t requires a feature flag.
+if(NOT WIN32)
+  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_XOPEN_SOURCE=700")
 endif()
 
 if(FUZZ)
diff --git a/src/STYLE.md b/src/STYLE.md
index 4d2c5de..17295b4 100644
--- a/src/STYLE.md
+++ b/src/STYLE.md
@@ -14,10 +14,10 @@
 given rule. Module-wide deviations on naming should be respected while
 integer and return value conventions take precedence over consistency.
 
-Some modules have seen few changes, so they still retain the original
-indentation style for now. When editing these, try to retain the
-original style. For Emacs, `doc/c-indentation.el` from OpenSSL may be
-helpful in this.
+Modules from OpenSSL's legacy ASN.1 and X.509 stack are retained for
+compatibility and left largely unmodified. To ease importing patches from
+upstream, they match OpenSSL's new indentation style. For Emacs,
+`doc/openssl-c-indent.el` from OpenSSL may be helpful in this.
 
 
 ## Language
diff --git a/src/crypto/asn1/a_strnid.c b/src/crypto/asn1/a_strnid.c
index ba1224e..c558bce 100644
--- a/src/crypto/asn1/a_strnid.c
+++ b/src/crypto/asn1/a_strnid.c
@@ -247,6 +247,7 @@
         }
         tmp->flags = flags | STABLE_FLAGS_MALLOC;
         tmp->nid = nid;
+        tmp->minsize = tmp->maxsize = -1;
         new_nid = 1;
     } else
         tmp->flags = (tmp->flags & STABLE_FLAGS_MALLOC) | flags;
diff --git a/src/crypto/asn1/bio_asn1.c b/src/crypto/asn1/bio_asn1.c
index 03cc9a6..45ad7e5 100644
--- a/src/crypto/asn1/bio_asn1.c
+++ b/src/crypto/asn1/bio_asn1.c
@@ -165,10 +165,12 @@
     ctx->copylen = 0;
     ctx->asn1_class = V_ASN1_UNIVERSAL;
     ctx->asn1_tag = V_ASN1_OCTET_STRING;
-    ctx->ex_buf = 0;
-    ctx->ex_pos = 0;
+    ctx->ex_buf = NULL;
     ctx->ex_len = 0;
+    ctx->ex_pos = 0;
     ctx->state = ASN1_STATE_START;
+    ctx->prefix = ctx->prefix_free = ctx->suffix = ctx->suffix_free = NULL;
+    ctx->ex_arg = NULL;
     return 1;
 }
 
diff --git a/src/crypto/asn1/bio_ndef.c b/src/crypto/asn1/bio_ndef.c
index 81a8aa7..488457b 100644
--- a/src/crypto/asn1/bio_ndef.c
+++ b/src/crypto/asn1/bio_ndef.c
@@ -139,6 +139,7 @@
     ndef_aux->ndef_bio = sarg.ndef_bio;
     ndef_aux->boundary = sarg.boundary;
     ndef_aux->out = out;
+    ndef_aux->derbuf = NULL;
 
     BIO_ctrl(asn_bio, BIO_C_SET_EX_ARG, 0, ndef_aux);
 
diff --git a/src/crypto/bio/bio_test.cc b/src/crypto/bio/bio_test.cc
index 3615ab4..f2eb20b 100644
--- a/src/crypto/bio/bio_test.cc
+++ b/src/crypto/bio/bio_test.cc
@@ -27,10 +27,10 @@
 #include <unistd.h>
 #else
 #include <io.h>
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/bio.h>
diff --git a/src/crypto/bio/connect.c b/src/crypto/bio/connect.c
index 01d49b1..7e54447 100644
--- a/src/crypto/bio/connect.c
+++ b/src/crypto/bio/connect.c
@@ -66,10 +66,10 @@
 #include <arpa/inet.h>
 #include <unistd.h>
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/buf.h>
@@ -538,6 +538,12 @@
   return BIO_ctrl(bio, BIO_C_SET_CONNECT, 1, (void*) port_str);
 }
 
+int BIO_set_conn_int_port(BIO *bio, const int *port) {
+  char buf[DECIMAL_SIZE(int) + 1];
+  BIO_snprintf(buf, sizeof(buf), "%d", *port);
+  return BIO_set_conn_port(bio, buf);
+}
+
 int BIO_set_nbio(BIO *bio, int on) {
   return BIO_ctrl(bio, BIO_C_SET_NBIO, on, NULL);
 }
diff --git a/src/crypto/bio/fd.c b/src/crypto/bio/fd.c
index 7d94843..13833df 100644
--- a/src/crypto/bio/fd.c
+++ b/src/crypto/bio/fd.c
@@ -63,9 +63,9 @@
 #include <unistd.h>
 #else
 #include <io.h>
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/buf.h>
diff --git a/src/crypto/bio/internal.h b/src/crypto/bio/internal.h
index eb6b26f..4ec77fa 100644
--- a/src/crypto/bio/internal.h
+++ b/src/crypto/bio/internal.h
@@ -67,9 +67,9 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 typedef int socklen_t;
 #endif
 
diff --git a/src/crypto/bio/socket.c b/src/crypto/bio/socket.c
index 3ef6967..0520c3e 100644
--- a/src/crypto/bio/socket.c
+++ b/src/crypto/bio/socket.c
@@ -63,9 +63,9 @@
 #if !defined(OPENSSL_WINDOWS)
 #include <unistd.h>
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #pragma comment(lib, "Ws2_32.lib")
 #endif
diff --git a/src/crypto/bio/socket_helper.c b/src/crypto/bio/socket_helper.c
index 4ddc094..9500788 100644
--- a/src/crypto/bio/socket_helper.c
+++ b/src/crypto/bio/socket_helper.c
@@ -26,10 +26,10 @@
 #include <netdb.h>
 #include <unistd.h>
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include "internal.h"
diff --git a/src/crypto/bn/bn_test.cc b/src/crypto/bn/bn_test.cc
index fe8cfd0..d909ee2 100644
--- a/src/crypto/bn/bn_test.cc
+++ b/src/crypto/bn/bn_test.cc
@@ -850,11 +850,17 @@
       return false;
     }
     BN_ULONG s = b->d[0];
+    BN_ULONG rmod = BN_mod_word(b.get(), s);
     BN_ULONG r = BN_div_word(b.get(), s);
     if (r == (BN_ULONG)-1) {
       return false;
     }
 
+    if (rmod != r) {
+      fprintf(stderr, "Mod (word) test failed!\n");
+      return false;
+    }
+
     if (fp != NULL) {
       BN_print_fp(fp, a.get());
       puts_fp(fp, " / ");
diff --git a/src/crypto/bn/div.c b/src/crypto/bn/div.c
index 6f67291..e824458 100644
--- a/src/crypto/bn/div.c
+++ b/src/crypto/bn/div.c
@@ -644,6 +644,20 @@
     return (BN_ULONG) -1;
   }
 
+#ifndef BN_ULLONG
+  /* If |w| is too long and we don't have |BN_ULLONG| then we need to fall back
+   * to using |BN_div_word|. */
+  if (w > ((BN_ULONG)1 << BN_BITS4)) {
+    BIGNUM *tmp = BN_dup(a);
+    if (tmp == NULL) {
+      return (BN_ULONG)-1;
+    }
+    ret = BN_div_word(tmp, w);
+    BN_free(tmp);
+    return ret;
+  }
+#endif
+
   w &= BN_MASK2;
   for (i = a->top - 1; i >= 0; i--) {
 #ifndef BN_ULLONG
diff --git a/src/crypto/bn/internal.h b/src/crypto/bn/internal.h
index 0a2982c..8b1c866 100644
--- a/src/crypto/bn/internal.h
+++ b/src/crypto/bn/internal.h
@@ -126,9 +126,9 @@
 #include <openssl/base.h>
 
 #if defined(OPENSSL_X86_64) && defined(_MSC_VER)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <intrin.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #pragma intrinsic(__umulh, _umul128)
 #endif
 
diff --git a/src/crypto/bytestring/bytestring_test.cc b/src/crypto/bytestring/bytestring_test.cc
index 84ecffc..e1d16f4 100644
--- a/src/crypto/bytestring/bytestring_test.cc
+++ b/src/crypto/bytestring/bytestring_test.cc
@@ -43,7 +43,7 @@
 }
 
 static bool TestGetUint() {
-  static const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
+  static const uint8_t kData[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
   uint8_t u8;
   uint16_t u16;
   uint32_t u32;
@@ -58,7 +58,10 @@
     u32 == 0x40506 &&
     CBS_get_u32(&data, &u32) &&
     u32 == 0x708090a &&
-    !CBS_get_u8(&data, &u8);
+    CBS_get_last_u8(&data, &u8) &&
+    u8 == 0xb &&
+    !CBS_get_u8(&data, &u8) &&
+    !CBS_get_last_u8(&data, &u8);
 }
 
 static bool TestGetPrefixed() {
diff --git a/src/crypto/bytestring/cbs.c b/src/crypto/bytestring/cbs.c
index ed54b49..c86afbd 100644
--- a/src/crypto/bytestring/cbs.c
+++ b/src/crypto/bytestring/cbs.c
@@ -128,6 +128,15 @@
   return cbs_get_u(cbs, out, 4);
 }
 
+int CBS_get_last_u8(CBS *cbs, uint8_t *out) {
+  if (cbs->len == 0) {
+    return 0;
+  }
+  *out = cbs->data[cbs->len - 1];
+  cbs->len--;
+  return 1;
+}
+
 int CBS_get_bytes(CBS *cbs, CBS *out, size_t len) {
   const uint8_t *v;
   if (!cbs_get(cbs, &v, len)) {
diff --git a/src/crypto/chacha/asm/chacha-x86.pl b/src/crypto/chacha/asm/chacha-x86.pl
index e576029..edce43d 100755
--- a/src/crypto/chacha/asm/chacha-x86.pl
+++ b/src/crypto/chacha/asm/chacha-x86.pl
@@ -19,13 +19,13 @@
 # P4		18.6/+84%
 # Core2		9.56/+89%	4.83
 # Westmere	9.50/+45%	3.35
-# Sandy Bridge	10.7/+47%	3.24
-# Haswell	8.22/+50%	2.89
-# Silvermont	17.8/+36%	8.53
+# Sandy Bridge	10.5/+47%	3.20
+# Haswell	8.15/+50%	2.83
+# Silvermont	17.4/+36%	8.35
 # Sledgehammer	10.2/+54%
-# Bulldozer	13.5/+50%	4.39(*)
+# Bulldozer	13.4/+50%	4.38(*)
 #
-# (*)  Bulldozer actually executes 4xXOP code path that delivers 3.50;
+# (*)	Bulldozer actually executes 4xXOP code path that delivers 3.55;
 #
 # Modified from upstream OpenSSL to remove the XOP code.
 
@@ -224,20 +224,18 @@
 
 	&xor	($a, &DWP(4*0,$b));		# xor with input
 	&xor	($b_,&DWP(4*4,$b));
-	&mov	(&DWP(4*0,"esp"),$a);		# off-load for later write
+	&mov	(&DWP(4*0,"esp"),$a);
 	&mov	($a,&wparam(0));		# load output pointer
 	&xor	($c, &DWP(4*8,$b));
 	&xor	($c_,&DWP(4*9,$b));
 	&xor	($d, &DWP(4*12,$b));
 	&xor	($d_,&DWP(4*14,$b));
-	&mov	(&DWP(4*4,"esp"),$b_);
-	&mov	($b_,&DWP(4*0,"esp"));
-	&mov	(&DWP(4*8,"esp"),$c);
-	&mov	(&DWP(4*9,"esp"),$c_);
-	&mov	(&DWP(4*12,"esp"),$d);
-	&mov	(&DWP(4*14,"esp"),$d_);
+	&mov	(&DWP(4*4,$a),$b_);		# write output
+	&mov	(&DWP(4*8,$a),$c);
+	&mov	(&DWP(4*9,$a),$c_);
+	&mov	(&DWP(4*12,$a),$d);
+	&mov	(&DWP(4*14,$a),$d_);
 
-	&mov	(&DWP(4*0,$a),$b_);		# write output in order
 	&mov	($b_,&DWP(4*1,"esp"));
 	&mov	($c, &DWP(4*2,"esp"));
 	&mov	($c_,&DWP(4*3,"esp"));
@@ -254,45 +252,35 @@
 	&xor	($d, &DWP(4*5,$b));
 	&xor	($d_,&DWP(4*6,$b));
 	&mov	(&DWP(4*1,$a),$b_);
-	&mov	($b_,&DWP(4*4,"esp"));
 	&mov	(&DWP(4*2,$a),$c);
 	&mov	(&DWP(4*3,$a),$c_);
-	&mov	(&DWP(4*4,$a),$b_);
 	&mov	(&DWP(4*5,$a),$d);
 	&mov	(&DWP(4*6,$a),$d_);
 
-	&mov	($c,&DWP(4*7,"esp"));
-	&mov	($d,&DWP(4*8,"esp"));
-	&mov	($d_,&DWP(4*9,"esp"));
-	&add	($c,&DWP(64+4*7,"esp"));
-	&mov	($b_, &DWP(4*10,"esp"));
-	&xor	($c,&DWP(4*7,$b));
+	&mov	($b_,&DWP(4*7,"esp"));
+	&mov	($c, &DWP(4*10,"esp"));
 	&mov	($c_,&DWP(4*11,"esp"));
-	&mov	(&DWP(4*7,$a),$c);
-	&mov	(&DWP(4*8,$a),$d);
-	&mov	(&DWP(4*9,$a),$d_);
-
-	&add	($b_, &DWP(64+4*10,"esp"));
-	&add	($c_,&DWP(64+4*11,"esp"));
-	&xor	($b_, &DWP(4*10,$b));
-	&xor	($c_,&DWP(4*11,$b));
-	&mov	(&DWP(4*10,$a),$b_);
-	&mov	(&DWP(4*11,$a),$c_);
-
-	&mov	($c,&DWP(4*12,"esp"));
-	&mov	($c_,&DWP(4*14,"esp"));
 	&mov	($d, &DWP(4*13,"esp"));
 	&mov	($d_,&DWP(4*15,"esp"));
+	&add	($b_,&DWP(64+4*7,"esp"));
+	&add	($c, &DWP(64+4*10,"esp"));
+	&add	($c_,&DWP(64+4*11,"esp"));
 	&add	($d, &DWP(64+4*13,"esp"));
 	&add	($d_,&DWP(64+4*15,"esp"));
+	&xor	($b_,&DWP(4*7,$b));
+	&xor	($c, &DWP(4*10,$b));
+	&xor	($c_,&DWP(4*11,$b));
 	&xor	($d, &DWP(4*13,$b));
 	&xor	($d_,&DWP(4*15,$b));
 	&lea	($b,&DWP(4*16,$b));
-	&mov	(&DWP(4*12,$a),$c);
+	&mov	(&DWP(4*7,$a),$b_);
+	&mov	($b_,&DWP(4*0,"esp"));
+	&mov	(&DWP(4*10,$a),$c);
 	&mov	($c,&wparam(2));		# len
+	&mov	(&DWP(4*11,$a),$c_);
 	&mov	(&DWP(4*13,$a),$d);
-	&mov	(&DWP(4*14,$a),$c_);
 	&mov	(&DWP(4*15,$a),$d_);
+	&mov	(&DWP(4*0,$a),$b_);
 	&lea	($a,&DWP(4*16,$a));
 	&sub	($c,64);
 	&jnz	(&label("outer_loop"));
@@ -567,12 +555,12 @@
 
     my ($xa0,$xa1,$xa2,$xa3,$xt0,$xt1,$xt2,$xt3)=map("xmm$_",(0..7));
 
-    for($i=0;$i<256;$i+=64) {
-	#&movdqa	($xa0,&QWP($i+16*0-128,"ebx"));	# it's there
-	&movdqa		($xa1,&QWP($i+16*1-128,"ebx"));
-	&movdqa		($xa2,&QWP($i+16*2-128,"ebx"));
-	&movdqa		($xa3,&QWP($i+16*3-128,"ebx"));
+	#&movdqa	($xa0,&QWP(16*0-128,"ebx"));	# it's there
+	&movdqa		($xa1,&QWP(16*1-128,"ebx"));
+	&movdqa		($xa2,&QWP(16*2-128,"ebx"));
+	&movdqa		($xa3,&QWP(16*3-128,"ebx"));
 
+    for($i=0;$i<256;$i+=64) {
 	&paddd		($xa0,&QWP($i+16*0-128,"ebp"));	# accumulate key material
 	&paddd		($xa1,&QWP($i+16*1-128,"ebp"));
 	&paddd		($xa2,&QWP($i+16*2-128,"ebp"));
@@ -593,29 +581,25 @@
 
 	#($xa2,$xt2)=($xt2,$xa2);
 
-	&movdqa		(&QWP($i+16*0-128,"ebx"),$xa0);
+	&movdqu		($xt0,&QWP(64*0-128,$inp));	# load input
+	&movdqu		($xt1,&QWP(64*1-128,$inp));
+	&movdqu		($xa2,&QWP(64*2-128,$inp));
+	&movdqu		($xt3,&QWP(64*3-128,$inp));
+	&lea		($inp,&QWP($i<192?16:(64*4-16*3),$inp));
+	&pxor		($xt0,$xa0);
 	&movdqa		($xa0,&QWP($i+16*4-128,"ebx"))	if ($i<192);
-	&movdqa		(&QWP($i+16*1-128,"ebx"),$xa1);
-	&movdqa		(&QWP($i+16*2-128,"ebx"),$xt2);
-	&movdqa		(&QWP($i+16*3-128,"ebx"),$xa3);
+	&pxor		($xt1,$xa1);
+	&movdqa		($xa1,&QWP($i+16*5-128,"ebx"))	if ($i<192);
+	&pxor		($xt2,$xa2);
+	&movdqa		($xa2,&QWP($i+16*6-128,"ebx"))	if ($i<192);
+	&pxor		($xt3,$xa3);
+	&movdqa		($xa3,&QWP($i+16*7-128,"ebx"))	if ($i<192);
+	&movdqu		(&QWP(64*0-128,$out),$xt0);	# store output
+	&movdqu		(&QWP(64*1-128,$out),$xt1);
+	&movdqu		(&QWP(64*2-128,$out),$xt2);
+	&movdqu		(&QWP(64*3-128,$out),$xt3);
+	&lea		($out,&QWP($i<192?16:(64*4-16*3),$out));
     }
-    for($i=0;$i<256;$i+=64) {
-	my $j = 16*($i/64);
-	&movdqu		($xa0,&QWP($i+16*0-128,$inp));	# load input
-	&movdqu		($xa1,&QWP($i+16*1-128,$inp));
-	&movdqu		($xa2,&QWP($i+16*2-128,$inp));
-	&movdqu		($xa3,&QWP($i+16*3-128,$inp));
-	&pxor		($xa0,&QWP($j+64*0-128,"ebx"));
-	&pxor		($xa1,&QWP($j+64*1-128,"ebx"));
-	&pxor		($xa2,&QWP($j+64*2-128,"ebx"));
-	&pxor		($xa3,&QWP($j+64*3-128,"ebx"));
-	&movdqu		(&QWP($i+16*0-128,$out),$xa0);	# write output
-	&movdqu		(&QWP($i+16*1-128,$out),$xa1);
-	&movdqu		(&QWP($i+16*2-128,$out),$xa2);
-	&movdqu		(&QWP($i+16*3-128,$out),$xa3);
-    }
-	&lea		($inp,&DWP(256,$inp));
-	&lea		($out,&DWP(256,$out));
 	&sub		($len,64*4);
 	&jnc		(&label("outer_loop"));
 
diff --git a/src/crypto/chacha/chacha.c b/src/crypto/chacha/chacha.c
index afe1b2a..1562089 100644
--- a/src/crypto/chacha/chacha.c
+++ b/src/crypto/chacha/chacha.c
@@ -16,10 +16,13 @@
 
 #include <openssl/chacha.h>
 
+#include <assert.h>
 #include <string.h>
 
 #include <openssl/cpu.h>
 
+#include "../internal.h"
+
 
 #define U8TO32_LITTLE(p)                              \
   (((uint32_t)((p)[0])) | ((uint32_t)((p)[1]) << 8) | \
@@ -36,8 +39,9 @@
 void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len,
                       const uint8_t key[32], const uint8_t nonce[12],
                       uint32_t counter) {
-  uint32_t counter_nonce[4];
-  counter_nonce[0] = counter;
+  assert(!buffers_alias(out, in_len, in, in_len) || in == out);
+
+  uint32_t counter_nonce[4];  counter_nonce[0] = counter;
   counter_nonce[1] = U8TO32_LITTLE(nonce + 0);
   counter_nonce[2] = U8TO32_LITTLE(nonce + 4);
   counter_nonce[3] = U8TO32_LITTLE(nonce + 8);
@@ -118,6 +122,8 @@
 void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in, size_t in_len,
                       const uint8_t key[32], const uint8_t nonce[12],
                       uint32_t counter) {
+  assert(!buffers_alias(out, in_len, in, in_len) || in == out);
+
   uint32_t input[16];
   uint8_t buf[64];
   size_t todo, i;
diff --git a/src/crypto/chacha/chacha_test.cc b/src/crypto/chacha/chacha_test.cc
index f364f98..0a5972f 100644
--- a/src/crypto/chacha/chacha_test.cc
+++ b/src/crypto/chacha/chacha_test.cc
@@ -218,25 +218,16 @@
   std::unique_ptr<uint8_t[]> buf(new uint8_t[len]);
   CRYPTO_chacha_20(buf.get(), kInput, len, kKey, kNonce, kCounter);
   if (memcmp(buf.get(), kOutput, len) != 0) {
-    fprintf(stderr, "Mismatch at length %u.\n", static_cast<unsigned>(len));
+    fprintf(stderr, "Mismatch at length %zu.\n", len);
     return false;
   }
 
-  // Test in-place at various offsets.
-  static const size_t kOffsets[] = {
-      0,  1,  2,  8,  15, 16,  17,  31,  32,  33,  63,
-      64, 65, 95, 96, 97, 127, 128, 129, 255, 256, 257,
-  };
-  for (size_t offset : kOffsets) {
-    buf.reset(new uint8_t[len + offset]);
-    memcpy(buf.get() + offset, kInput, len);
-    CRYPTO_chacha_20(buf.get(), buf.get() + offset, len, kKey, kNonce,
-                     kCounter);
-    if (memcmp(buf.get(), kOutput, len) != 0) {
-      fprintf(stderr, "Mismatch at length %u with in-place offset %u.\n",
-              static_cast<unsigned>(len), static_cast<unsigned>(offset));
-      return false;
-    }
+  // Test in-place.
+  memcpy(buf.get(), kInput, len);
+  CRYPTO_chacha_20(buf.get(), buf.get(), len, kKey, kNonce, kCounter);
+  if (memcmp(buf.get(), kOutput, len) != 0) {
+    fprintf(stderr, "Mismatch at length %zu, in-place.\n", len);
+    return false;
   }
 
   return true;
diff --git a/src/crypto/cipher/aead.c b/src/crypto/cipher/aead.c
index b1db83d..57eecc1 100644
--- a/src/crypto/cipher/aead.c
+++ b/src/crypto/cipher/aead.c
@@ -20,6 +20,7 @@
 #include <openssl/err.h>
 
 #include "internal.h"
+#include "../internal.h"
 
 
 size_t EVP_AEAD_key_length(const EVP_AEAD *aead) { return aead->key_len; }
@@ -80,21 +81,15 @@
   ctx->aead = NULL;
 }
 
-/* check_alias returns 0 if |out| points within the buffer determined by |in|
- * and |in_len| and 1 otherwise.
- *
- * When processing, there's only an issue if |out| points within in[:in_len]
- * and isn't equal to |in|. If that's the case then writing the output will
- * stomp input that hasn't been read yet.
- *
- * This function checks for that case. */
-static int check_alias(const uint8_t *in, size_t in_len, const uint8_t *out) {
-  if (out <= in) {
-    return 1;
-  } else if (in + in_len <= out) {
+/* check_alias returns 1 if |out| is compatible with |in| and 0 otherwise. If
+ * |in| and |out| alias, we require that |in| == |out|. */
+static int check_alias(const uint8_t *in, size_t in_len, const uint8_t *out,
+                       size_t out_len) {
+  if (!buffers_alias(in, in_len, out, out_len)) {
     return 1;
   }
-  return 0;
+
+  return in == out;
 }
 
 int EVP_AEAD_CTX_seal(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
@@ -108,7 +103,7 @@
     goto error;
   }
 
-  if (!check_alias(in, in_len, out)) {
+  if (!check_alias(in, in_len, out, max_out_len)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT);
     goto error;
   }
@@ -130,7 +125,7 @@
                       size_t max_out_len, const uint8_t *nonce,
                       size_t nonce_len, const uint8_t *in, size_t in_len,
                       const uint8_t *ad, size_t ad_len) {
-  if (!check_alias(in, in_len, out)) {
+  if (!check_alias(in, in_len, out, max_out_len)) {
     OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_OUTPUT_ALIASES_INPUT);
     goto error;
   }
diff --git a/src/crypto/cipher/aead_test.cc b/src/crypto/cipher/aead_test.cc
index f21291e..8bad93f 100644
--- a/src/crypto/cipher/aead_test.cc
+++ b/src/crypto/cipher/aead_test.cc
@@ -225,84 +225,65 @@
     return false;
   }
 
-  // First test with out > in, which we expect to fail.
-  for (auto offset : offsets) {
-    if (offset == 0) {
-      // Will be tested in the next loop.
-      continue;
-    }
+  // Test with out != in which we expect to fail.
+  std::vector<uint8_t> buffer(2 + valid_encryption_len);
+  uint8_t *in = buffer.data() + 1;
+  uint8_t *out1 = buffer.data();
+  uint8_t *out2 = buffer.data() + 2;
 
-    std::vector<uint8_t> buffer(offset + valid_encryption_len);
-    memcpy(buffer.data(), kPlaintext, sizeof(kPlaintext));
-    uint8_t *out = buffer.data() + offset;
+  memcpy(in, kPlaintext, sizeof(kPlaintext));
+  size_t out_len;
+  if (EVP_AEAD_CTX_seal(ctx.get(), out1, &out_len,
+                        sizeof(kPlaintext) + max_overhead, nonce.data(),
+                        nonce_len, in, sizeof(kPlaintext), nullptr, 0) ||
+      EVP_AEAD_CTX_seal(ctx.get(), out2, &out_len,
+                        sizeof(kPlaintext) + max_overhead, nonce.data(),
+                        nonce_len, in, sizeof(kPlaintext), nullptr, 0)) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal unexpectedly succeeded.\n");
+    return false;
+  }
+  ERR_clear_error();
 
-    size_t out_len;
-    if (!EVP_AEAD_CTX_seal(ctx.get(), out, &out_len,
-                           sizeof(kPlaintext) + max_overhead, nonce.data(),
-                           nonce_len, buffer.data(), sizeof(kPlaintext),
-                           nullptr, 0)) {
-      // We expect offsets where the output is greater than the input to fail.
-      ERR_clear_error();
-    } else {
-      fprintf(stderr,
-              "EVP_AEAD_CTX_seal unexpectedly succeeded for offset %u.\n",
-              static_cast<unsigned>(offset));
-      return false;
-    }
+  memcpy(in, valid_encryption.data(), valid_encryption_len);
+  if (EVP_AEAD_CTX_open(ctx.get(), out1, &out_len, valid_encryption_len,
+                        nonce.data(), nonce_len, in, valid_encryption_len,
+                        nullptr, 0) ||
+      EVP_AEAD_CTX_open(ctx.get(), out2, &out_len, valid_encryption_len,
+                        nonce.data(), nonce_len, in, valid_encryption_len,
+                        nullptr, 0)) {
+    fprintf(stderr, "EVP_AEAD_CTX_open unexpectedly succeeded.\n");
+    return false;
+  }
+  ERR_clear_error();
 
-    memcpy(buffer.data(), valid_encryption.data(), valid_encryption_len);
-    if (!EVP_AEAD_CTX_open(ctx.get(), out, &out_len, valid_encryption_len,
-                           nonce.data(), nonce_len, buffer.data(),
-                           valid_encryption_len, nullptr, 0)) {
-      // We expect offsets where the output is greater than the input to fail.
-      ERR_clear_error();
-    } else {
-      fprintf(stderr,
-              "EVP_AEAD_CTX_open unexpectedly succeeded for offset %u.\n",
-              static_cast<unsigned>(offset));
-      ERR_print_errors_fp(stderr);
-      return false;
-    }
+  // Test with out == in, which we expect to work.
+  memcpy(in, kPlaintext, sizeof(kPlaintext));
+
+  if (!EVP_AEAD_CTX_seal(ctx.get(), in, &out_len,
+                         sizeof(kPlaintext) + max_overhead, nonce.data(),
+                         nonce_len, in, sizeof(kPlaintext), nullptr, 0)) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal failed in-place.\n");
+    return false;
   }
 
-  // Test with out <= in, which we expect to work.
-  for (auto offset : offsets) {
-    std::vector<uint8_t> buffer(offset + valid_encryption_len);
-    uint8_t *const out = buffer.data();
-    uint8_t *const in = buffer.data() + offset;
-    memcpy(in, kPlaintext, sizeof(kPlaintext));
+  if (out_len != valid_encryption_len ||
+      memcmp(in, valid_encryption.data(), out_len) != 0) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal produced bad output in-place.\n");
+    return false;
+  }
 
-    size_t out_len;
-    if (!EVP_AEAD_CTX_seal(ctx.get(), out, &out_len,
-                           sizeof(kPlaintext) + max_overhead, nonce.data(),
-                           nonce_len, in, sizeof(kPlaintext), nullptr, 0)) {
-      fprintf(stderr, "EVP_AEAD_CTX_seal failed for offset -%u.\n",
-              static_cast<unsigned>(offset));
-      return false;
-    }
+  memcpy(in, valid_encryption.data(), valid_encryption_len);
+  if (!EVP_AEAD_CTX_open(ctx.get(), in, &out_len, valid_encryption_len,
+                         nonce.data(), nonce_len, in, valid_encryption_len,
+                         nullptr, 0)) {
+    fprintf(stderr, "EVP_AEAD_CTX_open failed in-place.\n");
+    return false;
+  }
 
-    if (out_len != valid_encryption_len ||
-        memcmp(out, valid_encryption.data(), out_len) != 0) {
-      fprintf(stderr, "EVP_AEAD_CTX_seal produced bad output for offset -%u.\n",
-              static_cast<unsigned>(offset));
-      return false;
-    }
-
-    memcpy(in, valid_encryption.data(), valid_encryption_len);
-    if (!EVP_AEAD_CTX_open(ctx.get(), out, &out_len,
-                           offset + valid_encryption_len, nonce.data(),
-                           nonce_len, in, valid_encryption_len, nullptr, 0)) {
-      fprintf(stderr, "EVP_AEAD_CTX_open failed for offset -%u.\n",
-              static_cast<unsigned>(offset));
-      return false;
-    }
-
-    if (out_len != sizeof(kPlaintext) ||
-        memcmp(out, kPlaintext, out_len) != 0) {
-      fprintf(stderr, "EVP_AEAD_CTX_open produced bad output for offset -%u.\n",
-              static_cast<unsigned>(offset));
-      return false;
-    }
+  if (out_len != sizeof(kPlaintext) ||
+      memcmp(in, kPlaintext, out_len) != 0) {
+    fprintf(stderr, "EVP_AEAD_CTX_open produced bad output in-place.\n");
+    return false;
   }
 
   return true;
diff --git a/src/crypto/cipher/e_aes.c b/src/crypto/cipher/e_aes.c
index d61d048..24c4d8a 100644
--- a/src/crypto/cipher/e_aes.c
+++ b/src/crypto/cipher/e_aes.c
@@ -67,9 +67,7 @@
 #endif
 
 
-#if defined(_MSC_VER)
-#pragma warning(disable: 4702) /* Unreachable code. */
-#endif
+OPENSSL_MSVC_PRAGMA(warning(disable: 4702)) /* Unreachable code. */
 
 typedef struct {
   union {
diff --git a/src/crypto/cipher/test/aes_128_gcm_tests.txt b/src/crypto/cipher/test/aes_128_gcm_tests.txt
index 0e33c91..eac9aa9 100644
--- a/src/crypto/cipher/test/aes_128_gcm_tests.txt
+++ b/src/crypto/cipher/test/aes_128_gcm_tests.txt
@@ -1,4 +1,4 @@
-# The AES-128-GCM test cases from cipher_test.txt have been merged into this
+# The AES-128-GCM test cases from cipher_tests.txt have been merged into this
 # file.
 
 KEY: d480429666d48b400633921c5407d1d1
diff --git a/src/crypto/cipher/test/aes_256_gcm_tests.txt b/src/crypto/cipher/test/aes_256_gcm_tests.txt
index dbcee81..30fd422 100644
--- a/src/crypto/cipher/test/aes_256_gcm_tests.txt
+++ b/src/crypto/cipher/test/aes_256_gcm_tests.txt
@@ -1,4 +1,4 @@
-# The AES-256-GCM test cases from cipher_test.txt have been merged into this
+# The AES-256-GCM test cases from cipher_tests.txt have been merged into this
 # file.
 
 KEY: e5ac4a32c67e425ac4b143c83c6f161312a97d88d634afdf9f4da5bd35223f01
diff --git a/src/crypto/cipher/test/cipher_test.txt b/src/crypto/cipher/test/cipher_tests.txt
similarity index 100%
rename from src/crypto/cipher/test/cipher_test.txt
rename to src/crypto/cipher/test/cipher_tests.txt
diff --git a/src/crypto/cpu-intel.c b/src/crypto/cpu-intel.c
index 431e1e1..f2e0c4c 100644
--- a/src/crypto/cpu-intel.c
+++ b/src/crypto/cpu-intel.c
@@ -69,10 +69,10 @@
 #include <string.h>
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <immintrin.h>
 #include <intrin.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include "internal.h"
diff --git a/src/crypto/curve25519/x25519-x86_64.c b/src/crypto/curve25519/x25519-x86_64.c
index 9776c75..1bd86a0 100644
--- a/src/crypto/curve25519/x25519-x86_64.c
+++ b/src/crypto/curve25519/x25519-x86_64.c
@@ -1,3 +1,24 @@
+/* Copyright (c) 2015, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+/* This code is mostly taken from the ref10 version of Ed25519 in SUPERCOP
+ * 20141124 (http://bench.cr.yp.to/supercop.html). That code is released as
+ * public domain but this file has the ISC license just to keep licencing
+ * simple.
+ *
+ * The field functions are shared by Ed25519 and X25519 where possible. */
+
 #include <openssl/curve25519.h>
 
 #include <string.h>
diff --git a/src/crypto/dsa/dsa.c b/src/crypto/dsa/dsa.c
index fe29aa0..1de0071 100644
--- a/src/crypto/dsa/dsa.c
+++ b/src/crypto/dsa/dsa.c
@@ -845,6 +845,7 @@
     goto err;
   }
 
+  BN_set_flags(&kq, BN_FLG_CONSTTIME);
   K = &kq;
 
   if (!BN_mod_exp_mont(r, dsa->g, K, dsa->p, ctx, dsa->method_mont_p)) {
diff --git a/src/crypto/err/err.c b/src/crypto/err/err.c
index c4e4b13..48d631f 100644
--- a/src/crypto/err/err.c
+++ b/src/crypto/err/err.c
@@ -114,9 +114,9 @@
 #include <string.h>
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/mem.h>
diff --git a/src/crypto/evp/evp.c b/src/crypto/evp/evp.c
index 79993aa..bc1585e 100644
--- a/src/crypto/evp/evp.c
+++ b/src/crypto/evp/evp.c
@@ -356,6 +356,8 @@
 
 void OpenSSL_add_all_algorithms(void) {}
 
+void OpenSSL_add_all_algorithms_conf(void) {}
+
 void OpenSSL_add_all_ciphers(void) {}
 
 void OpenSSL_add_all_digests(void) {}
diff --git a/src/crypto/evp/evp_test.cc b/src/crypto/evp/evp_test.cc
index a7dac2b..b01d1e4 100644
--- a/src/crypto/evp/evp_test.cc
+++ b/src/crypto/evp/evp_test.cc
@@ -51,30 +51,27 @@
  * ====================================================================
  */
 
+#include <openssl/evp.h>
+
 #include <stdio.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <string.h>
 
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable: 4702)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(push))
+OPENSSL_MSVC_PRAGMA(warning(disable: 4702))
 
 #include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #include <openssl/bytestring.h>
 #include <openssl/crypto.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
-#include <openssl/evp.h>
 
 #include "../test/file_test.h"
 #include "../test/scoped_types.h"
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index 433072c..e35fb7b 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -123,9 +123,9 @@
 
 #if defined(OPENSSL_NO_THREADS)
 #elif defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #else
 #include <pthread.h>
 #endif
@@ -152,6 +152,19 @@
 #endif
 
 
+/* buffers_alias returns one if |a| and |b| alias and zero otherwise. */
+static inline int buffers_alias(const uint8_t *a, size_t a_len,
+                                const uint8_t *b, size_t b_len) {
+  /* Cast |a| and |b| to integers. In C, pointer comparisons between unrelated
+   * objects are undefined whereas pointer to integer conversions are merely
+   * implementation-defined. We assume the implementation defined it in a sane
+   * way. */
+  uintptr_t a_u = (uintptr_t)a;
+  uintptr_t b_u = (uintptr_t)b;
+  return a_u + a_len > b_u && b_u + b_len > a_u;
+}
+
+
 /* Constant-time utility functions.
  *
  * The following methods return a bitmask of all ones (0xff...f) for true and 0
diff --git a/src/crypto/mem.c b/src/crypto/mem.c
index 7d73c73..ca4c559 100644
--- a/src/crypto/mem.c
+++ b/src/crypto/mem.c
@@ -66,9 +66,9 @@
 #include <string.h>
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #else
 #include <strings.h>
 #endif
@@ -118,12 +118,11 @@
 }
 
 int CRYPTO_memcmp(const void *in_a, const void *in_b, size_t len) {
-  size_t i;
   const uint8_t *a = in_a;
   const uint8_t *b = in_b;
   uint8_t x = 0;
 
-  for (i = 0; i < len; i++) {
+  for (size_t i = 0; i < len; i++) {
     x |= a[i] ^ b[i];
   }
 
diff --git a/src/crypto/modes/internal.h b/src/crypto/modes/internal.h
index b46e836..430d040 100644
--- a/src/crypto/modes/internal.h
+++ b/src/crypto/modes/internal.h
@@ -121,9 +121,9 @@
 #endif
 #elif defined(_MSC_VER)
 #if _MSC_VER >= 1300
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <intrin.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
 #define BSWAP8(x) _byteswap_uint64((uint64_t)(x))
 #define BSWAP4(x) _byteswap_ulong((uint32_t)(x))
diff --git a/src/crypto/newhope/CMakeLists.txt b/src/crypto/newhope/CMakeLists.txt
index 5d339ba..7787cce 100644
--- a/src/crypto/newhope/CMakeLists.txt
+++ b/src/crypto/newhope/CMakeLists.txt
@@ -19,6 +19,13 @@
 )
 
 add_executable(
+  newhope_statistical_test
+
+  newhope_statistical_test.cc
+  $<TARGET_OBJECTS:test_support>
+)
+
+add_executable(
   newhope_vectors_test
 
   newhope_vectors_test.cc
@@ -26,6 +33,8 @@
 )
 
 target_link_libraries(newhope_test crypto)
+target_link_libraries(newhope_statistical_test crypto)
 target_link_libraries(newhope_vectors_test crypto)
 add_dependencies(all_tests newhope_test)
+add_dependencies(all_tests newhope_statistical_test)
 add_dependencies(all_tests newhope_vectors_test)
diff --git a/src/crypto/newhope/internal.h b/src/crypto/newhope/internal.h
index 5c82d68..efc5adf 100644
--- a/src/crypto/newhope/internal.h
+++ b/src/crypto/newhope/internal.h
@@ -43,12 +43,6 @@
  * |seed|. (In the reference implementation this was done with SHAKE-128.) */
 void newhope_poly_uniform(NEWHOPE_POLY* a, const uint8_t* seed);
 
-/* newhope_poly_getnoise sets |r| to a random polynomial where the coefficients
- * are
- * sampled from the noise distribution. (In the reference implementation, this
- * is given a random seed and a nonce.)*/
-void newhope_poly_getnoise(NEWHOPE_POLY* r);
-
 void newhope_helprec(NEWHOPE_POLY* c, const NEWHOPE_POLY* v,
                      const uint8_t rbits[32]);
 
@@ -58,9 +52,6 @@
 void newhope_reconcile(uint8_t* key, const NEWHOPE_POLY* v,
                        const NEWHOPE_POLY* c);
 
-/* newhope_poly_ntt performs NTT(r) in-place. */
-void newhope_poly_ntt(NEWHOPE_POLY* r);
-
 /* newhope_poly_invntt performs the inverse of NTT(r) in-place. */
 void newhope_poly_invntt(NEWHOPE_POLY* r);
 
diff --git a/src/crypto/newhope/newhope.c b/src/crypto/newhope/newhope.c
index c590cfa..7edb7eb 100644
--- a/src/crypto/newhope/newhope.c
+++ b/src/crypto/newhope/newhope.c
@@ -47,8 +47,7 @@
 }
 
 void NEWHOPE_offer(uint8_t *offermsg, NEWHOPE_POLY *s) {
-  newhope_poly_getnoise(s);
-  newhope_poly_ntt(s);
+  NEWHOPE_POLY_noise_ntt(s);
 
   /* The first part of the offer message is the seed, which compactly encodes
    * a. */
@@ -58,8 +57,7 @@
   newhope_poly_uniform(&a, seed);
 
   NEWHOPE_POLY e;
-  newhope_poly_getnoise(&e);
-  newhope_poly_ntt(&e);
+  NEWHOPE_POLY_noise_ntt(&e);
 
   /* The second part of the offer message is the polynomial pk = a*s+e */
   NEWHOPE_POLY pk;
@@ -78,17 +76,13 @@
   /* Decode the |offermsg|, generating the same |a| as the peer, from the peer's
    * seed. */
   NEWHOPE_POLY pk, a;
-  const uint8_t *seed = &offermsg[NEWHOPE_POLY_LENGTH];
-  newhope_poly_uniform(&a, seed);
-  NEWHOPE_POLY_frombytes(&pk, offermsg);
+  NEWHOPE_offer_frommsg(&pk, &a, offermsg);
 
   /* Generate noise polynomials used to generate our key. */
   NEWHOPE_POLY sp, ep, epp;
-  newhope_poly_getnoise(&sp);
-  newhope_poly_ntt(&sp);
-  newhope_poly_getnoise(&ep);
-  newhope_poly_ntt(&ep);
-  newhope_poly_getnoise(&epp);  /* intentionally not NTT */
+  NEWHOPE_POLY_noise_ntt(&sp);
+  NEWHOPE_POLY_noise_ntt(&ep);
+  NEWHOPE_POLY_noise(&epp);  /* intentionally not NTT */
 
   /* Generate random bytes used for reconciliation. (The reference
    * implementation calls ChaCha20 here.) */
@@ -171,3 +165,10 @@
   newhope_poly_invntt(&v);
   newhope_reconcile(k, &v, reconciliation);
 }
+
+void NEWHOPE_offer_frommsg(NEWHOPE_POLY *out_pk, NEWHOPE_POLY *out_a,
+                           const uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH]) {
+  NEWHOPE_POLY_frombytes(out_pk, offermsg);
+  const uint8_t *seed = offermsg + NEWHOPE_POLY_LENGTH;
+  newhope_poly_uniform(out_a, seed);
+}
diff --git a/src/crypto/newhope/newhope_statistical_test.cc b/src/crypto/newhope/newhope_statistical_test.cc
new file mode 100644
index 0000000..44fac48
--- /dev/null
+++ b/src/crypto/newhope/newhope_statistical_test.cc
@@ -0,0 +1,156 @@
+/* Copyright (c) 2016, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <string>
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/crypto.h>
+#include <openssl/rand.h>
+
+#include "../test/scoped_types.h"
+#include "internal.h"
+
+
+static const unsigned kNumTests = 1000;
+
+static bool TestNoise(void) {
+  printf("noise distribution:\n");
+
+  size_t buckets[1 + 2 * PARAM_K];
+  memset(buckets, 0, sizeof(buckets));
+  for (size_t i = 0; i < kNumTests; i++) {
+    NEWHOPE_POLY s;
+    NEWHOPE_POLY_noise(&s);
+    for (int j = 0; j < PARAM_N; j++) {
+      uint16_t value = (s.coeffs[j] + PARAM_K) % PARAM_Q;
+      buckets[value]++;
+    }
+  }
+
+  int64_t sum = 0, square_sum = 0;
+  for (int64_t i = 0; i < 1 + 2 * PARAM_K; i++) {
+    sum += (i - PARAM_K) * (int64_t) buckets[i];
+    square_sum += (i - PARAM_K) * (i - PARAM_K) * (int64_t) buckets[i];
+  }
+  double mean = double(sum) / double(PARAM_N * kNumTests);
+
+  double expected_variance = 0.5 * 0.5 * double(PARAM_K * 2);
+  double variance = double(square_sum) / double(PARAM_N * kNumTests) - mean * mean;
+
+  for (size_t i = 0; i < 1 + 2 * PARAM_K; i++) {
+    std::string dots;
+    for (size_t j = 0; j < 79 * buckets[i] / buckets[PARAM_K]; j++) {
+      dots += "+";
+    }
+    printf("%+zd\t%zd\t%s\n", i - PARAM_K, buckets[i], dots.c_str());
+  }
+  printf("mean: got %f, want %f\n", mean, 0.0);
+  printf("variance: got %f, want %f\n", variance, expected_variance);
+  printf("\n");
+
+  if (mean < -0.5 || 0.5 < mean) {
+    fprintf(stderr, "mean out of range: %f\n", mean);
+    return false;
+  }
+
+  if (variance < expected_variance - 1.0 || expected_variance + 1.0 < variance) {
+    fprintf(stderr, "variance out of range: got %f, want %f\n", variance,
+            expected_variance);
+    return false;
+  }
+  return true;
+}
+
+static int Hamming32(const uint8_t key[NEWHOPE_KEY_LENGTH]) {
+  static int kHamming[256] = {
+    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
+    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8,
+  };
+
+  int r = 0;
+  for(int i = 0; i < NEWHOPE_KEY_LENGTH; i++) {
+    r += kHamming[key[i]];
+  }
+  return r;
+}
+
+static bool TestKeys(void) {
+  printf("keys (prior to whitening):\n");
+
+  uint8_t key[NEWHOPE_KEY_LENGTH];
+  uint8_t offermsg[NEWHOPE_OFFERMSG_LENGTH];
+
+  ScopedNEWHOPE_POLY sk(NEWHOPE_POLY_new()), pk(NEWHOPE_POLY_new()),
+      sp(NEWHOPE_POLY_new()), ep(NEWHOPE_POLY_new()), epp(NEWHOPE_POLY_new()),
+      a(NEWHOPE_POLY_new()), bp(NEWHOPE_POLY_new()), rec(NEWHOPE_POLY_new());
+
+  int ones = 0;
+  for (size_t i = 0; i < kNumTests; i++) {
+    NEWHOPE_offer(offermsg, sk.get());
+    NEWHOPE_offer_frommsg(pk.get(), a.get(), offermsg);
+
+    NEWHOPE_POLY_noise_ntt(sp.get());
+    NEWHOPE_POLY_noise_ntt(ep.get());
+    NEWHOPE_POLY_noise(epp.get());  /* intentionally not NTT */
+
+    uint8_t rand[32];
+    RAND_bytes(rand, 32);
+
+    NEWHOPE_accept_computation(key, bp.get(), rec.get(),
+                               sp.get(), ep.get(), epp.get(), rand,
+                               pk.get(), a.get());
+    ones += Hamming32(key);
+  }
+
+  int bits = NEWHOPE_KEY_LENGTH * 8 * kNumTests;
+  int diff = bits - 2 * ones;
+  double fraction = (double) abs(diff) / bits;
+  printf("ones:   %d\n", ones);
+  printf("zeroes: %d\n", (bits - ones));
+  printf("diff:   got %d (%f), want 0\n", diff, fraction);
+  printf("\n");
+
+  if (fraction > 0.01) {
+    fprintf(stderr, "key bias is too high (%f)\n", fraction);
+    return false;
+  }
+
+  return true;
+}
+
+int main(void) {
+  if (!TestKeys() ||
+      !TestNoise()) {
+    return 1;
+  }
+  printf("PASS\n");
+  return 0;
+}
diff --git a/src/crypto/newhope/newhope_test.txt b/src/crypto/newhope/newhope_tests.txt
similarity index 100%
rename from src/crypto/newhope/newhope_test.txt
rename to src/crypto/newhope/newhope_tests.txt
diff --git a/src/crypto/newhope/poly.c b/src/crypto/newhope/poly.c
index a84bdeb..44cd383 100644
--- a/src/crypto/newhope/poly.c
+++ b/src/crypto/newhope/poly.c
@@ -126,7 +126,7 @@
   }
 }
 
-void newhope_poly_getnoise(NEWHOPE_POLY* r) {
+void NEWHOPE_POLY_noise(NEWHOPE_POLY* r) {
 #if PARAM_K != 16
 #error "poly_getnoise in poly.c only supports k=16"
 #endif
@@ -171,7 +171,13 @@
   }
 }
 
-void newhope_poly_ntt(NEWHOPE_POLY* r) {
+void NEWHOPE_POLY_noise_ntt(NEWHOPE_POLY* r) {
+  NEWHOPE_POLY_noise(r);
+  /* Forward NTT transformation.  Because we're operating on a noise polynomial,
+   * we can regard the bits as already reversed and skip the bit-reversal
+   * step:
+   *
+   * newhope_bitrev_vector(r->coeffs); */
   newhope_mul_coefficients(r->coeffs, newhope_psis_bitrev_montgomery);
   newhope_ntt((uint16_t *) r->coeffs, newhope_omegas_montgomery);
 }
diff --git a/src/crypto/poly1305/poly1305_test.txt b/src/crypto/poly1305/poly1305_tests.txt
similarity index 100%
rename from src/crypto/poly1305/poly1305_test.txt
rename to src/crypto/poly1305/poly1305_tests.txt
diff --git a/src/crypto/rand/windows.c b/src/crypto/rand/windows.c
index 9d7dd78..5a9a96b 100644
--- a/src/crypto/rand/windows.c
+++ b/src/crypto/rand/windows.c
@@ -19,7 +19,7 @@
 #include <limits.h>
 #include <stdlib.h>
 
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 
 #include <windows.h>
 
@@ -30,7 +30,7 @@
 #include <ntsecapi.h>
 #undef SystemFunction036
 
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #include "internal.h"
 
diff --git a/src/crypto/test/file_test.h b/src/crypto/test/file_test.h
index e90cc86..a859127 100644
--- a/src/crypto/test/file_test.h
+++ b/src/crypto/test/file_test.h
@@ -20,19 +20,15 @@
 #include <stdint.h>
 #include <stdio.h>
 
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable: 4702)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(push))
+OPENSSL_MSVC_PRAGMA(warning(disable: 4702))
 
 #include <string>
 #include <map>
 #include <set>
 #include <vector>
 
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 // File-based test framework.
 //
diff --git a/src/crypto/thread.c b/src/crypto/thread.c
index 8837115..373f8db 100644
--- a/src/crypto/thread.c
+++ b/src/crypto/thread.c
@@ -61,9 +61,9 @@
 #if !defined(OPENSSL_WINDOWS)
 #include <errno.h>
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/mem.h>
@@ -74,6 +74,11 @@
 void CRYPTO_set_locking_callback(void (*func)(int mode, int lock_num,
                                               const char *file, int line)) {}
 
+void (*CRYPTO_get_locking_callback(void))(int mode, int lock_num,
+                                          const char *file, int line) {
+  return NULL;
+}
+
 void CRYPTO_set_add_lock_callback(int (*func)(int *num, int mount, int lock_num,
                                               const char *file, int line)) {}
 
@@ -99,3 +104,19 @@
 
 void CRYPTO_set_dynlock_destroy_callback(void (*dyn_destroy_function)(
     struct CRYPTO_dynlock_value *l, const char *file, int line)) {}
+
+struct CRYPTO_dynlock_value *(*CRYPTO_get_dynlock_create_callback(void))(
+    const char *file, int line) {
+  return NULL;
+}
+
+void (*CRYPTO_get_dynlock_lock_callback(void))(int mode,
+                                               struct CRYPTO_dynlock_value *l,
+                                               const char *file, int line) {
+  return NULL;
+}
+
+void (*CRYPTO_get_dynlock_destroy_callback(void))(
+    struct CRYPTO_dynlock_value *l, const char *file, int line) {
+  return NULL;
+}
diff --git a/src/crypto/thread_test.c b/src/crypto/thread_test.c
index 92a5373..12ca2ec 100644
--- a/src/crypto/thread_test.c
+++ b/src/crypto/thread_test.c
@@ -21,9 +21,9 @@
 
 #if defined(OPENSSL_WINDOWS)
 
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 typedef HANDLE thread_t;
 
diff --git a/src/crypto/thread_win.c b/src/crypto/thread_win.c
index 9f7d82b..c7a90f7 100644
--- a/src/crypto/thread_win.c
+++ b/src/crypto/thread_win.c
@@ -16,9 +16,9 @@
 
 #if defined(OPENSSL_WINDOWS) && !defined(OPENSSL_NO_THREADS)
 
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/crypto/x509/x509_txt.c b/src/crypto/x509/x509_txt.c
index 86af3fe..17e6cdb 100644
--- a/src/crypto/x509/x509_txt.c
+++ b/src/crypto/x509/x509_txt.c
@@ -199,6 +199,11 @@
     case X509_V_ERR_IP_ADDRESS_MISMATCH:
         return ("IP address mismatch");
 
+    case X509_V_ERR_INVALID_CALL:
+        return ("Invalid certificate verification context");
+    case X509_V_ERR_STORE_LOOKUP:
+        return ("Issuer certificate lookup error");
+
     default:
         BIO_snprintf(buf, sizeof buf, "error number %ld", n);
         return (buf);
diff --git a/src/crypto/x509/x509_vfy.c b/src/crypto/x509/x509_vfy.c
index 2ed2f03..520408f 100644
--- a/src/crypto/x509/x509_vfy.c
+++ b/src/crypto/x509/x509_vfy.c
@@ -198,6 +198,7 @@
     STACK_OF(X509) *sktmp = NULL;
     if (ctx->cert == NULL) {
         OPENSSL_PUT_ERROR(X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY);
+        ctx->error = X509_V_ERR_INVALID_CALL;
         return -1;
     }
     if (ctx->chain != NULL) {
@@ -206,6 +207,7 @@
          * cannot do another one.
          */
         OPENSSL_PUT_ERROR(X509, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+        ctx->error = X509_V_ERR_INVALID_CALL;
         return -1;
     }
 
@@ -218,6 +220,7 @@
     ctx->chain = sk_X509_new_null();
     if (ctx->chain == NULL || !sk_X509_push(ctx->chain, ctx->cert)) {
         OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+        ctx->error = X509_V_ERR_OUT_OF_MEM;
         goto end;
     }
     X509_up_ref(ctx->cert);
@@ -227,6 +230,7 @@
     if (ctx->untrusted != NULL
         && (sktmp = sk_X509_dup(ctx->untrusted)) == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+        ctx->error = X509_V_ERR_OUT_OF_MEM;
         goto end;
     }
 
@@ -250,8 +254,10 @@
          */
         if (ctx->param->flags & X509_V_FLAG_TRUSTED_FIRST) {
             ok = ctx->get_issuer(&xtmp, ctx, x);
-            if (ok < 0)
+            if (ok < 0) {
+                ctx->error = X509_V_ERR_STORE_LOOKUP;
                 goto end;
+            }
             /*
              * If successful for now free up cert so it will be picked up
              * again later.
@@ -268,6 +274,7 @@
             if (xtmp != NULL) {
                 if (!sk_X509_push(ctx->chain, xtmp)) {
                     OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+                    ctx->error = X509_V_ERR_OUT_OF_MEM;
                     ok = 0;
                     goto end;
                 }
@@ -349,14 +356,17 @@
                 break;
             ok = ctx->get_issuer(&xtmp, ctx, x);
 
-            if (ok < 0)
+            if (ok < 0) {
+                ctx->error = X509_V_ERR_STORE_LOOKUP;
                 goto end;
+            }
             if (ok == 0)
                 break;
             x = xtmp;
             if (!sk_X509_push(ctx->chain, x)) {
                 X509_free(xtmp);
                 OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+                ctx->error = X509_V_ERR_OUT_OF_MEM;
                 ok = 0;
                 goto end;
             }
@@ -493,6 +503,10 @@
         sk_X509_free(sktmp);
     if (chain_ss != NULL)
         X509_free(chain_ss);
+
+    /* Safety net, error returns must set ctx->error */
+    if (ok <= 0 && ctx->error == X509_V_OK)
+        ctx->error = X509_V_ERR_UNSPECIFIED;
     return ok;
 }
 
@@ -709,12 +723,19 @@
             NAME_CONSTRAINTS *nc = sk_X509_value(ctx->chain, j)->nc;
             if (nc) {
                 rv = NAME_CONSTRAINTS_check(x, nc);
-                if (rv != X509_V_OK) {
+                switch (rv) {
+                case X509_V_OK:
+                    continue;
+                case X509_V_ERR_OUT_OF_MEM:
+                    ctx->error = rv;
+                    return 0;
+                default:
                     ctx->error = rv;
                     ctx->error_depth = i;
                     ctx->current_cert = x;
                     if (!ctx->verify_cb(0, ctx))
                         return 0;
+                    break;
                 }
             }
         }
@@ -1605,6 +1626,7 @@
                             ctx->param->policies, ctx->param->flags);
     if (ret == 0) {
         OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+        ctx->error = X509_V_ERR_OUT_OF_MEM;
         return 0;
     }
     /* Invalid or inconsistent extensions */
@@ -1633,7 +1655,12 @@
 
     if (ctx->param->flags & X509_V_FLAG_NOTIFY_POLICY) {
         ctx->current_cert = NULL;
-        ctx->error = X509_V_OK;
+        /*
+         * Verification errors need to be "sticky", a callback may have allowed
+         * an SSL handshake to continue despite an error, and we must then
+         * remain in an error state.  Therefore, we MUST NOT clear earlier
+         * verification errors by setting the error to X509_V_OK.
+         */
         if (!ctx->verify_cb(2, ctx))
             return 0;
     }
diff --git a/src/decrepit/cast/cast.c b/src/decrepit/cast/cast.c
index 94b0710..75ea656 100644
--- a/src/decrepit/cast/cast.c
+++ b/src/decrepit/cast/cast.c
@@ -57,9 +57,9 @@
 #include <openssl/cast.h>
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <intrin.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include "internal.h"
diff --git a/src/include/openssl/aead.h b/src/include/openssl/aead.h
index d9a640c..7895825 100644
--- a/src/include/openssl/aead.h
+++ b/src/include/openssl/aead.h
@@ -226,7 +226,7 @@
  * insufficient, zero will be returned. (In this case, |*out_len| is set to
  * zero.)
  *
- * If |in| and |out| alias then |out| must be <= |in|. */
+ * If |in| and |out| alias then |out| must be == |in|. */
 OPENSSL_EXPORT int EVP_AEAD_CTX_seal(const EVP_AEAD_CTX *ctx, uint8_t *out,
                                      size_t *out_len, size_t max_out_len,
                                      const uint8_t *nonce, size_t nonce_len,
@@ -251,7 +251,7 @@
  * insufficient, zero will be returned. (In this case, |*out_len| is set to
  * zero.)
  *
- * If |in| and |out| alias then |out| must be <= |in|. */
+ * If |in| and |out| alias then |out| must be == |in|. */
 OPENSSL_EXPORT int EVP_AEAD_CTX_open(const EVP_AEAD_CTX *ctx, uint8_t *out,
                                      size_t *out_len, size_t max_out_len,
                                      const uint8_t *nonce, size_t nonce_len,
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index d4a4eac..7a3adfb 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -161,6 +161,13 @@
 #define OPENSSL_PRINTF_FORMAT_FUNC(string_index, first_to_check)
 #endif
 
+/* OPENSSL_MSVC_PRAGMA emits a pragma on MSVC and nothing on other compilers. */
+#if defined(_MSC_VER)
+#define OPENSSL_MSVC_PRAGMA(arg) __pragma(arg)
+#else
+#define OPENSSL_MSVC_PRAGMA(arg)
+#endif
+
 
 /* CRYPTO_THREADID is a dummy value. */
 typedef int CRYPTO_THREADID;
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 3e547ac..5416c65 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -562,6 +562,10 @@
  * will connect to. It returns one on success and zero otherwise. */
 OPENSSL_EXPORT int BIO_set_conn_port(BIO *bio, const char *port_str);
 
+/* BIO_set_conn_int_port sets |*port| as the port that |bio| will connect to.
+ * It returns one on success and zero otherwise. */
+OPENSSL_EXPORT int BIO_set_conn_int_port(BIO *bio, const int *port);
+
 /* BIO_set_nbio sets whether |bio| will use non-blocking I/O operations. It
  * returns one on success and zero otherwise. */
 OPENSSL_EXPORT int BIO_set_nbio(BIO *bio, int on);
@@ -583,8 +587,12 @@
 #define BIO_CTRL_DGRAM_MTU_EXCEEDED 43 /* check whether the MTU was exceed in
                                           the previous write operation. */
 
-#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT \
-  45 /* Next DTLS handshake timeout to adjust socket timeouts */
+/* BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT sets a read deadline to drive
+ * retransmits. The |parg| argument to |BIO_ctrl| will be a pointer to a
+ * |timeval| struct. If the structure is all zeros, it clears the read
+ * deadline. Otherwise, |BIO_read| must fail with a temporary error
+ * (e.g. |EAGAIN|) after the deadline. */
+#define BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT 45
 
 #define BIO_CTRL_DGRAM_GET_PEER           46
 
@@ -710,6 +718,8 @@
 #define BIO_CTRL_INFO		3  /* opt - extra tit-bits */
 #define BIO_CTRL_SET		4  /* man - set the 'IO' type */
 #define BIO_CTRL_GET		5  /* man - get the 'IO' type */
+#define BIO_CTRL_PUSH	6
+#define BIO_CTRL_POP	7
 #define BIO_CTRL_GET_CLOSE	8  /* man - set the 'close' on free */
 #define BIO_CTRL_SET_CLOSE	9  /* man - set the 'close' on free */
 #define BIO_CTRL_PENDING	10  /* opt - is their more data buffered */
@@ -720,10 +730,8 @@
 #define BIO_CTRL_GET_CALLBACK	15  /* opt - set callback function */
 #define BIO_CTRL_SET_FILENAME	30	/* BIO_s_file special */
 
-/* These are never used, but exist to allow code to compile more easily. */
-#define BIO_CTRL_DUP	100
-#define BIO_CTRL_PUSH	101
-#define BIO_CTRL_POP	102
+/* BIO_CTRL_DUP is never used, but exists to allow code to compile more easily. */
+#define BIO_CTRL_DUP	12
 
 
 /* Android compatibility section.
diff --git a/src/include/openssl/bytestring.h b/src/include/openssl/bytestring.h
index 68ede2d..3a8d4e5 100644
--- a/src/include/openssl/bytestring.h
+++ b/src/include/openssl/bytestring.h
@@ -95,6 +95,10 @@
  * and advances |cbs|. It returns one on success and zero on error. */
 OPENSSL_EXPORT int CBS_get_u32(CBS *cbs, uint32_t *out);
 
+/* CBS_get_last_u8 sets |*out| to the last uint8_t from |cbs| and shortens
+ * |cbs|. It returns one on success and zero on error. */
+OPENSSL_EXPORT int CBS_get_last_u8(CBS *cbs, uint8_t *out);
+
 /* CBS_get_bytes sets |*out| to the next |len| bytes from |cbs| and advances
  * |cbs|. It returns one on success and zero on error. */
 OPENSSL_EXPORT int CBS_get_bytes(CBS *cbs, CBS *out, size_t len);
diff --git a/src/include/openssl/chacha.h b/src/include/openssl/chacha.h
index 64713c2..3d035e6 100644
--- a/src/include/openssl/chacha.h
+++ b/src/include/openssl/chacha.h
@@ -23,8 +23,8 @@
 
 
 /* CRYPTO_chacha_20 encrypts |in_len| bytes from |in| with the given key and
- * nonce and writes the result to |out|, which may be equal to |in|. The
- * initial block counter is specified by |counter|. */
+ * nonce and writes the result to |out|. If |in| and |out| alias, they must be
+ * equal. The initial block counter is specified by |counter|. */
 OPENSSL_EXPORT void CRYPTO_chacha_20(uint8_t *out, const uint8_t *in,
                                      size_t in_len, const uint8_t key[32],
                                      const uint8_t nonce[12], uint32_t counter);
diff --git a/src/include/openssl/evp.h b/src/include/openssl/evp.h
index 398c741..faf160f 100644
--- a/src/include/openssl/evp.h
+++ b/src/include/openssl/evp.h
@@ -654,6 +654,9 @@
 /* OpenSSL_add_all_algorithms does nothing. */
 OPENSSL_EXPORT void OpenSSL_add_all_algorithms(void);
 
+/* OpenSSL_add_all_algorithms_conf does nothing. */
+OPENSSL_EXPORT void OpenSSL_add_all_algorithms_conf(void);
+
 /* OpenSSL_add_all_ciphers does nothing. */
 OPENSSL_EXPORT void OpenSSL_add_all_ciphers(void);
 
diff --git a/src/include/openssl/newhope.h b/src/include/openssl/newhope.h
index 31d559f..487e03f 100644
--- a/src/include/openssl/newhope.h
+++ b/src/include/openssl/newhope.h
@@ -89,6 +89,14 @@
 
 /* Lower-level functions. */
 
+/* NEWHOPE_POLY_noise sets |r| to a random polynomial where the coefficients are
+ * sampled from the noise distribution. */
+OPENSSL_EXPORT void NEWHOPE_POLY_noise(NEWHOPE_POLY* r);
+
+/* NEWHOPE_POLY_noise_ntt sets |r| to an output of NEWHOPE_POLY_noise, and then
+ * applies NTT(r) in-place. */
+OPENSSL_EXPORT void NEWHOPE_POLY_noise_ntt(NEWHOPE_POLY* r);
+
 /* NEWHOPE_offer_computation is the work of |NEWHOPE_offer|, less the encoding
  * parts.  The inputs are the noise polynomials |s| and |e|, and random
  * polynomial |a|. The output is the polynomial |pk|. */
@@ -125,6 +133,12 @@
 OPENSSL_EXPORT void NEWHOPE_POLY_tobytes(uint8_t r[NEWHOPE_POLY_LENGTH],
                                          const NEWHOPE_POLY* p);
 
+/* NEWHOPE_offer_frommsg decodes an offer message |msg| into its constituent
+ * polynomials |out_pk| and |a|. */
+OPENSSL_EXPORT void NEWHOPE_offer_frommsg(
+    NEWHOPE_POLY *out_pk, NEWHOPE_POLY *out_a,
+    const uint8_t msg[NEWHOPE_OFFERMSG_LENGTH]);
+
 
 #if defined(__cplusplus)
 } /* extern "C" */
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 3770c6e..03a4ea5 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1095,6 +1095,9 @@
 /* SSL_CIPHER_is_ECDSA returns one if |cipher| uses ECDSA. */
 OPENSSL_EXPORT int SSL_CIPHER_is_ECDSA(const SSL_CIPHER *cipher);
 
+/* SSL_CIPHER_is_DHE returns one if |cipher| uses DHE. */
+OPENSSL_EXPORT int SSL_CIPHER_is_DHE(const SSL_CIPHER *cipher);
+
 /* SSL_CIPHER_is_ECDHE returns one if |cipher| uses ECDHE. */
 OPENSSL_EXPORT int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher);
 
@@ -2725,6 +2728,13 @@
 OPENSSL_EXPORT void SSL_CTX_set_keylog_callback(
     SSL_CTX *ctx, void (*cb)(const SSL *ssl, const char *line));
 
+/* SSL_CTX_set_current_time_cb configures a callback to retrieve the current
+ * time, which should be set in |*out_clock|. This can be used for testing
+ * purposes; for example, a callback can be configured that returns a time
+ * set explicitly by the test. */
+OPENSSL_EXPORT void SSL_CTX_set_current_time_cb(
+    SSL_CTX *ctx, void (*cb)(const SSL *ssl, struct timeval *out_clock));
+
 enum ssl_renegotiate_mode_t {
   ssl_renegotiate_never = 0,
   ssl_renegotiate_once,
@@ -3822,7 +3832,8 @@
   void (*keylog_callback)(const SSL *ssl, const char *line);
 
   /* current_time_cb, if not NULL, is the function to use to get the current
-   * time. It sets |*out_clock| to the current time. */
+   * time. It sets |*out_clock| to the current time. See
+   * |SSL_CTX_set_current_time_cb|. */
   void (*current_time_cb)(const SSL *ssl, struct timeval *out_clock);
 
   /* quiet_shutdown is true if the connection should not send a close_notify on
@@ -4173,13 +4184,10 @@
       uint16_t received;
     } custom_extensions;
 
-    /* SNI extension */
-
     /* should_ack_sni is used by a server and indicates that the SNI extension
      * should be echoed in the ServerHello. */
     unsigned should_ack_sni:1;
 
-
     /* Client-only: cert_req determines if a client certificate is to be sent.
      * This is 0 if no client Certificate message is to be sent, 1 if there is
      * a client certificate, and 2 to send an empty client Certificate
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index 64e1d31..1da5de7 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -399,7 +399,7 @@
 #define SSL3_MT_CERTIFICATE 11
 #define SSL3_MT_SERVER_KEY_EXCHANGE 12
 #define SSL3_MT_CERTIFICATE_REQUEST 13
-#define SSL3_MT_SERVER_DONE 14
+#define SSL3_MT_SERVER_HELLO_DONE 14
 #define SSL3_MT_CERTIFICATE_VERIFY 15
 #define SSL3_MT_CLIENT_KEY_EXCHANGE 16
 #define SSL3_MT_FINISHED 20
@@ -409,8 +409,9 @@
 #define SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS 203
 #define DTLS1_MT_HELLO_VERIFY_REQUEST 3
 
-/* SSL3_MT_NEWSESSION_TICKET is a legacy alias for |SSL3_MT_NEW_SESSION_TICKET|
- * for consumers which use |SSL_CTX_set_msg_callback|. */
+/* The following are legacy aliases for consumers which use
+ * |SSL_CTX_set_msg_callback|. */
+#define SSL3_MT_SERVER_DONE SSL3_MT_SERVER_HELLO_DONE
 #define SSL3_MT_NEWSESSION_TICKET SSL3_MT_NEW_SESSION_TICKET
 
 
diff --git a/src/include/openssl/thread.h b/src/include/openssl/thread.h
index 02539e8..9a96fb4 100644
--- a/src/include/openssl/thread.h
+++ b/src/include/openssl/thread.h
@@ -101,7 +101,11 @@
 typedef uint32_t CRYPTO_refcount_t;
 
 
-/* Deprecated functions */
+/* Deprecated functions.
+ *
+ * Historically, OpenSSL required callers to provide locking callbacks.
+ * BoringSSL is thread-safe by default, but some old code calls these functions
+ * and so no-op implementations are provided. */
 
 /* These defines do nothing but are provided to make old code easier to
  * compile. */
@@ -123,6 +127,11 @@
 OPENSSL_EXPORT void CRYPTO_set_add_lock_callback(int (*func)(
     int *num, int amount, int lock_num, const char *file, int line));
 
+/* CRYPTO_get_locking_callback returns NULL. */
+OPENSSL_EXPORT void (*CRYPTO_get_locking_callback(void))(int mode, int lock_num,
+                                                         const char *file,
+                                                         int line);
+
 /* CRYPTO_get_lock_name returns a fixed, dummy string. */
 OPENSSL_EXPORT const char *CRYPTO_get_lock_name(int lock_num);
 
@@ -140,14 +149,6 @@
 /* CRYPTO_THREADID_current does nothing. */
 OPENSSL_EXPORT void CRYPTO_THREADID_current(CRYPTO_THREADID *id);
 
-
-/* Private functions.
- *
- * Some old code calls these functions and so no-op implementations are
- * provided.
- *
- * TODO(fork): cleanup callers and remove. */
-
 OPENSSL_EXPORT void CRYPTO_set_id_callback(unsigned long (*func)(void));
 
 typedef struct {
@@ -166,6 +167,15 @@
     void (*dyn_destroy_function)(struct CRYPTO_dynlock_value *l,
                                  const char *file, int line));
 
+OPENSSL_EXPORT struct CRYPTO_dynlock_value *(
+    *CRYPTO_get_dynlock_create_callback(void))(const char *file, int line);
+
+OPENSSL_EXPORT void (*CRYPTO_get_dynlock_lock_callback(void))(
+    int mode, struct CRYPTO_dynlock_value *l, const char *file, int line);
+
+OPENSSL_EXPORT void (*CRYPTO_get_dynlock_destroy_callback(void))(
+    struct CRYPTO_dynlock_value *l, const char *file, int line);
+
 
 #if defined(__cplusplus)
 }  /* extern C */
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index 6ed9fa9..d195940 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -436,6 +436,10 @@
 #define TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0x0300CCA9
 #define TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0x0300CCAC
 
+/* PSK ciphersuites from mattsson-tls-ecdhe-psk-aead */
+#define TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256 0x0300D001
+#define TLS1_CK_ECDHE_PSK_WITH_AES_256_GCM_SHA384 0x0300D002
+
 /* TODO(davidben): Remove this. Historically, the CK names for CHACHA20_POLY1305
  * were missing 'WITH' and 'SHA256'. */
 #define TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305 \
@@ -627,6 +631,10 @@
 #define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 \
   TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 
+/* PSK ciphersuites from mattsson-tls-ecdhe-psk-aead */
+#define TLS1_TXT_ECDHE_PSK_WITH_AES_128_GCM_SHA256 "ECDHE-PSK-AES128-GCM-SHA256"
+#define TLS1_TXT_ECDHE_PSK_WITH_AES_256_GCM_SHA384 "ECDHE-PSK-AES256-GCM-SHA384"
+
 /* CECPQ1 ciphersuites.  These are specific to BoringSSL and not standard. */
 #define TLS1_TXT_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256 \
   "CECPQ1-RSA-CHACHA20-POLY1305-SHA256"
diff --git a/src/include/openssl/x509_vfy.h b/src/include/openssl/x509_vfy.h
index a9d0519..16f03d6 100644
--- a/src/include/openssl/x509_vfy.h
+++ b/src/include/openssl/x509_vfy.h
@@ -292,7 +292,7 @@
 		X509_LOOKUP_ctrl((x),X509_L_ADD_DIR,(name),(long)(type),NULL)
 
 #define		X509_V_OK					0
-/* illegal error (for uninitialized values, to avoid X509_V_OK): 1 */
+#define		X509_V_ERR_UNSPECIFIED				1
 
 #define		X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT		2
 #define		X509_V_ERR_UNABLE_TO_GET_CRL			3
@@ -347,6 +347,7 @@
 #define		X509_V_ERR_PERMITTED_VIOLATION			47
 #define		X509_V_ERR_EXCLUDED_VIOLATION			48
 #define		X509_V_ERR_SUBTREE_MINMAX			49
+#define		X509_V_ERR_APPLICATION_VERIFICATION		50
 #define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_TYPE		51
 #define		X509_V_ERR_UNSUPPORTED_CONSTRAINT_SYNTAX	52
 #define		X509_V_ERR_UNSUPPORTED_NAME_SYNTAX		53
@@ -365,8 +366,10 @@
 #define		X509_V_ERR_EMAIL_MISMATCH			63
 #define		X509_V_ERR_IP_ADDRESS_MISMATCH			64
 
-/* The application is not happy */
-#define		X509_V_ERR_APPLICATION_VERIFICATION		50
+/* Caller error */
+#define		X509_V_ERR_INVALID_CALL				65
+/* Issuer lookup error */
+#define		X509_V_ERR_STORE_LOOKUP				66
 
 /* Certificate verify flags */
 
@@ -614,4 +617,3 @@
 }
 #endif
 #endif
-
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index c82cb9b..e17e1f2 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -6,21 +6,19 @@
   ssl
 
   custom_extensions.c
+  handshake_server.c
+  handshake_client.c
   d1_both.c
-  d1_clnt.c
   d1_lib.c
   d1_meth.c
   d1_pkt.c
   d1_srtp.c
-  d1_srvr.c
   dtls_record.c
   s3_both.c
-  s3_clnt.c
   s3_enc.c
   s3_lib.c
   s3_meth.c
   s3_pkt.c
-  s3_srvr.c
   ssl_aead_ctx.c
   ssl_asn1.c
   ssl_buffer.c
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index 8d8784b..78f566e 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -299,18 +299,15 @@
 
   static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
   int ret =
-      dtls1_write_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                        sizeof(kChangeCipherSpec), use_epoch);
+      dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                         sizeof(kChangeCipherSpec), use_epoch);
   if (ret <= 0) {
     return ret;
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC,
-                      kChangeCipherSpec, sizeof(kChangeCipherSpec), ssl,
-                      ssl->msg_callback_arg);
-  }
-
+  ssl_do_msg_callback(ssl, 1 /* write */, ssl->version,
+                      SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                      sizeof(kChangeCipherSpec));
   return 1;
 }
 
@@ -378,8 +375,8 @@
       goto err;
     }
 
-    int write_ret = dtls1_write_bytes(ssl, SSL3_RT_HANDSHAKE, buf, len,
-                                      use_epoch);
+    int write_ret =
+        dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, len, use_epoch);
     if (write_ret <= 0) {
       ret = write_ret;
       goto err;
@@ -388,11 +385,9 @@
     ssl->init_num -= todo;
   } while (ssl->init_num > 0);
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(
-        1 /* write */, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
-        (size_t)(ssl->init_off + ssl->init_num), ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, SSL3_RT_HANDSHAKE,
+                      ssl->init_buf->data,
+                      (size_t)(ssl->init_off + ssl->init_num));
 
   ssl->init_off = 0;
   ssl->init_num = 0;
@@ -420,24 +415,6 @@
          frag->reassembly == NULL;
 }
 
-/* dtls1_discard_fragment_body discards a handshake fragment body of length
- * |frag_len|. It returns one on success and zero on error.
- *
- * TODO(davidben): This function will go away when ssl_read_bytes is gone from
- * the DTLS side. */
-static int dtls1_discard_fragment_body(SSL *ssl, size_t frag_len) {
-  uint8_t discard[256];
-  while (frag_len > 0) {
-    size_t chunk = frag_len < sizeof(discard) ? frag_len : sizeof(discard);
-    int ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, discard, chunk, 0);
-    if (ret != (int) chunk) {
-      return 0;
-    }
-    frag_len -= chunk;
-  }
-  return 1;
-}
-
 /* dtls1_get_buffered_message returns the buffered message corresponding to
  * |msg_hdr|. If none exists, it creates a new one and inserts it in the
  * queue. Otherwise, it checks |msg_hdr| is consistent with the existing one. It
@@ -483,75 +460,92 @@
   return frag;
 }
 
-/* dtls1_process_fragment reads a handshake fragment and processes it. It
- * returns one if a fragment was successfully processed and 0 or -1 on error. */
-static int dtls1_process_fragment(SSL *ssl) {
-  /* Read handshake message header. */
-  uint8_t header[DTLS1_HM_HEADER_LENGTH];
-  int ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, header,
-                             DTLS1_HM_HEADER_LENGTH, 0);
-  if (ret <= 0) {
-    return ret;
+/* dtls1_process_handshake_record reads a handshake record and processes it. It
+ * returns one if the record was successfully processed and 0 or -1 on error. */
+static int dtls1_process_handshake_record(SSL *ssl) {
+  SSL3_RECORD *rr = &ssl->s3->rrec;
+
+start:
+  if (rr->length == 0) {
+    int ret = dtls1_get_record(ssl);
+    if (ret <= 0) {
+      return ret;
+    }
   }
-  if (ret != DTLS1_HM_HEADER_LENGTH) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+
+  /* Cross-epoch records are discarded, but we may receive out-of-order
+   * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec
+   * before the appropriate point in the handshake. Those must be silently
+   * discarded.
+   *
+   * However, only allow the out-of-order records in the correct epoch.
+   * Application data must come in the encrypted epoch, and ChangeCipherSpec in
+   * the unencrypted epoch (we never renegotiate). Other cases fall through and
+   * fail with a fatal error. */
+  if ((rr->type == SSL3_RT_APPLICATION_DATA &&
+       ssl->s3->aead_read_ctx != NULL) ||
+      (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC &&
+       ssl->s3->aead_read_ctx == NULL)) {
+    rr->length = 0;
+    goto start;
+  }
+
+  if (rr->type != SSL3_RT_HANDSHAKE) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
     return -1;
   }
 
-  /* Parse the message fragment header. */
-  struct hm_header_st msg_hdr;
-  dtls1_get_message_header(header, &msg_hdr);
+  CBS cbs;
+  CBS_init(&cbs, rr->data, rr->length);
 
-  /* TODO(davidben): dtls1_read_bytes is the wrong abstraction for DTLS. There
-   * should be no need to reach into |ssl->s3->rrec.length|. */
-  const size_t frag_off = msg_hdr.frag_off;
-  const size_t frag_len = msg_hdr.frag_len;
-  const size_t msg_len = msg_hdr.msg_len;
-  if (frag_off > msg_len || frag_off + frag_len < frag_off ||
-      frag_off + frag_len > msg_len ||
-      msg_len > ssl_max_handshake_message_len(ssl) ||
-      frag_len > ssl->s3->rrec.length) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return -1;
-  }
-
-  if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
-      msg_hdr.seq > (unsigned)ssl->d1->handshake_read_seq +
-                    kHandshakeBufferSize) {
-    /* Ignore fragments from the past, or ones too far in the future. */
-    if (!dtls1_discard_fragment_body(ssl, frag_len)) {
+  while (CBS_len(&cbs) > 0) {
+    /* Read a handshake fragment. */
+    struct hm_header_st msg_hdr;
+    CBS body;
+    if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
       return -1;
     }
-    return 1;
-  }
 
-  hm_fragment *frag = dtls1_get_buffered_message(ssl, &msg_hdr);
-  if (frag == NULL) {
-    return -1;
-  }
-  assert(frag->msg_header.msg_len == msg_len);
-
-  if (frag->reassembly == NULL) {
-    /* The message is already assembled. */
-    if (!dtls1_discard_fragment_body(ssl, frag_len)) {
+    const size_t frag_off = msg_hdr.frag_off;
+    const size_t frag_len = msg_hdr.frag_len;
+    const size_t msg_len = msg_hdr.msg_len;
+    if (frag_off > msg_len || frag_off + frag_len < frag_off ||
+        frag_off + frag_len > msg_len ||
+        msg_len > ssl_max_handshake_message_len(ssl)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
       return -1;
     }
-    return 1;
-  }
-  assert(msg_len > 0);
 
-  /* Read the body of the fragment. */
-  ret = dtls1_read_bytes(ssl, SSL3_RT_HANDSHAKE, frag->fragment + frag_off,
-                         frag_len, 0);
-  if (ret != (int) frag_len) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-    return -1;
-  }
-  dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
+    if (msg_hdr.seq < ssl->d1->handshake_read_seq ||
+        msg_hdr.seq >
+            (unsigned)ssl->d1->handshake_read_seq + kHandshakeBufferSize) {
+      /* Ignore fragments from the past, or ones too far in the future. */
+      continue;
+    }
 
+    hm_fragment *frag = dtls1_get_buffered_message(ssl, &msg_hdr);
+    if (frag == NULL) {
+      return -1;
+    }
+    assert(frag->msg_header.msg_len == msg_len);
+
+    if (frag->reassembly == NULL) {
+      /* The message is already assembled. */
+      continue;
+    }
+    assert(msg_len > 0);
+
+    /* Copy the body into the fragment. */
+    memcpy(frag->fragment + frag_off, CBS_data(&body), CBS_len(&body));
+    dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
+  }
+
+  rr->length = 0;
+  ssl_read_buffer_discard(ssl);
   return 1;
 }
 
@@ -584,9 +578,9 @@
     return ssl->init_num;
   }
 
-  /* Process fragments until one is found. */
+  /* Process handshake records until the next message is ready. */
   while (!dtls1_is_next_message_complete(ssl)) {
-    int ret = dtls1_process_fragment(ssl);
+    int ret = dtls1_process_handshake_record(ssl);
     if (ret <= 0) {
       *ok = 0;
       return ret;
@@ -637,11 +631,10 @@
   if (hash_message == ssl_hash_message && !ssl3_hash_current_message(ssl)) {
     goto err;
   }
-  if (ssl->msg_callback) {
-    ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
-                    ssl->init_num + DTLS1_HM_HEADER_LENGTH, ssl,
-                    ssl->msg_callback_arg);
-  }
+
+  ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE,
+                      ssl->init_buf->data,
+                      ssl->init_num + DTLS1_HM_HEADER_LENGTH);
 
   pitem_free(item);
   dtls1_hm_fragment_free(frag);
@@ -658,27 +651,6 @@
   return -1;
 }
 
-int dtls1_read_failed(SSL *ssl, int code) {
-  if (code > 0) {
-    assert(0);
-    return 1;
-  }
-
-  if (!dtls1_is_timer_expired(ssl)) {
-    /* not a timeout, none of our business, let higher layers handle this. In
-     * fact, it's probably an error */
-    return code;
-  }
-
-  if (!SSL_in_init(ssl)) {
-    /* done, no need to send a retransmit */
-    BIO_set_flags(ssl->rbio, BIO_FLAGS_READ);
-    return code;
-  }
-
-  return DTLSv1_handle_timeout(ssl);
-}
-
 static uint16_t dtls1_get_queue_priority(uint16_t seq, int is_ccs) {
   assert(seq * 2 >= seq);
 
@@ -862,13 +834,18 @@
   return kMinMTU;
 }
 
-void dtls1_get_message_header(uint8_t *data,
-                              struct hm_header_st *msg_hdr) {
-  memset(msg_hdr, 0x00, sizeof(struct hm_header_st));
-  msg_hdr->type = *(data++);
-  n2l3(data, msg_hdr->msg_len);
+int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
+                         CBS *out_body) {
+  memset(out_hdr, 0x00, sizeof(struct hm_header_st));
 
-  n2s(data, msg_hdr->seq);
-  n2l3(data, msg_hdr->frag_off);
-  n2l3(data, msg_hdr->frag_len);
+  if (!CBS_get_u8(cbs, &out_hdr->type) ||
+      !CBS_get_u24(cbs, &out_hdr->msg_len) ||
+      !CBS_get_u16(cbs, &out_hdr->seq) ||
+      !CBS_get_u24(cbs, &out_hdr->frag_off) ||
+      !CBS_get_u24(cbs, &out_hdr->frag_len) ||
+      !CBS_get_bytes(cbs, out_body, out_hdr->frag_len)) {
+    return 0;
+  }
+
+  return 1;
 }
diff --git a/src/ssl/d1_clnt.c b/src/ssl/d1_clnt.c
deleted file mode 100644
index 19ad1f8..0000000
--- a/src/ssl/d1_clnt.c
+++ /dev/null
@@ -1,521 +0,0 @@
-/*
- * DTLS implementation written by Nagendra Modadugu
- * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. 
- */
-/* ====================================================================
- * Copyright (c) 1999-2007 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    openssl-core@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
- *
- */
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
- */
-
-#include <openssl/ssl.h>
-
-#include <assert.h>
-#include <string.h>
-
-#include <openssl/bn.h>
-#include <openssl/buf.h>
-#include <openssl/dh.h>
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include <openssl/md5.h>
-#include <openssl/mem.h>
-#include <openssl/rand.h>
-
-#include "internal.h"
-
-
-static int dtls1_get_hello_verify(SSL *ssl);
-
-int dtls1_connect(SSL *ssl) {
-  BUF_MEM *buf = NULL;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-  int ret = -1;
-  int new_state, state, skip = 0;
-
-  assert(ssl->handshake_func == dtls1_connect);
-  assert(!ssl->server);
-  assert(SSL_IS_DTLS(ssl));
-
-  ERR_clear_system_error();
-
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
-
-  for (;;) {
-    state = ssl->state;
-
-    switch (ssl->state) {
-      case SSL_ST_CONNECT:
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
-        }
-
-        if (ssl->init_buf == NULL) {
-          buf = BUF_MEM_new();
-          if (buf == NULL ||
-              !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
-            ret = -1;
-            goto end;
-          }
-          ssl->init_buf = buf;
-          buf = NULL;
-        }
-        ssl->init_num = 0;
-
-        if (!ssl_init_wbio_buffer(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
-        ssl->d1->send_cookie = 0;
-        ssl->hit = 0;
-        break;
-
-      case SSL3_ST_CW_CLNT_HELLO_A:
-      case SSL3_ST_CW_CLNT_HELLO_B:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_client_hello(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        if (ssl->d1->send_cookie) {
-          ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
-        } else {
-          ssl->s3->tmp.next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
-        }
-        ssl->state = SSL3_ST_CW_FLUSH;
-        break;
-
-      case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
-        ret = dtls1_get_hello_verify(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        if (ssl->d1->send_cookie) {
-          /* start again, with a cookie */
-          dtls1_stop_timer(ssl);
-          ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
-        } else {
-          ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
-        }
-        break;
-
-      case SSL3_ST_CR_SRVR_HELLO_A:
-        ret = ssl3_get_server_hello(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        if (ssl->hit) {
-          ssl->state = SSL3_ST_CR_CHANGE;
-          if (ssl->tlsext_ticket_expected) {
-            /* receive renewed session ticket */
-            ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
-          }
-        } else {
-          ssl->state = SSL3_ST_CR_CERT_A;
-        }
-        break;
-
-      case SSL3_ST_CR_CERT_A:
-        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ret = ssl3_get_server_certificate(ssl);
-          if (ret <= 0) {
-            goto end;
-          }
-          if (ssl->s3->tmp.certificate_status_expected) {
-            ssl->state = SSL3_ST_CR_CERT_STATUS_A;
-          } else {
-            ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
-          }
-        } else {
-          skip = 1;
-          ssl->state = SSL3_ST_CR_KEY_EXCH_A;
-        }
-        break;
-
-      case SSL3_ST_VERIFY_SERVER_CERT:
-        ret = ssl3_verify_server_cert(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_CR_KEY_EXCH_A;
-        break;
-
-      case SSL3_ST_CR_KEY_EXCH_A:
-        ret = ssl3_get_server_key_exchange(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          ssl->state = SSL3_ST_CR_CERT_REQ_A;
-        } else {
-          ssl->state = SSL3_ST_CR_SRVR_DONE_A;
-        }
-        break;
-
-      case SSL3_ST_CR_CERT_REQ_A:
-        ret = ssl3_get_certificate_request(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_CR_SRVR_DONE_A;
-        break;
-
-      case SSL3_ST_CR_SRVR_DONE_A:
-        ret = ssl3_get_server_done(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        dtls1_stop_timer(ssl);
-        if (ssl->s3->tmp.cert_req) {
-          ssl->s3->tmp.next_state = SSL3_ST_CW_CERT_A;
-        } else {
-          ssl->s3->tmp.next_state = SSL3_ST_CW_KEY_EXCH_A;
-        }
-        ssl->state = ssl->s3->tmp.next_state;
-        break;
-
-      case SSL3_ST_CW_CERT_A:
-      case SSL3_ST_CW_CERT_B:
-      case SSL3_ST_CW_CERT_C:
-      case SSL3_ST_CW_CERT_D:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_client_certificate(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_CW_KEY_EXCH_A;
-        break;
-
-      case SSL3_ST_CW_KEY_EXCH_A:
-      case SSL3_ST_CW_KEY_EXCH_B:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_client_key_exchange(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        /* For TLS, cert_req is set to 2, so a cert chain
-         * of nothing is sent, but no verify packet is sent */
-        if (ssl->s3->tmp.cert_req == 1) {
-          ssl->state = SSL3_ST_CW_CERT_VRFY_A;
-        } else {
-          ssl->state = SSL3_ST_CW_CHANGE_A;
-        }
-        break;
-
-      case SSL3_ST_CW_CERT_VRFY_A:
-      case SSL3_ST_CW_CERT_VRFY_B:
-      case SSL3_ST_CW_CERT_VRFY_C:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_cert_verify(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_CW_CHANGE_A;
-        break;
-
-      case SSL3_ST_CW_CHANGE_A:
-      case SSL3_ST_CW_CHANGE_B:
-        if (!ssl->hit) {
-          dtls1_start_timer(ssl);
-        }
-        ret = dtls1_send_change_cipher_spec(ssl, SSL3_ST_CW_CHANGE_A,
-                                            SSL3_ST_CW_CHANGE_B);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_CW_FINISHED_A;
-
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
-          ret = -1;
-          goto end;
-        }
-        break;
-
-      case SSL3_ST_CW_FINISHED_A:
-      case SSL3_ST_CW_FINISHED_B:
-        if (!ssl->hit) {
-          dtls1_start_timer(ssl);
-        }
-
-        ret = ssl3_send_finished(ssl, SSL3_ST_CW_FINISHED_A,
-                                 SSL3_ST_CW_FINISHED_B);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_CW_FLUSH;
-
-        if (ssl->hit) {
-          ssl->s3->tmp.next_state = SSL_ST_OK;
-        } else {
-          /* Allow NewSessionTicket if ticket expected */
-          if (ssl->tlsext_ticket_expected) {
-            ssl->s3->tmp.next_state = SSL3_ST_CR_SESSION_TICKET_A;
-          } else {
-            ssl->s3->tmp.next_state = SSL3_ST_CR_CHANGE;
-          }
-        }
-        break;
-
-      case SSL3_ST_CR_SESSION_TICKET_A:
-        ret = ssl3_get_new_session_ticket(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_CR_CHANGE;
-        break;
-
-      case SSL3_ST_CR_CERT_STATUS_A:
-        ret = ssl3_get_cert_status(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
-        break;
-
-      case SSL3_ST_CR_CHANGE:
-        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_CLIENT_READ)) {
-          ret = -1;
-          goto end;
-        }
-        ssl->state = SSL3_ST_CR_FINISHED_A;
-        break;
-
-      case SSL3_ST_CR_FINISHED_A:
-        ret = ssl3_get_finished(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        dtls1_stop_timer(ssl);
-
-        if (ssl->hit) {
-          ssl->state = SSL3_ST_CW_CHANGE_A;
-        } else {
-          ssl->state = SSL_ST_OK;
-        }
-
-        break;
-
-      case SSL3_ST_CW_FLUSH:
-        if (BIO_flush(ssl->wbio) <= 0) {
-          ssl->rwstate = SSL_WRITING;
-          ret = -1;
-          goto end;
-        }
-        ssl->state = ssl->s3->tmp.next_state;
-        break;
-
-      case SSL_ST_OK:
-        /* clean a few things up */
-        ssl3_cleanup_key_block(ssl);
-
-        /* Remove write buffering now. */
-        ssl_free_wbio_buffer(ssl);
-
-	/* |init_buf| cannot be released because post-handshake retransmit
-         * relies on that buffer being available as scratch space.
-         *
-         * TODO(davidben): Fix this. */
-        ssl->init_num = 0;
-        ssl->s3->initial_handshake_complete = 1;
-
-        ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
-
-        ret = 1;
-
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
-        }
-
-        /* done with handshaking */
-        ssl->d1->handshake_read_seq = 0;
-        ssl->d1->next_handshake_write_seq = 0;
-        goto end;
-
-      default:
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE);
-        ret = -1;
-        goto end;
-    }
-
-    /* did we do anything? */
-    if (!ssl->s3->tmp.reuse_message && !skip) {
-      if ((cb != NULL) && (ssl->state != state)) {
-        new_state = ssl->state;
-        ssl->state = state;
-        cb(ssl, SSL_CB_CONNECT_LOOP, 1);
-        ssl->state = new_state;
-      }
-    }
-    skip = 0;
-  }
-
-end:
-  BUF_MEM_free(buf);
-  if (cb != NULL) {
-    cb(ssl, SSL_CB_CONNECT_EXIT, ret);
-  }
-  return ret;
-}
-
-static int dtls1_get_hello_verify(SSL *ssl) {
-  long n;
-  int al, ok = 0;
-  CBS hello_verify_request, cookie;
-  uint16_t server_version;
-
-  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
-
-  if (!ok) {
-    return n;
-  }
-
-  if (ssl->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
-    ssl->d1->send_cookie = 0;
-    ssl->s3->tmp.reuse_message = 1;
-    return 1;
-  }
-
-  CBS_init(&hello_verify_request, ssl->init_msg, n);
-
-  if (!CBS_get_u16(&hello_verify_request, &server_version) ||
-      !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
-      CBS_len(&hello_verify_request) != 0) {
-    al = SSL_AD_DECODE_ERROR;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
-  }
-
-  if (CBS_len(&cookie) > sizeof(ssl->d1->cookie)) {
-    al = SSL_AD_ILLEGAL_PARAMETER;
-    goto f_err;
-  }
-
-  memcpy(ssl->d1->cookie, CBS_data(&cookie), CBS_len(&cookie));
-  ssl->d1->cookie_len = CBS_len(&cookie);
-
-  ssl->d1->send_cookie = 1;
-  return 1;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  return -1;
-}
diff --git a/src/ssl/d1_lib.c b/src/ssl/d1_lib.c
index c1c9f91..73ae4cf 100644
--- a/src/ssl/d1_lib.c
+++ b/src/ssl/d1_lib.c
@@ -350,3 +350,11 @@
 int dtls1_handshake_write(SSL *ssl) {
   return dtls1_do_handshake_write(ssl, dtls1_use_current_epoch);
 }
+
+void dtls1_expect_flight(SSL *ssl) {
+  dtls1_start_timer(ssl);
+}
+
+void dtls1_received_flight(SSL *ssl) {
+  dtls1_stop_timer(ssl);
+}
diff --git a/src/ssl/d1_meth.c b/src/ssl/d1_meth.c
index 49a2595..32a4651 100644
--- a/src/ssl/d1_meth.c
+++ b/src/ssl/d1_meth.c
@@ -63,8 +63,6 @@
     1 /* is_dtls */,
     dtls1_new,
     dtls1_free,
-    dtls1_accept,
-    dtls1_connect,
     dtls1_get_message,
     dtls1_read_app_data,
     dtls1_read_change_cipher_spec,
@@ -75,6 +73,9 @@
     DTLS1_HM_HEADER_LENGTH,
     dtls1_set_handshake_header,
     dtls1_handshake_write,
+    dtls1_send_change_cipher_spec,
+    dtls1_expect_flight,
+    dtls1_received_flight,
 };
 
 const SSL_METHOD *DTLS_method(void) {
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index 34eeddb..b821ab3 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -114,7 +114,9 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/bio.h>
 #include <openssl/buf.h>
+#include <openssl/bytestring.h>
 #include <openssl/mem.h>
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -123,13 +125,7 @@
 #include "internal.h"
 
 
-static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf,
-                          unsigned int len, enum dtls1_use_epoch_t use_epoch);
-
-/* dtls1_get_record reads a new input record. On success, it places it in
- * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
- * more data is needed. */
-static int dtls1_get_record(SSL *ssl) {
+int dtls1_get_record(SSL *ssl) {
 again:
   switch (ssl->s3->recv_shutdown) {
     case ssl_shutdown_none:
@@ -143,50 +139,57 @@
 
   /* Read a new packet if there is no unconsumed one. */
   if (ssl_read_buffer_len(ssl) == 0) {
-    int ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
-    if (ret <= 0) {
-      return ret;
+    int read_ret = ssl_read_buffer_extend_to(ssl, 0 /* unused */);
+    if (read_ret < 0 && dtls1_is_timer_expired(ssl)) {
+      /* For blocking BIOs, retransmits must be handled internally. */
+      int timeout_ret = DTLSv1_handle_timeout(ssl);
+      if (timeout_ret <= 0) {
+        return timeout_ret;
+      }
+      goto again;
+    }
+    if (read_ret <= 0) {
+      return read_ret;
     }
   }
   assert(ssl_read_buffer_len(ssl) > 0);
 
-  /* Ensure the packet is large enough to decrypt in-place. */
-  if (ssl_read_buffer_len(ssl) < ssl_record_prefix_len(ssl)) {
-    ssl_read_buffer_clear(ssl);
-    goto again;
-  }
-
-  uint8_t *out = ssl_read_buffer(ssl) + ssl_record_prefix_len(ssl);
-  size_t max_out = ssl_read_buffer_len(ssl) - ssl_record_prefix_len(ssl);
+  CBS body;
   uint8_t type, alert;
-  size_t len, consumed;
-  switch (dtls_open_record(ssl, &type, out, &len, &consumed, &alert, max_out,
-                           ssl_read_buffer(ssl), ssl_read_buffer_len(ssl))) {
-    case ssl_open_record_success:
-      ssl_read_buffer_consume(ssl, consumed);
+  size_t consumed;
+  enum ssl_open_record_t open_ret =
+      dtls_open_record(ssl, &type, &body, &consumed, &alert,
+                       ssl_read_buffer(ssl), ssl_read_buffer_len(ssl));
+  ssl_read_buffer_consume(ssl, consumed);
+  switch (open_ret) {
+    case ssl_open_record_partial:
+      /* Impossible in DTLS. */
+      break;
 
-      if (len > 0xffff) {
+    case ssl_open_record_success:
+      if (CBS_len(&body) > 0xffff) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
         return -1;
       }
 
       SSL3_RECORD *rr = &ssl->s3->rrec;
       rr->type = type;
-      rr->length = (uint16_t)len;
-      rr->data = out;
+      rr->length = (uint16_t)CBS_len(&body);
+      rr->data = (uint8_t *)CBS_data(&body);
       return 1;
 
     case ssl_open_record_discard:
-      ssl_read_buffer_consume(ssl, consumed);
       goto again;
 
+    case ssl_open_record_close_notify:
+      return 0;
+
+    case ssl_open_record_fatal_alert:
+      return -1;
+
     case ssl_open_record_error:
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
       return -1;
-
-    case ssl_open_record_partial:
-      /* Impossible in DTLS. */
-      break;
   }
 
   assert(0);
@@ -196,214 +199,29 @@
 
 int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) {
   assert(!SSL_in_init(ssl));
-  return dtls1_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
-}
 
-int dtls1_read_change_cipher_spec(SSL *ssl) {
-  uint8_t byte;
-  int ret = dtls1_read_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, &byte,
-                             1 /* len */, 0 /* no peek */);
-  if (ret <= 0) {
-    return ret;
-  }
-  assert(ret == 1);
+  SSL3_RECORD *rr = &ssl->s3->rrec;
 
-  if (ssl->s3->rrec.length != 0 || byte != SSL3_MT_CCS) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return -1;
-  }
-
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(0, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1,
-                      ssl, ssl->msg_callback_arg);
-  }
-
-  return 1;
-}
-
-void dtls1_read_close_notify(SSL *ssl) {
-  /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS
-   * alerts also aren't delivered reliably, so we may even time out because the
-   * peer never received our close_notify. Report to the caller that the channel
-   * has fully shut down. */
-  if (ssl->s3->recv_shutdown == ssl_shutdown_none) {
-    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
-  }
-}
-
-/* Return up to 'len' payload bytes received in 'type' records.
- * 'type' is one of the following:
- *
- *   -  SSL3_RT_HANDSHAKE (when dtls1_get_message calls us)
- *   -  SSL3_RT_CHANGE_CIPHER_SPEC (when dtls1_read_change_cipher_spec calls us)
- *   -  SSL3_RT_APPLICATION_DATA (when dtls1_read_app_data calls us)
- *
- * If we don't have stored data to work from, read a DTLS record first (possibly
- * multiple records if we still don't have anything to return).
- *
- * This function must handle any surprises the peer may have for us, such as
- * Alert records (e.g. close_notify) and out of records. */
-int dtls1_read_bytes(SSL *ssl, int type, unsigned char *buf, int len, int peek) {
-  int al, ret;
-  unsigned int n;
-  SSL3_RECORD *rr;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-
-  if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE &&
-       type != SSL3_RT_CHANGE_CIPHER_SPEC) ||
-      (peek && type != SSL3_RT_APPLICATION_DATA)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
-  }
-
-start:
-  /* ssl->s3->rrec.type     - is the type of record
-   * ssl->s3->rrec.data     - data
-   * ssl->s3->rrec.off      - offset into 'data' for next read
-   * ssl->s3->rrec.length   - number of bytes. */
-  rr = &ssl->s3->rrec;
-
-  /* Check for timeout */
-  if (DTLSv1_handle_timeout(ssl) > 0) {
-    goto start;
-  }
-
-  /* get new packet if necessary */
+again:
   if (rr->length == 0) {
-    ret = dtls1_get_record(ssl);
+    int ret = dtls1_get_record(ssl);
     if (ret <= 0) {
-      ret = dtls1_read_failed(ssl, ret);
-      /* anything other than a timeout is an error */
-      if (ret <= 0) {
-        return ret;
-      } else {
-        goto start;
-      }
+      return ret;
     }
   }
 
-  /* we now have a packet which can be read and processed */
-
-  if (type == rr->type) {
-    /* Discard empty records. */
-    if (rr->length == 0) {
-      goto start;
-    }
-
-    if (len <= 0) {
-      return len;
-    }
-
-    if ((unsigned int)len > rr->length) {
-      n = rr->length;
-    } else {
-      n = (unsigned int)len;
-    }
-
-    memcpy(buf, rr->data, n);
-    if (!peek) {
-      rr->length -= n;
-      rr->data += n;
-      if (rr->length == 0) {
-        /* The record has been consumed, so we may now clear the buffer. */
-        ssl_read_buffer_discard(ssl);
-      }
-    }
-
-    return n;
-  }
-
-  /* If we get here, then type != rr->type. */
-
-  /* If an alert record, process the alert. */
-  if (rr->type == SSL3_RT_ALERT) {
-    /* Alerts records may not contain fragmented or multiple alerts. */
-    if (rr->length != 2) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
-      goto f_err;
-    }
-
-    if (ssl->msg_callback) {
-      ssl->msg_callback(0, ssl->version, SSL3_RT_ALERT, rr->data, 2, ssl,
-                      ssl->msg_callback_arg);
-    }
-    const uint8_t alert_level = rr->data[0];
-    const uint8_t alert_descr = rr->data[1];
-    rr->length -= 2;
-    rr->data += 2;
-
-    if (ssl->info_callback != NULL) {
-      cb = ssl->info_callback;
-    } else if (ssl->ctx->info_callback != NULL) {
-      cb = ssl->ctx->info_callback;
-    }
-
-    if (cb != NULL) {
-      uint16_t alert = (alert_level << 8) | alert_descr;
-      cb(ssl, SSL_CB_READ_ALERT, alert);
-    }
-
-    if (alert_level == SSL3_AL_WARNING) {
-      if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
-        return 0;
-      }
-    } else if (alert_level == SSL3_AL_FATAL) {
-      char tmp[16];
-
-      OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
-      BIO_snprintf(tmp, sizeof tmp, "%d", alert_descr);
-      ERR_add_error_data(2, "SSL alert number ", tmp);
-      ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
-      SSL_CTX_remove_session(ssl->ctx, ssl->session);
-      return 0;
-    } else {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE);
-      goto f_err;
-    }
-
-    goto start;
-  }
-
-  /* Cross-epoch records are discarded, but we may receive out-of-order
-   * application data between ChangeCipherSpec and Finished or a ChangeCipherSpec
-   * before the appropriate point in the handshake. Those must be silently
-   * discarded.
-   *
-   * However, only allow the out-of-order records in the correct epoch.
-   * Application data must come in the encrypted epoch, and ChangeCipherSpec in
-   * the unencrypted epoch (we never renegotiate). Other cases fall through and
-   * fail with a fatal error. */
-  if ((rr->type == SSL3_RT_APPLICATION_DATA &&
-       ssl->s3->aead_read_ctx != NULL) ||
-      (rr->type == SSL3_RT_CHANGE_CIPHER_SPEC &&
-       ssl->s3->aead_read_ctx == NULL)) {
-    rr->length = 0;
-    goto start;
-  }
-
   if (rr->type == SSL3_RT_HANDSHAKE) {
-    if (type != SSL3_RT_APPLICATION_DATA) {
-      /* Out-of-order handshake record while looking for ChangeCipherSpec. Drop
-       * it silently. */
-      assert(type == SSL3_RT_CHANGE_CIPHER_SPEC);
-      rr->length = 0;
-      goto start;
-    }
-
     /* Parse the first fragment header to determine if this is a pre-CCS or
      * post-CCS handshake record. DTLS resets handshake message numbers on each
      * handshake, so renegotiations and retransmissions are ambiguous. */
-    if (rr->length < DTLS1_HM_HEADER_LENGTH) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
-      goto f_err;
-    }
+    CBS cbs, body;
     struct hm_header_st msg_hdr;
-    dtls1_get_message_header(rr->data, &msg_hdr);
+    CBS_init(&cbs, rr->data, rr->length);
+    if (!dtls1_parse_fragment(&cbs, &msg_hdr, &body)) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HANDSHAKE_RECORD);
+      return -1;
+    }
 
     if (msg_hdr.type == SSL3_MT_FINISHED) {
       if (msg_hdr.frag_off == 0) {
@@ -418,19 +236,95 @@
       }
 
       rr->length = 0;
-      goto start;
+      goto again;
     }
 
     /* Otherwise, this is a pre-CCS handshake message from an unsupported
      * renegotiation attempt. Fall through to the error path. */
   }
 
-  al = SSL_AD_UNEXPECTED_MESSAGE;
-  OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+  if (rr->type != SSL3_RT_APPLICATION_DATA) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+    return -1;
+  }
 
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  return -1;
+  /* Discard empty records. */
+  if (rr->length == 0) {
+    goto again;
+  }
+
+  if (len <= 0) {
+    return len;
+  }
+
+  if ((unsigned)len > rr->length) {
+    len = rr->length;
+  }
+
+  memcpy(buf, rr->data, len);
+  if (!peek) {
+    /* TODO(davidben): Should the record be truncated instead? This is a
+     * datagram transport. See https://crbug.com/boringssl/65. */
+    rr->length -= len;
+    rr->data += len;
+    if (rr->length == 0) {
+      /* The record has been consumed, so we may now clear the buffer. */
+      ssl_read_buffer_discard(ssl);
+    }
+  }
+
+  return len;
+}
+
+int dtls1_read_change_cipher_spec(SSL *ssl) {
+  SSL3_RECORD *rr = &ssl->s3->rrec;
+
+again:
+  if (rr->length == 0) {
+    int ret = dtls1_get_record(ssl);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  /* Drop handshake records silently. The epochs match, so this must be a
+   * retransmit of a message we already received. */
+  if (rr->type == SSL3_RT_HANDSHAKE) {
+    rr->length = 0;
+    goto again;
+  }
+
+  /* Other record types are illegal in this epoch. Note all application data
+   * records come in the encrypted epoch. */
+  if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+    return -1;
+  }
+
+  if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+    return -1;
+  }
+
+  ssl_do_msg_callback(ssl, 0 /* read */, ssl->version,
+                      SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, rr->length);
+
+  rr->length = 0;
+  ssl_read_buffer_discard(ssl);
+  return 1;
+}
+
+void dtls1_read_close_notify(SSL *ssl) {
+  /* Bidirectional shutdown doesn't make sense for an unordered transport. DTLS
+   * alerts also aren't delivered reliably, so we may even time out because the
+   * peer never received our close_notify. Report to the caller that the channel
+   * has fully shut down. */
+  if (ssl->s3->recv_shutdown == ssl_shutdown_none) {
+    ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+  }
 }
 
 int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) {
@@ -441,20 +335,26 @@
     return -1;
   }
 
-  return dtls1_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf_, len,
-                           dtls1_use_current_epoch);
+  if (len < 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_LENGTH);
+    return -1;
+  }
+
+  if (len == 0) {
+    return 0;
+  }
+
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf_, (size_t)len,
+                               dtls1_use_current_epoch);
+  if (ret <= 0) {
+    return ret;
+  }
+  return len;
 }
 
-/* Call this to write data in records of type 'type' It will return <= 0 if not
- * all data has been sent or non-blocking IO. */
-int dtls1_write_bytes(SSL *ssl, int type, const void *buf, int len,
-                      enum dtls1_use_epoch_t use_epoch) {
+int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
+                       enum dtls1_use_epoch_t use_epoch) {
   assert(len <= SSL3_RT_MAX_PLAIN_LENGTH);
-  return do_dtls1_write(ssl, type, buf, len, use_epoch);
-}
-
-static int do_dtls1_write(SSL *ssl, int type, const uint8_t *buf,
-                          unsigned int len, enum dtls1_use_epoch_t use_epoch) {
   /* There should never be a pending write buffer in DTLS. One can't write half
    * a datagram, so the write buffer is always dropped in
    * |ssl_write_buffer_flush|. */
@@ -474,10 +374,6 @@
     return -1;
   }
 
-  if (len == 0) {
-    return 0;
-  }
-
   size_t max_out = len + ssl_max_seal_overhead(ssl);
   uint8_t *out;
   size_t ciphertext_len;
@@ -493,13 +389,13 @@
   if (ret <= 0) {
     return ret;
   }
-  return (int)len;
+  return 1;
 }
 
 int dtls1_dispatch_alert(SSL *ssl) {
   ssl->s3->alert_dispatch = 0;
-  int ret = do_dtls1_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2,
-                           dtls1_use_current_epoch);
+  int ret = dtls1_write_record(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2,
+                               dtls1_use_current_epoch);
   if (ret <= 0) {
     ssl->s3->alert_dispatch = 1;
     return ret;
@@ -510,22 +406,11 @@
     BIO_flush(ssl->wbio);
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_ALERT,
-                      ssl->s3->send_alert, 2, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, SSL3_RT_ALERT,
+                      ssl->s3->send_alert, 2);
 
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
-
-  if (cb != NULL) {
-    int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
-    cb(ssl, SSL_CB_WRITE_ALERT, alert);
-  }
+  int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+  ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert);
 
   return 1;
 }
diff --git a/src/ssl/d1_srvr.c b/src/ssl/d1_srvr.c
deleted file mode 100644
index 9913e73..0000000
--- a/src/ssl/d1_srvr.c
+++ /dev/null
@@ -1,452 +0,0 @@
-/*
- * DTLS implementation written by Nagendra Modadugu
- * (nagendra@cs.stanford.edu) for the OpenSSL project 2005. 
- */
-/* ====================================================================
- * Copyright (c) 1999-2007 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    openssl-core@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com).
- *
- */
-/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
- * All rights reserved.
- *
- * This package is an SSL implementation written
- * by Eric Young (eay@cryptsoft.com).
- * The implementation was written so as to conform with Netscapes SSL.
- *
- * This library is free for commercial and non-commercial use as long as
- * the following conditions are aheared to.  The following conditions
- * apply to all code found in this distribution, be it the RC4, RSA,
- * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
- * included with this distribution is covered by the same copyright terms
- * except that the holder is Tim Hudson (tjh@cryptsoft.com).
- *
- * Copyright remains Eric Young's, and as such any Copyright notices in
- * the code are not to be removed.
- * If this package is used in a product, Eric Young should be given attribution
- * as the author of the parts of the library used.
- * This can be in the form of a textual message at program startup or
- * in documentation (online or textual) provided with the package.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *    "This product includes cryptographic software written by
- *     Eric Young (eay@cryptsoft.com)"
- *    The word 'cryptographic' can be left out if the rouines from the library
- *    being used are not cryptographic related :-).
- * 4. If you include any Windows specific code (or a derivative thereof) from
- *    the apps directory (application code) you must include an acknowledgement:
- *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
- *
- * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * The licence and distribution terms for any publically available version or
- * derivative of this code cannot be changed.  i.e. this code cannot simply be
- * copied and put under another distribution licence
- * [including the GNU Public Licence.]
- */
-
-#include <openssl/ssl.h>
-
-#include <assert.h>
-
-#include <openssl/bn.h>
-#include <openssl/buf.h>
-#include <openssl/dh.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/md5.h>
-#include <openssl/rand.h>
-#include <openssl/x509.h>
-
-#include "internal.h"
-
-
-int dtls1_accept(SSL *ssl) {
-  BUF_MEM *buf = NULL;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-  uint32_t alg_a;
-  int ret = -1;
-  int new_state, state, skip = 0;
-
-  assert(ssl->handshake_func == dtls1_accept);
-  assert(ssl->server);
-  assert(SSL_IS_DTLS(ssl));
-
-  ERR_clear_system_error();
-
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
-
-  for (;;) {
-    state = ssl->state;
-
-    switch (ssl->state) {
-      case SSL_ST_ACCEPT:
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
-        }
-
-        if (ssl->init_buf == NULL) {
-          buf = BUF_MEM_new();
-          if (buf == NULL || !BUF_MEM_reserve(buf, SSL3_RT_MAX_PLAIN_LENGTH)) {
-            ret = -1;
-            goto end;
-          }
-          ssl->init_buf = buf;
-          buf = NULL;
-        }
-
-        ssl->init_num = 0;
-
-        if (!ssl_init_wbio_buffer(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
-        if (!ssl3_init_handshake_buffer(ssl)) {
-          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          ret = -1;
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
-        break;
-
-      case SSL3_ST_SR_CLNT_HELLO_A:
-      case SSL3_ST_SR_CLNT_HELLO_B:
-      case SSL3_ST_SR_CLNT_HELLO_C:
-        ret = ssl3_get_client_hello(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        dtls1_stop_timer(ssl);
-        ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
-        break;
-
-      case SSL3_ST_SW_SRVR_HELLO_A:
-      case SSL3_ST_SW_SRVR_HELLO_B:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_server_hello(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        if (ssl->hit) {
-          if (ssl->tlsext_ticket_expected) {
-            ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
-          } else {
-            ssl->state = SSL3_ST_SW_CHANGE_A;
-          }
-        } else {
-          ssl->state = SSL3_ST_SW_CERT_A;
-        }
-        break;
-
-      case SSL3_ST_SW_CERT_A:
-      case SSL3_ST_SW_CERT_B:
-        if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
-          dtls1_start_timer(ssl);
-          ret = ssl3_send_server_certificate(ssl);
-          if (ret <= 0) {
-            goto end;
-          }
-          if (ssl->s3->tmp.certificate_status_expected) {
-            ssl->state = SSL3_ST_SW_CERT_STATUS_A;
-          } else {
-            ssl->state = SSL3_ST_SW_KEY_EXCH_A;
-          }
-        } else {
-          skip = 1;
-          ssl->state = SSL3_ST_SW_KEY_EXCH_A;
-        }
-        break;
-
-      case SSL3_ST_SW_CERT_STATUS_A:
-      case SSL3_ST_SW_CERT_STATUS_B:
-        ret = ssl3_send_certificate_status(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_SW_KEY_EXCH_A;
-        break;
-
-      case SSL3_ST_SW_KEY_EXCH_A:
-      case SSL3_ST_SW_KEY_EXCH_B:
-      case SSL3_ST_SW_KEY_EXCH_C:
-        alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
-
-        /* Send a ServerKeyExchange message if:
-         * - The key exchange is ephemeral or anonymous
-         *   Diffie-Hellman.
-         * - There is a PSK identity hint.
-         *
-         * TODO(davidben): This logic is currently duplicated
-         * in s3_srvr.c. Fix this. In the meantime, keep them
-         * in sync. */
-        if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher) ||
-            ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
-          dtls1_start_timer(ssl);
-          ret = ssl3_send_server_key_exchange(ssl);
-          if (ret <= 0) {
-            goto end;
-          }
-        } else {
-          skip = 1;
-        }
-
-        ssl->state = SSL3_ST_SW_CERT_REQ_A;
-        break;
-
-      case SSL3_ST_SW_CERT_REQ_A:
-      case SSL3_ST_SW_CERT_REQ_B:
-        if (ssl->s3->tmp.cert_request) {
-          dtls1_start_timer(ssl);
-          ret = ssl3_send_certificate_request(ssl);
-          if (ret <= 0) {
-            goto end;
-          }
-        } else {
-          skip = 1;
-        }
-        ssl->state = SSL3_ST_SW_SRVR_DONE_A;
-        break;
-
-      case SSL3_ST_SW_SRVR_DONE_A:
-      case SSL3_ST_SW_SRVR_DONE_B:
-        dtls1_start_timer(ssl);
-        ret = ssl3_send_server_done(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->s3->tmp.next_state = SSL3_ST_SR_CERT_A;
-        ssl->state = SSL3_ST_SW_FLUSH;
-        break;
-
-      case SSL3_ST_SW_FLUSH:
-        if (BIO_flush(ssl->wbio) <= 0) {
-          ssl->rwstate = SSL_WRITING;
-          ret = -1;
-          goto end;
-        }
-        ssl->state = ssl->s3->tmp.next_state;
-        break;
-
-      case SSL3_ST_SR_CERT_A:
-        if (ssl->s3->tmp.cert_request) {
-          ret = ssl3_get_client_certificate(ssl);
-          if (ret <= 0) {
-            goto end;
-          }
-        }
-        ssl->state = SSL3_ST_SR_KEY_EXCH_A;
-        break;
-
-      case SSL3_ST_SR_KEY_EXCH_A:
-      case SSL3_ST_SR_KEY_EXCH_B:
-        ret = ssl3_get_client_key_exchange(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_SR_CERT_VRFY_A;
-        break;
-
-      case SSL3_ST_SR_CERT_VRFY_A:
-        ret = ssl3_get_cert_verify(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_SR_CHANGE;
-        break;
-
-      case SSL3_ST_SR_CHANGE:
-        ret = ssl->method->ssl_read_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_READ)) {
-          ret = -1;
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_SR_FINISHED_A;
-        break;
-
-      case SSL3_ST_SR_FINISHED_A:
-        ret = ssl3_get_finished(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        dtls1_stop_timer(ssl);
-        if (ssl->hit) {
-          ssl->state = SSL_ST_OK;
-        } else if (ssl->tlsext_ticket_expected) {
-          ssl->state = SSL3_ST_SW_SESSION_TICKET_A;
-        } else {
-          ssl->state = SSL3_ST_SW_CHANGE_A;
-        }
-        break;
-
-      case SSL3_ST_SW_SESSION_TICKET_A:
-      case SSL3_ST_SW_SESSION_TICKET_B:
-        ret = ssl3_send_new_session_ticket(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_SW_CHANGE_A;
-        break;
-
-      case SSL3_ST_SW_CHANGE_A:
-      case SSL3_ST_SW_CHANGE_B:
-        ret = dtls1_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
-                                            SSL3_ST_SW_CHANGE_B);
-
-        if (ret <= 0) {
-          goto end;
-        }
-
-        ssl->state = SSL3_ST_SW_FINISHED_A;
-
-        if (!tls1_change_cipher_state(ssl, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
-          ret = -1;
-          goto end;
-        }
-        break;
-
-      case SSL3_ST_SW_FINISHED_A:
-      case SSL3_ST_SW_FINISHED_B:
-        ret = ssl3_send_finished(ssl, SSL3_ST_SW_FINISHED_A,
-                                 SSL3_ST_SW_FINISHED_B);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_SW_FLUSH;
-        if (ssl->hit) {
-          ssl->s3->tmp.next_state = SSL3_ST_SR_CHANGE;
-        } else {
-          ssl->s3->tmp.next_state = SSL_ST_OK;
-        }
-        break;
-
-      case SSL_ST_OK:
-        ssl3_cleanup_key_block(ssl);
-
-        /* remove buffering on output */
-        ssl_free_wbio_buffer(ssl);
-
-	/* |init_buf| cannot be released because post-handshake retransmit
-         * relies on that buffer being available as scratch space.
-         *
-         * TODO(davidben): Fix this. */
-        ssl->init_num = 0;
-        ssl->s3->initial_handshake_complete = 1;
-
-        ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
-
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
-        }
-
-        ret = 1;
-
-        /* done handshaking, next message is client hello */
-        ssl->d1->handshake_read_seq = 0;
-        /* next message is server hello */
-        ssl->d1->handshake_write_seq = 0;
-        ssl->d1->next_handshake_write_seq = 0;
-        goto end;
-
-      default:
-        OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_STATE);
-        ret = -1;
-        goto end;
-    }
-
-    if (!ssl->s3->tmp.reuse_message && !skip) {
-      if (cb != NULL && ssl->state != state) {
-        new_state = ssl->state;
-        ssl->state = state;
-        cb(ssl, SSL_CB_ACCEPT_LOOP, 1);
-        ssl->state = new_state;
-      }
-    }
-    skip = 0;
-  }
-
-end:
-  BUF_MEM_free(buf);
-  if (cb != NULL) {
-    cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
-  }
-  return ret;
-}
diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.c
index eaf6df7..e784e55 100644
--- a/src/ssl/dtls_record.c
+++ b/src/ssl/dtls_record.c
@@ -118,6 +118,7 @@
 #include <openssl/err.h>
 
 #include "internal.h"
+#include "../crypto/internal.h"
 
 
 /* to_u64_be treats |in| as a 8-byte big-endian integer and returns the value as
@@ -171,10 +172,12 @@
   }
 }
 
-enum ssl_open_record_t dtls_open_record(
-    SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len,
-    size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in,
-    size_t in_len) {
+enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+                                        size_t *out_consumed,
+                                        uint8_t *out_alert, uint8_t *in,
+                                        size_t in_len) {
+  *out_consumed = 0;
+
   CBS cbs;
   CBS_init(&cbs, in, in_len);
 
@@ -195,10 +198,8 @@
     return ssl_open_record_discard;
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(0 /* read */, 0, SSL3_RT_HEADER, in,
-                      DTLS1_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 0 /* read */, 0, SSL3_RT_HEADER, in,
+                      DTLS1_RT_HEADER_LENGTH);
 
   uint16_t epoch = (((uint16_t)sequence[0]) << 8) | sequence[1];
   if (epoch != ssl->d1->r_epoch ||
@@ -211,11 +212,9 @@
     return ssl_open_record_discard;
   }
 
-  /* Decrypt the body. */
-  size_t plaintext_len;
-  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, &plaintext_len, max_out,
-                         type, version, sequence, CBS_data(&body),
-                         CBS_len(&body))) {
+  /* Decrypt the body in-place. */
+  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, type, version, sequence,
+                         (uint8_t *)CBS_data(&body), CBS_len(&body))) {
     /* Bad packets are silently dropped in DTLS. See section 4.2.1 of RFC 6347.
      * Clear the error queue of any errors decryption may have added. Drop the
      * entire packet as it must not have come from the peer.
@@ -226,9 +225,10 @@
     *out_consumed = in_len - CBS_len(&cbs);
     return ssl_open_record_discard;
   }
+  *out_consumed = in_len - CBS_len(&cbs);
 
   /* Check the plaintext length. */
-  if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) {
+  if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
@@ -239,15 +239,24 @@
   /* TODO(davidben): Limit the number of empty records as in TLS? This is only
    * useful if we also limit discarded packets. */
 
+  if (type == SSL3_RT_ALERT) {
+    return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out));
+  }
+
+  ssl->s3->warning_alert_count = 0;
+
   *out_type = type;
-  *out_len = plaintext_len;
-  *out_consumed = in_len - CBS_len(&cbs);
   return ssl_open_record_success;
 }
 
 int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
                      uint8_t type, const uint8_t *in, size_t in_len,
                      enum dtls1_use_epoch_t use_epoch) {
+  if (buffers_alias(in, in_len, out, max_out)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
+    return 0;
+  }
+
   /* Determine the parameters for the current epoch. */
   uint16_t epoch = ssl->d1->w_epoch;
   SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx;
@@ -265,12 +274,6 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return 0;
   }
-  /* Check the record header does not alias any part of the input.
-   * |SSL_AEAD_CTX_seal| will internally enforce other aliasing requirements. */
-  if (in < out + DTLS1_RT_HEADER_LENGTH && out < in + in_len) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
-    return 0;
-  }
 
   out[0] = type;
 
@@ -299,10 +302,8 @@
 
   *out_len = DTLS1_RT_HEADER_LENGTH + ciphertext_len;
 
-  if (ssl->msg_callback) {
-    ssl->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out,
-                      DTLS1_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 1 /* write */, 0, SSL3_RT_HEADER, out,
+                      DTLS1_RT_HEADER_LENGTH);
 
   return 1;
 }
diff --git a/src/ssl/s3_clnt.c b/src/ssl/handshake_client.c
similarity index 92%
rename from src/ssl/s3_clnt.c
rename to src/ssl/handshake_client.c
index 2665f15..9b96bcd 100644
--- a/src/ssl/s3_clnt.c
+++ b/src/ssl/handshake_client.c
@@ -170,32 +170,36 @@
 #include "../crypto/dh/internal.h"
 
 
+static int ssl3_send_client_hello(SSL *ssl);
+static int dtls1_get_hello_verify(SSL *ssl);
+static int ssl3_get_server_hello(SSL *ssl);
+static int ssl3_get_server_certificate(SSL *ssl);
+static int ssl3_get_cert_status(SSL *ssl);
+static int ssl3_verify_server_cert(SSL *ssl);
+static int ssl3_get_server_key_exchange(SSL *ssl);
+static int ssl3_get_certificate_request(SSL *ssl);
+static int ssl3_get_server_hello_done(SSL *ssl);
+static int ssl3_send_client_certificate(SSL *ssl);
+static int ssl3_send_client_key_exchange(SSL *ssl);
+static int ssl3_send_cert_verify(SSL *ssl);
+static int ssl3_send_next_proto(SSL *ssl);
+static int ssl3_send_channel_id(SSL *ssl);
+static int ssl3_get_new_session_ticket(SSL *ssl);
+
 int ssl3_connect(SSL *ssl) {
   BUF_MEM *buf = NULL;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
   int ret = -1;
-  int new_state, state, skip = 0;
+  int state, skip = 0;
 
   assert(ssl->handshake_func == ssl3_connect);
   assert(!ssl->server);
-  assert(!SSL_IS_DTLS(ssl));
-
-  ERR_clear_system_error();
-
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
 
   for (;;) {
     state = ssl->state;
 
     switch (ssl->state) {
       case SSL_ST_CONNECT:
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
-        }
+        ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
@@ -215,12 +219,6 @@
           goto end;
         }
 
-        if (!ssl3_init_handshake_buffer(ssl)) {
-          OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-          ret = -1;
-          goto end;
-        }
-
         ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
         break;
 
@@ -230,10 +228,29 @@
         if (ret <= 0) {
           goto end;
         }
-        ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
+
+        if (!SSL_IS_DTLS(ssl) || ssl->d1->send_cookie) {
+          ssl->s3->tmp.next_state = SSL3_ST_CR_SRVR_HELLO_A;
+        } else {
+          ssl->s3->tmp.next_state = DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A;
+        }
         ssl->state = SSL3_ST_CW_FLUSH;
         break;
 
+      case DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A:
+        assert(SSL_IS_DTLS(ssl));
+        ret = dtls1_get_hello_verify(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+        if (ssl->d1->send_cookie) {
+          ssl->method->received_flight(ssl);
+          ssl->state = SSL3_ST_CW_CLNT_HELLO_A;
+        } else {
+          ssl->state = SSL3_ST_CR_SRVR_HELLO_A;
+        }
+        break;
+
       case SSL3_ST_CR_SRVR_HELLO_A:
         ret = ssl3_get_server_hello(ssl);
         if (ret <= 0) {
@@ -268,6 +285,14 @@
         }
         break;
 
+      case SSL3_ST_CR_CERT_STATUS_A:
+        ret = ssl3_get_cert_status(ssl);
+        if (ret <= 0) {
+          goto end;
+        }
+        ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
+        break;
+
       case SSL3_ST_VERIFY_SERVER_CERT:
         ret = ssl3_verify_server_cert(ssl);
         if (ret <= 0) {
@@ -298,16 +323,16 @@
         break;
 
       case SSL3_ST_CR_SRVR_DONE_A:
-        ret = ssl3_get_server_done(ssl);
+        ret = ssl3_get_server_hello_done(ssl);
         if (ret <= 0) {
           goto end;
         }
+        ssl->method->received_flight(ssl);
         if (ssl->s3->tmp.cert_req) {
           ssl->state = SSL3_ST_CW_CERT_A;
         } else {
           ssl->state = SSL3_ST_CW_KEY_EXCH_A;
         }
-
         break;
 
       case SSL3_ST_CW_CERT_A:
@@ -348,8 +373,8 @@
 
       case SSL3_ST_CW_CHANGE_A:
       case SSL3_ST_CW_CHANGE_B:
-        ret = ssl3_send_change_cipher_spec(ssl, SSL3_ST_CW_CHANGE_A,
-                                           SSL3_ST_CW_CHANGE_B);
+        ret = ssl->method->send_change_cipher_spec(ssl, SSL3_ST_CW_CHANGE_A,
+                                                   SSL3_ST_CW_CHANGE_B);
         if (ret <= 0) {
           goto end;
         }
@@ -429,6 +454,19 @@
         }
         break;
 
+      case SSL3_ST_FALSE_START:
+        /* Allow NewSessionTicket if ticket expected */
+        if (ssl->tlsext_ticket_expected) {
+          ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
+        } else {
+          ssl->state = SSL3_ST_CR_CHANGE;
+        }
+        ssl->s3->tmp.in_false_start = 1;
+
+        ssl_free_wbio_buffer(ssl);
+        ret = 1;
+        goto end;
+
       case SSL3_ST_CR_SESSION_TICKET_A:
         ret = ssl3_get_new_session_ticket(ssl);
         if (ret <= 0) {
@@ -437,14 +475,6 @@
         ssl->state = SSL3_ST_CR_CHANGE;
         break;
 
-      case SSL3_ST_CR_CERT_STATUS_A:
-        ret = ssl3_get_cert_status(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        ssl->state = SSL3_ST_VERIFY_SERVER_CERT;
-        break;
-
       case SSL3_ST_CR_CHANGE:
         ret = ssl->method->ssl_read_change_cipher_spec(ssl);
         if (ret <= 0) {
@@ -463,6 +493,7 @@
         if (ret <= 0) {
           goto end;
         }
+        ssl->method->received_flight(ssl);
 
         if (ssl->hit) {
           ssl->state = SSL3_ST_CW_CHANGE_A;
@@ -478,28 +509,24 @@
           goto end;
         }
         ssl->state = ssl->s3->tmp.next_state;
-        break;
-
-      case SSL3_ST_FALSE_START:
-        /* Allow NewSessionTicket if ticket expected */
-        if (ssl->tlsext_ticket_expected) {
-          ssl->state = SSL3_ST_CR_SESSION_TICKET_A;
-        } else {
-          ssl->state = SSL3_ST_CR_CHANGE;
+        if (ssl->state != SSL_ST_OK) {
+          ssl->method->expect_flight(ssl);
         }
-        ssl->s3->tmp.in_false_start = 1;
-
-        ssl_free_wbio_buffer(ssl);
-        ret = 1;
-        goto end;
+        break;
 
       case SSL_ST_OK:
         /* clean a few things up */
         ssl3_cleanup_key_block(ssl);
 
-        BUF_MEM_free(ssl->init_buf);
-        ssl->init_buf = NULL;
-        ssl->init_num = 0;
+        /* |init_buf| cannot be released in DTLS because post-handshake
+         * retransmit relies on that buffer being available as scratch space.
+         *
+         * TODO(davidben): Fix this. */
+        if (!SSL_IS_DTLS(ssl)) {
+          BUF_MEM_free(ssl->init_buf);
+          ssl->init_buf = NULL;
+          ssl->init_num = 0;
+        }
 
         /* Remove write buffering now. */
         ssl_free_wbio_buffer(ssl);
@@ -514,13 +541,13 @@
           ssl_update_cache(ssl, SSL_SESS_CACHE_CLIENT);
         }
 
-        ret = 1;
-        /* ssl->server=0; */
-
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
+        if (SSL_IS_DTLS(ssl)) {
+          ssl->d1->handshake_read_seq = 0;
+          ssl->d1->next_handshake_write_seq = 0;
         }
 
+        ret = 1;
+        ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
         goto end;
 
       default:
@@ -529,22 +556,18 @@
         goto end;
     }
 
-    if (!ssl->s3->tmp.reuse_message && !skip) {
-      if (cb != NULL && ssl->state != state) {
-        new_state = ssl->state;
-        ssl->state = state;
-        cb(ssl, SSL_CB_CONNECT_LOOP, 1);
-        ssl->state = new_state;
-      }
+    if (!ssl->s3->tmp.reuse_message && !skip && ssl->state != state) {
+      int new_state = ssl->state;
+      ssl->state = state;
+      ssl_do_info_callback(ssl, SSL_CB_CONNECT_LOOP, 1);
+      ssl->state = new_state;
     }
     skip = 0;
   }
 
 end:
   BUF_MEM_free(buf);
-  if (cb != NULL) {
-    cb(ssl, SSL_CB_CONNECT_EXIT, ret);
-  }
+  ssl_do_info_callback(ssl, SSL_CB_CONNECT_EXIT, ret);
   return ret;
 }
 
@@ -603,14 +626,14 @@
   return CBB_flush(out);
 }
 
-int ssl3_send_client_hello(SSL *ssl) {
+static int ssl3_send_client_hello(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_CLNT_HELLO_B) {
     return ssl_do_write(ssl);
   }
 
-  /* In DTLS, reset the handshake buffer each time a new ClientHello is
-   * assembled. We may send multiple if we receive HelloVerifyRequest. */
-  if (SSL_IS_DTLS(ssl) && !ssl3_init_handshake_buffer(ssl)) {
+  /* The handshake buffer is reset on every ClientHello. Notably, in DTLS, we
+   * may send multiple ClientHellos if we receive HelloVerifyRequest. */
+  if (!ssl3_init_handshake_buffer(ssl)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
   }
@@ -694,7 +717,51 @@
   return -1;
 }
 
-int ssl3_get_server_hello(SSL *ssl) {
+static int dtls1_get_hello_verify(SSL *ssl) {
+  long n;
+  int al, ok = 0;
+  CBS hello_verify_request, cookie;
+  uint16_t server_version;
+
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
+
+  if (!ok) {
+    return n;
+  }
+
+  if (ssl->s3->tmp.message_type != DTLS1_MT_HELLO_VERIFY_REQUEST) {
+    ssl->d1->send_cookie = 0;
+    ssl->s3->tmp.reuse_message = 1;
+    return 1;
+  }
+
+  CBS_init(&hello_verify_request, ssl->init_msg, n);
+
+  if (!CBS_get_u16(&hello_verify_request, &server_version) ||
+      !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
+      CBS_len(&hello_verify_request) != 0) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
+  }
+
+  if (CBS_len(&cookie) > sizeof(ssl->d1->cookie)) {
+    al = SSL_AD_ILLEGAL_PARAMETER;
+    goto f_err;
+  }
+
+  memcpy(ssl->d1->cookie, CBS_data(&cookie), CBS_len(&cookie));
+  ssl->d1->cookie_len = CBS_len(&cookie);
+
+  ssl->d1->send_cookie = 1;
+  return 1;
+
+f_err:
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+  return -1;
+}
+
+static int ssl3_get_server_hello(SSL *ssl) {
   STACK_OF(SSL_CIPHER) *sk;
   const SSL_CIPHER *c;
   CERT *ct = ssl->cert;
@@ -924,7 +991,7 @@
   return ret;
 }
 
-int ssl3_get_server_certificate(SSL *ssl) {
+static int ssl3_get_server_certificate(SSL *ssl) {
   int al, ok, ret = -1;
   unsigned long n;
   X509 *x = NULL;
@@ -1014,7 +1081,64 @@
   return ret;
 }
 
-int ssl3_get_server_key_exchange(SSL *ssl) {
+static int ssl3_get_cert_status(SSL *ssl) {
+  int ok, al;
+  long n;
+  CBS certificate_status, ocsp_response;
+  uint8_t status_type;
+
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
+
+  if (!ok) {
+    return n;
+  }
+
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
+    /* A server may send status_request in ServerHello and then change
+     * its mind about sending CertificateStatus. */
+    ssl->s3->tmp.reuse_message = 1;
+    return 1;
+  }
+
+  CBS_init(&certificate_status, ssl->init_msg, n);
+  if (!CBS_get_u8(&certificate_status, &status_type) ||
+      status_type != TLSEXT_STATUSTYPE_ocsp ||
+      !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) ||
+      CBS_len(&ocsp_response) == 0 ||
+      CBS_len(&certificate_status) != 0) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
+  }
+
+  if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response,
+                &ssl->session->ocsp_response_length)) {
+    al = SSL_AD_INTERNAL_ERROR;
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto f_err;
+  }
+  return 1;
+
+f_err:
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+  return -1;
+}
+
+static int ssl3_verify_server_cert(SSL *ssl) {
+  int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain);
+  if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) {
+    int al = ssl_verify_alarm_type(ssl->verify_result);
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
+  } else {
+    ret = 1;
+    ERR_clear_error(); /* but we keep ssl->verify_result */
+  }
+
+  return ret;
+}
+
+static int ssl3_get_server_key_exchange(SSL *ssl) {
   EVP_MD_CTX md_ctx;
   int al, ok;
   EVP_PKEY *pkey = NULL;
@@ -1283,7 +1407,7 @@
   return X509_NAME_cmp(*a, *b);
 }
 
-int ssl3_get_certificate_request(SSL *ssl) {
+static int ssl3_get_certificate_request(SSL *ssl) {
   int ok, ret = 0;
   X509_NAME *xn = NULL;
   STACK_OF(X509_NAME) *ca_sk = NULL;
@@ -1296,7 +1420,7 @@
 
   ssl->s3->tmp.cert_req = 0;
 
-  if (ssl->s3->tmp.message_type == SSL3_MT_SERVER_DONE) {
+  if (ssl->s3->tmp.message_type == SSL3_MT_SERVER_HELLO_DONE) {
     ssl->s3->tmp.reuse_message = 1;
     /* If we get here we don't need the handshake buffer as we won't be doing
      * client auth. */
@@ -1391,128 +1515,12 @@
   return ret;
 }
 
-int ssl3_get_new_session_ticket(SSL *ssl) {
-  int ok, al;
-  long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
-                                        ssl_hash_message, &ok);
-
-  if (!ok) {
-    return n;
-  }
-
-  CBS new_session_ticket, ticket;
-  uint32_t ticket_lifetime_hint;
-  CBS_init(&new_session_ticket, ssl->init_msg, n);
-  if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) ||
-      !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
-      CBS_len(&new_session_ticket) != 0) {
-    al = SSL_AD_DECODE_ERROR;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
-  }
-
-  if (CBS_len(&ticket) == 0) {
-    /* RFC 5077 allows a server to change its mind and send no ticket after
-     * negotiating the extension. The value of |tlsext_ticket_expected| is
-     * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
-     * update. */
-    ssl->tlsext_ticket_expected = 0;
-    return 1;
-  }
-
-  if (ssl->hit) {
-    /* The server is sending a new ticket for an existing session. Sessions are
-     * immutable once established, so duplicate all but the ticket of the
-     * existing session. */
-    uint8_t *bytes;
-    size_t bytes_len;
-    if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) {
-      goto err;
-    }
-    SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len);
-    OPENSSL_free(bytes);
-    if (new_session == NULL) {
-      /* This should never happen. */
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-
-    SSL_SESSION_free(ssl->session);
-    ssl->session = new_session;
-  }
-
-  if (!CBS_stow(&ticket, &ssl->session->tlsext_tick,
-                &ssl->session->tlsext_ticklen)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-  ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
-
-  /* Generate a session ID for this session based on the session ticket. We use
-   * the session ID mechanism for detecting ticket resumption. This also fits in
-   * with assumptions elsewhere in OpenSSL.*/
-  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id,
-                  &ssl->session->session_id_length, EVP_sha256(), NULL)) {
-    goto err;
-  }
-
-  return 1;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-err:
-  return -1;
-}
-
-int ssl3_get_cert_status(SSL *ssl) {
-  int ok, al;
-  long n;
-  CBS certificate_status, ocsp_response;
-  uint8_t status_type;
-
-  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
-
-  if (!ok) {
-    return n;
-  }
-
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_STATUS) {
-    /* A server may send status_request in ServerHello and then change
-     * its mind about sending CertificateStatus. */
-    ssl->s3->tmp.reuse_message = 1;
-    return 1;
-  }
-
-  CBS_init(&certificate_status, ssl->init_msg, n);
-  if (!CBS_get_u8(&certificate_status, &status_type) ||
-      status_type != TLSEXT_STATUSTYPE_ocsp ||
-      !CBS_get_u24_length_prefixed(&certificate_status, &ocsp_response) ||
-      CBS_len(&ocsp_response) == 0 ||
-      CBS_len(&certificate_status) != 0) {
-    al = SSL_AD_DECODE_ERROR;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
-  }
-
-  if (!CBS_stow(&ocsp_response, &ssl->session->ocsp_response,
-                &ssl->session->ocsp_response_length)) {
-    al = SSL_AD_INTERNAL_ERROR;
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    goto f_err;
-  }
-  return 1;
-
-f_err:
-  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  return -1;
-}
-
-int ssl3_get_server_done(SSL *ssl) {
+static int ssl3_get_server_hello_done(SSL *ssl) {
   int ok;
   long n;
 
-  n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_DONE, ssl_hash_message,
-                                   &ok);
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO_DONE,
+                                   ssl_hash_message, &ok);
 
   if (!ok) {
     return n;
@@ -1528,10 +1536,105 @@
   return 1;
 }
 
+/* ssl3_has_client_certificate returns true if a client certificate is
+ * configured. */
+static int ssl3_has_client_certificate(SSL *ssl) {
+  return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl);
+}
+
+static int ssl_do_client_cert_cb(SSL *ssl, X509 **out_x509,
+                                 EVP_PKEY **out_pkey) {
+  if (ssl->ctx->client_cert_cb == NULL) {
+    return 0;
+  }
+
+  int ret = ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey);
+  if (ret <= 0) {
+    return ret;
+  }
+
+  assert(*out_x509 != NULL);
+  assert(*out_pkey != NULL);
+  return 1;
+}
+
+static int ssl3_send_client_certificate(SSL *ssl) {
+  if (ssl->state == SSL3_ST_CW_CERT_A) {
+    /* Call cert_cb to update the certificate. */
+    if (ssl->cert->cert_cb) {
+      int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
+      if (ret < 0) {
+        ssl->rwstate = SSL_X509_LOOKUP;
+        return -1;
+      }
+      if (ret == 0) {
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+        return -1;
+      }
+    }
+
+    if (ssl3_has_client_certificate(ssl)) {
+      ssl->state = SSL3_ST_CW_CERT_C;
+    } else {
+      ssl->state = SSL3_ST_CW_CERT_B;
+    }
+  }
+
+  if (ssl->state == SSL3_ST_CW_CERT_B) {
+    /* Call client_cert_cb to update the certificate. */
+    X509 *x509 = NULL;
+    EVP_PKEY *pkey = NULL;
+    int ret = ssl_do_client_cert_cb(ssl, &x509, &pkey);
+    if (ret < 0) {
+      ssl->rwstate = SSL_X509_LOOKUP;
+      return -1;
+    }
+
+    int setup_error = ret == 1 && (!SSL_use_certificate(ssl, x509) ||
+                                   !SSL_use_PrivateKey(ssl, pkey));
+    X509_free(x509);
+    EVP_PKEY_free(pkey);
+    if (setup_error) {
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
+      return -1;
+    }
+
+    ssl->state = SSL3_ST_CW_CERT_C;
+  }
+
+  if (ssl->state == SSL3_ST_CW_CERT_C) {
+    if (!ssl3_has_client_certificate(ssl)) {
+      /* Without a client certificate, the handshake buffer may be released. */
+      ssl3_free_handshake_buffer(ssl);
+
+      if (ssl->version == SSL3_VERSION) {
+        /* In SSL 3.0, send no certificate by skipping both messages. */
+        ssl->s3->tmp.cert_req = 0;
+        ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
+        return 1;
+      }
+
+      /* In TLS, send an empty Certificate message. */
+      ssl->s3->tmp.cert_req = 2;
+      uint8_t *p = ssl_handshake_start(ssl);
+      l2n3(0, p);
+      if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) {
+        return -1;
+      }
+    } else if (!ssl3_output_cert_chain(ssl)) {
+      return -1;
+    }
+    ssl->state = SSL3_ST_CW_CERT_D;
+  }
+
+  assert(ssl->state == SSL3_ST_CW_CERT_D);
+  return ssl_do_write(ssl);
+}
+
 OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
                        SIZE_T_IS_SMALLER_THAN_UNSIGNED);
 
-int ssl3_send_client_key_exchange(SSL *ssl) {
+static int ssl3_send_client_key_exchange(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_KEY_EXCH_B) {
     return ssl_do_write(ssl);
   }
@@ -1729,7 +1832,7 @@
   return -1;
 }
 
-int ssl3_send_cert_verify(SSL *ssl) {
+static int ssl3_send_cert_verify(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_CERT_VRFY_C) {
     return ssl_do_write(ssl);
   }
@@ -1818,86 +1921,7 @@
   return -1;
 }
 
-/* ssl3_has_client_certificate returns true if a client certificate is
- * configured. */
-static int ssl3_has_client_certificate(SSL *ssl) {
-  return ssl->cert && ssl->cert->x509 && ssl_has_private_key(ssl);
-}
-
-int ssl3_send_client_certificate(SSL *ssl) {
-  if (ssl->state == SSL3_ST_CW_CERT_A) {
-    /* Call cert_cb to update the certificate. */
-    if (ssl->cert->cert_cb) {
-      int ret = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
-      if (ret < 0) {
-        ssl->rwstate = SSL_X509_LOOKUP;
-        return -1;
-      }
-      if (ret == 0) {
-        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-        return -1;
-      }
-    }
-
-    if (ssl3_has_client_certificate(ssl)) {
-      ssl->state = SSL3_ST_CW_CERT_C;
-    } else {
-      ssl->state = SSL3_ST_CW_CERT_B;
-    }
-  }
-
-  if (ssl->state == SSL3_ST_CW_CERT_B) {
-    /* Call client_cert_cb to update the certificate. */
-    X509 *x509 = NULL;
-    EVP_PKEY *pkey = NULL;
-    int ret = ssl_do_client_cert_cb(ssl, &x509, &pkey);
-    if (ret < 0) {
-      ssl->rwstate = SSL_X509_LOOKUP;
-      return -1;
-    }
-
-    int setup_error = ret == 1 && (!SSL_use_certificate(ssl, x509) ||
-                                   !SSL_use_PrivateKey(ssl, pkey));
-    X509_free(x509);
-    EVP_PKEY_free(pkey);
-    if (setup_error) {
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return -1;
-    }
-
-    ssl->state = SSL3_ST_CW_CERT_C;
-  }
-
-  if (ssl->state == SSL3_ST_CW_CERT_C) {
-    if (!ssl3_has_client_certificate(ssl)) {
-      /* Without a client certificate, the handshake buffer may be released. */
-      ssl3_free_handshake_buffer(ssl);
-
-      if (ssl->version == SSL3_VERSION) {
-        /* In SSL 3.0, send no certificate by skipping both messages. */
-        ssl->s3->tmp.cert_req = 0;
-        ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
-        return 1;
-      }
-
-      /* In TLS, send an empty Certificate message. */
-      ssl->s3->tmp.cert_req = 2;
-      uint8_t *p = ssl_handshake_start(ssl);
-      l2n3(0, p);
-      if (!ssl_set_handshake_header(ssl, SSL3_MT_CERTIFICATE, 3)) {
-        return -1;
-      }
-    } else if (!ssl3_output_cert_chain(ssl)) {
-      return -1;
-    }
-    ssl->state = SSL3_ST_CW_CERT_D;
-  }
-
-  assert(ssl->state == SSL3_ST_CW_CERT_D);
-  return ssl_do_write(ssl);
-}
-
-int ssl3_send_next_proto(SSL *ssl) {
+static int ssl3_send_next_proto(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_NEXT_PROTO_B) {
     return ssl_do_write(ssl);
   }
@@ -1928,7 +1952,7 @@
   return ssl_do_write(ssl);
 }
 
-int ssl3_send_channel_id(SSL *ssl) {
+static int ssl3_send_channel_id(SSL *ssl) {
   if (ssl->state == SSL3_ST_CW_CHANNEL_ID_B) {
     return ssl_do_write(ssl);
   }
@@ -2009,31 +2033,75 @@
   return ret;
 }
 
-int ssl_do_client_cert_cb(SSL *ssl, X509 **out_x509, EVP_PKEY **out_pkey) {
-  if (ssl->ctx->client_cert_cb == NULL) {
-    return 0;
+static int ssl3_get_new_session_ticket(SSL *ssl) {
+  int ok, al;
+  long n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
+                                        ssl_hash_message, &ok);
+
+  if (!ok) {
+    return n;
   }
 
-  int ret = ssl->ctx->client_cert_cb(ssl, out_x509, out_pkey);
-  if (ret <= 0) {
-    return ret;
+  CBS new_session_ticket, ticket;
+  uint32_t ticket_lifetime_hint;
+  CBS_init(&new_session_ticket, ssl->init_msg, n);
+  if (!CBS_get_u32(&new_session_ticket, &ticket_lifetime_hint) ||
+      !CBS_get_u16_length_prefixed(&new_session_ticket, &ticket) ||
+      CBS_len(&new_session_ticket) != 0) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
   }
 
-  assert(*out_x509 != NULL);
-  assert(*out_pkey != NULL);
+  if (CBS_len(&ticket) == 0) {
+    /* RFC 5077 allows a server to change its mind and send no ticket after
+     * negotiating the extension. The value of |tlsext_ticket_expected| is
+     * checked in |ssl_update_cache| so is cleared here to avoid an unnecessary
+     * update. */
+    ssl->tlsext_ticket_expected = 0;
+    return 1;
+  }
+
+  if (ssl->hit) {
+    /* The server is sending a new ticket for an existing session. Sessions are
+     * immutable once established, so duplicate all but the ticket of the
+     * existing session. */
+    uint8_t *bytes;
+    size_t bytes_len;
+    if (!SSL_SESSION_to_bytes_for_ticket(ssl->session, &bytes, &bytes_len)) {
+      goto err;
+    }
+    SSL_SESSION *new_session = SSL_SESSION_from_bytes(bytes, bytes_len);
+    OPENSSL_free(bytes);
+    if (new_session == NULL) {
+      /* This should never happen. */
+      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+      goto err;
+    }
+
+    SSL_SESSION_free(ssl->session);
+    ssl->session = new_session;
+  }
+
+  if (!CBS_stow(&ticket, &ssl->session->tlsext_tick,
+                &ssl->session->tlsext_ticklen)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+  ssl->session->tlsext_tick_lifetime_hint = ticket_lifetime_hint;
+
+  /* Generate a session ID for this session based on the session ticket. We use
+   * the session ID mechanism for detecting ticket resumption. This also fits in
+   * with assumptions elsewhere in OpenSSL.*/
+  if (!EVP_Digest(CBS_data(&ticket), CBS_len(&ticket), ssl->session->session_id,
+                  &ssl->session->session_id_length, EVP_sha256(), NULL)) {
+    goto err;
+  }
+
   return 1;
-}
 
-int ssl3_verify_server_cert(SSL *ssl) {
-  int ret = ssl_verify_cert_chain(ssl, ssl->session->cert_chain);
-  if (ssl->verify_mode != SSL_VERIFY_NONE && ret <= 0) {
-    int al = ssl_verify_alarm_type(ssl->verify_result);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
-  } else {
-    ret = 1;
-    ERR_clear_error(); /* but we keep ssl->verify_result */
-  }
-
-  return ret;
+f_err:
+  ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+err:
+  return -1;
 }
diff --git a/src/ssl/s3_srvr.c b/src/ssl/handshake_server.c
similarity index 95%
rename from src/ssl/s3_srvr.c
rename to src/ssl/handshake_server.c
index 50007eb..68a013e 100644
--- a/src/ssl/s3_srvr.c
+++ b/src/ssl/handshake_server.c
@@ -173,38 +173,37 @@
 #include "../crypto/dh/internal.h"
 
 
+static int ssl3_get_initial_bytes(SSL *ssl);
+static int ssl3_get_v2_client_hello(SSL *ssl);
+static int ssl3_get_client_hello(SSL *ssl);
+static int ssl3_send_server_hello(SSL *ssl);
+static int ssl3_send_server_certificate(SSL *ssl);
+static int ssl3_send_certificate_status(SSL *ssl);
+static int ssl3_send_server_key_exchange(SSL *ssl);
+static int ssl3_send_certificate_request(SSL *ssl);
+static int ssl3_send_server_hello_done(SSL *ssl);
+static int ssl3_get_client_certificate(SSL *ssl);
+static int ssl3_get_client_key_exchange(SSL *ssl);
+static int ssl3_get_cert_verify(SSL *ssl);
+static int ssl3_get_next_proto(SSL *ssl);
+static int ssl3_get_channel_id(SSL *ssl);
+static int ssl3_send_new_session_ticket(SSL *ssl);
+
 int ssl3_accept(SSL *ssl) {
   BUF_MEM *buf = NULL;
   uint32_t alg_a;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
   int ret = -1;
-  int new_state, state, skip = 0;
+  int state, skip = 0;
 
   assert(ssl->handshake_func == ssl3_accept);
   assert(ssl->server);
-  assert(!SSL_IS_DTLS(ssl));
-
-  ERR_clear_system_error();
-
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
-
-  if (ssl->cert == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return -1;
-  }
 
   for (;;) {
     state = ssl->state;
 
     switch (ssl->state) {
       case SSL_ST_ACCEPT:
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_START, 1);
-        }
+        ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
         if (ssl->init_buf == NULL) {
           buf = BUF_MEM_new();
@@ -230,7 +229,7 @@
           goto end;
         }
 
-        if (!ssl->s3->have_version) {
+        if (!ssl->s3->have_version && !SSL_IS_DTLS(ssl)) {
           ssl->state = SSL3_ST_SR_INITIAL_BYTES;
         } else {
           ssl->state = SSL3_ST_SR_CLNT_HELLO_A;
@@ -238,6 +237,7 @@
         break;
 
       case SSL3_ST_SR_INITIAL_BYTES:
+        assert(!SSL_IS_DTLS(ssl));
         ret = ssl3_get_initial_bytes(ssl);
         if (ret <= 0) {
           goto end;
@@ -247,6 +247,7 @@
         break;
 
       case SSL3_ST_SR_V2_CLIENT_HELLO:
+        assert(!SSL_IS_DTLS(ssl));
         ret = ssl3_get_v2_client_hello(ssl);
         if (ret <= 0) {
           goto end;
@@ -261,6 +262,7 @@
         if (ret <= 0) {
           goto end;
         }
+        ssl->method->received_flight(ssl);
         ssl->state = SSL3_ST_SW_SRVR_HELLO_A;
         break;
 
@@ -313,13 +315,7 @@
       case SSL3_ST_SW_KEY_EXCH_C:
         alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
 
-        /* Send a ServerKeyExchange message if:
-         * - The key exchange is ephemeral or anonymous
-         *   Diffie-Hellman.
-         * - There is a PSK identity hint.
-         *
-         * TODO(davidben): This logic is currently duplicated in d1_srvr.c. Fix
-         * this. In the meantime, keep them in sync. */
+        /* PSK ciphers send ServerKeyExchange if there is an identity hint. */
         if (ssl_cipher_requires_server_key_exchange(ssl->s3->tmp.new_cipher) ||
             ((alg_a & SSL_aPSK) && ssl->psk_identity_hint)) {
           ret = ssl3_send_server_key_exchange(ssl);
@@ -348,7 +344,7 @@
 
       case SSL3_ST_SW_SRVR_DONE_A:
       case SSL3_ST_SW_SRVR_DONE_B:
-        ret = ssl3_send_server_done(ssl);
+        ret = ssl3_send_server_hello_done(ssl);
         if (ret <= 0) {
           goto end;
         }
@@ -356,21 +352,6 @@
         ssl->state = SSL3_ST_SW_FLUSH;
         break;
 
-      case SSL3_ST_SW_FLUSH:
-        /* This code originally checked to see if any data was pending using
-         * BIO_CTRL_INFO and then flushed. This caused problems as documented
-         * in PR#1939. The proposed fix doesn't completely resolve this issue
-         * as buggy implementations of BIO_CTRL_PENDING still exist. So instead
-         * we just flush unconditionally. */
-        if (BIO_flush(ssl->wbio) <= 0) {
-          ssl->rwstate = SSL_WRITING;
-          ret = -1;
-          goto end;
-        }
-
-        ssl->state = ssl->s3->tmp.next_state;
-        break;
-
       case SSL3_ST_SR_CERT_A:
         if (ssl->s3->tmp.cert_request) {
           ret = ssl3_get_client_certificate(ssl);
@@ -445,6 +426,7 @@
           goto end;
         }
 
+        ssl->method->received_flight(ssl);
         if (ssl->hit) {
           ssl->state = SSL_ST_OK;
         } else if (ssl->tlsext_ticket_expected) {
@@ -452,6 +434,7 @@
         } else {
           ssl->state = SSL3_ST_SW_CHANGE_A;
         }
+
         /* If this is a full handshake with ChannelID then record the hashshake
          * hashes in |ssl->session| in case we need them to verify a ChannelID
          * signature on a resumption of this session in the future. */
@@ -474,8 +457,8 @@
 
       case SSL3_ST_SW_CHANGE_A:
       case SSL3_ST_SW_CHANGE_B:
-        ret = ssl3_send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
-                                           SSL3_ST_SW_CHANGE_B);
+        ret = ssl->method->send_change_cipher_spec(ssl, SSL3_ST_SW_CHANGE_A,
+                                                   SSL3_ST_SW_CHANGE_B);
         if (ret <= 0) {
           goto end;
         }
@@ -502,18 +485,36 @@
         }
         break;
 
+      case SSL3_ST_SW_FLUSH:
+        if (BIO_flush(ssl->wbio) <= 0) {
+          ssl->rwstate = SSL_WRITING;
+          ret = -1;
+          goto end;
+        }
+
+        ssl->state = ssl->s3->tmp.next_state;
+        if (ssl->state != SSL_ST_OK) {
+          ssl->method->expect_flight(ssl);
+        }
+        break;
+
       case SSL_ST_OK:
         /* clean a few things up */
         ssl3_cleanup_key_block(ssl);
 
-        BUF_MEM_free(ssl->init_buf);
-        ssl->init_buf = NULL;
-        ssl->init_num = 0;
+        /* In DTLS, |init_buf| cannot be released because post-handshake
+         * retransmit relies on that buffer being available as scratch space.
+         *
+         * TODO(davidben): Fix this. */
+        if (!SSL_IS_DTLS(ssl)) {
+          BUF_MEM_free(ssl->init_buf);
+          ssl->init_buf = NULL;
+          ssl->init_num = 0;
+        }
 
         /* remove buffering on output */
         ssl_free_wbio_buffer(ssl);
 
-
         /* If we aren't retaining peer certificates then we can discard it
          * now. */
         if (ssl->ctx->retain_only_sha256_of_client_certs) {
@@ -523,13 +524,17 @@
           ssl->session->cert_chain = NULL;
         }
 
+        if (SSL_IS_DTLS(ssl)) {
+          ssl->d1->handshake_read_seq = 0;
+          ssl->d1->handshake_write_seq = 0;
+          ssl->d1->next_handshake_write_seq = 0;
+        }
+
         ssl->s3->initial_handshake_complete = 1;
 
         ssl_update_cache(ssl, SSL_SESS_CACHE_SERVER);
 
-        if (cb != NULL) {
-          cb(ssl, SSL_CB_HANDSHAKE_DONE, 1);
-        }
+        ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_DONE, 1);
 
         ret = 1;
         goto end;
@@ -540,11 +545,10 @@
         goto end;
     }
 
-    if (!ssl->s3->tmp.reuse_message && !skip && cb != NULL &&
-        ssl->state != state) {
-      new_state = ssl->state;
+    if (!ssl->s3->tmp.reuse_message && !skip && ssl->state != state) {
+      int new_state = ssl->state;
       ssl->state = state;
-      cb(ssl, SSL_CB_ACCEPT_LOOP, 1);
+      ssl_do_info_callback(ssl, SSL_CB_ACCEPT_LOOP, 1);
       ssl->state = new_state;
     }
     skip = 0;
@@ -552,13 +556,11 @@
 
 end:
   BUF_MEM_free(buf);
-  if (cb != NULL) {
-    cb(ssl, SSL_CB_ACCEPT_EXIT, ret);
-  }
+  ssl_do_info_callback(ssl, SSL_CB_ACCEPT_EXIT, ret);
   return ret;
 }
 
-int ssl3_get_initial_bytes(SSL *ssl) {
+static int ssl3_get_initial_bytes(SSL *ssl) {
   /* Read the first 5 bytes, the size of the TLS record header. This is
    * sufficient to detect a V2ClientHello and ensures that we never read beyond
    * the first record. */
@@ -597,7 +599,7 @@
   return 1;
 }
 
-int ssl3_get_v2_client_hello(SSL *ssl) {
+static int ssl3_get_v2_client_hello(SSL *ssl) {
   const uint8_t *p;
   int ret;
   CBS v2_client_hello, cipher_specs, session_id, challenge;
@@ -637,10 +639,9 @@
                                   CBS_len(&v2_client_hello))) {
     return -1;
   }
-  if (ssl->msg_callback) {
-    ssl->msg_callback(0, SSL2_VERSION, 0, CBS_data(&v2_client_hello),
-                    CBS_len(&v2_client_hello), ssl, ssl->msg_callback_arg);
-  }
+
+  ssl_do_msg_callback(ssl, 0 /* read */, SSL2_VERSION, 0,
+                      CBS_data(&v2_client_hello), CBS_len(&v2_client_hello));
 
   if (!CBS_get_u8(&v2_client_hello, &msg_type) ||
       !CBS_get_u16(&v2_client_hello, &version) ||
@@ -724,7 +725,7 @@
   return 1;
 }
 
-int ssl3_get_client_hello(SSL *ssl) {
+static int ssl3_get_client_hello(SSL *ssl) {
   int ok, al = SSL_AD_INTERNAL_ERROR, ret = -1;
   long n;
   const SSL_CIPHER *c;
@@ -1069,7 +1070,7 @@
   return ret;
 }
 
-int ssl3_send_server_hello(SSL *ssl) {
+static int ssl3_send_server_hello(SSL *ssl) {
   if (ssl->state == SSL3_ST_SW_SRVR_HELLO_B) {
     return ssl_do_write(ssl);
   }
@@ -1120,7 +1121,19 @@
   return ssl_do_write(ssl);
 }
 
-int ssl3_send_certificate_status(SSL *ssl) {
+static int ssl3_send_server_certificate(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_CERT_A) {
+    if (!ssl3_output_cert_chain(ssl)) {
+      return 0;
+    }
+    ssl->state = SSL3_ST_SW_CERT_B;
+  }
+
+  /* SSL3_ST_SW_CERT_B */
+  return ssl_do_write(ssl);
+}
+
+static int ssl3_send_certificate_status(SSL *ssl) {
   if (ssl->state == SSL3_ST_SW_CERT_STATUS_A) {
     CBB out, ocsp_response;
     size_t length;
@@ -1146,19 +1159,7 @@
   return ssl_do_write(ssl);
 }
 
-int ssl3_send_server_done(SSL *ssl) {
-  if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) {
-    if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_DONE, 0)) {
-      return -1;
-    }
-    ssl->state = SSL3_ST_SW_SRVR_DONE_B;
-  }
-
-  /* SSL3_ST_SW_SRVR_DONE_B */
-  return ssl_do_write(ssl);
-}
-
-int ssl3_send_server_key_exchange(SSL *ssl) {
+static int ssl3_send_server_key_exchange(SSL *ssl) {
   if (ssl->state == SSL3_ST_SW_KEY_EXCH_C) {
     return ssl_do_write(ssl);
   }
@@ -1346,7 +1347,7 @@
   return -1;
 }
 
-int ssl3_send_certificate_request(SSL *ssl) {
+static int ssl3_send_certificate_request(SSL *ssl) {
   uint8_t *p, *d;
   size_t i;
   int j, nl, off, n;
@@ -1414,7 +1415,158 @@
   return -1;
 }
 
-int ssl3_get_client_key_exchange(SSL *ssl) {
+static int ssl3_send_server_hello_done(SSL *ssl) {
+  if (ssl->state == SSL3_ST_SW_SRVR_DONE_A) {
+    if (!ssl_set_handshake_header(ssl, SSL3_MT_SERVER_HELLO_DONE, 0)) {
+      return -1;
+    }
+    ssl->state = SSL3_ST_SW_SRVR_DONE_B;
+  }
+
+  /* SSL3_ST_SW_SRVR_DONE_B */
+  return ssl_do_write(ssl);
+}
+
+static int ssl3_get_client_certificate(SSL *ssl) {
+  int ok, al, ret = -1;
+  X509 *x = NULL;
+  unsigned long n;
+  STACK_OF(X509) *sk = NULL;
+  SHA256_CTX sha256;
+  CBS certificate_msg, certificate_list;
+  int is_first_certificate = 1;
+
+  assert(ssl->s3->tmp.cert_request);
+  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
+
+  if (!ok) {
+    return n;
+  }
+
+  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
+    if (ssl->version == SSL3_VERSION &&
+        ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
+      /* In SSL 3.0, the Certificate message is omitted to signal no certificate. */
+      if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+          (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
+        al = SSL_AD_HANDSHAKE_FAILURE;
+        goto f_err;
+      }
+
+      ssl->s3->tmp.reuse_message = 1;
+      return 1;
+    }
+
+    al = SSL_AD_UNEXPECTED_MESSAGE;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    goto f_err;
+  }
+
+  CBS_init(&certificate_msg, ssl->init_msg, n);
+
+  sk = sk_X509_new_null();
+  if (sk == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (!CBS_get_u24_length_prefixed(&certificate_msg, &certificate_list) ||
+      CBS_len(&certificate_msg) != 0) {
+    al = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+    goto f_err;
+  }
+
+  while (CBS_len(&certificate_list) > 0) {
+    CBS certificate;
+    const uint8_t *data;
+
+    if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      goto f_err;
+    }
+
+    if (is_first_certificate && ssl->ctx->retain_only_sha256_of_client_certs) {
+      /* If this is the first certificate, and we don't want to keep peer
+       * certificates in memory, then we hash it right away. */
+      SHA256_Init(&sha256);
+      SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate));
+      SHA256_Final(ssl->session->peer_sha256, &sha256);
+      ssl->session->peer_sha256_valid = 1;
+    }
+    is_first_certificate = 0;
+
+    /* A u24 length cannot overflow a long. */
+    data = CBS_data(&certificate);
+    x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
+    if (x == NULL) {
+      al = SSL_AD_BAD_CERTIFICATE;
+      OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
+      goto f_err;
+    }
+    if (data != CBS_data(&certificate) + CBS_len(&certificate)) {
+      al = SSL_AD_DECODE_ERROR;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
+      goto f_err;
+    }
+    if (!sk_X509_push(sk, x)) {
+      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+      goto err;
+    }
+    x = NULL;
+  }
+
+  if (sk_X509_num(sk) <= 0) {
+    /* No client certificate so the handshake buffer may be discarded. */
+    ssl3_free_handshake_buffer(ssl);
+
+    /* TLS does not mind 0 certs returned */
+    if (ssl->version == SSL3_VERSION) {
+      al = SSL_AD_HANDSHAKE_FAILURE;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
+      goto f_err;
+    } else if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
+               (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
+      /* Fail for TLS only if we required a certificate */
+      OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
+      al = SSL_AD_HANDSHAKE_FAILURE;
+      goto f_err;
+    }
+  } else {
+    if (ssl_verify_cert_chain(ssl, sk) <= 0) {
+      al = ssl_verify_alarm_type(ssl->verify_result);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
+      goto f_err;
+    }
+  }
+
+  X509_free(ssl->session->peer);
+  ssl->session->peer = sk_X509_shift(sk);
+  ssl->session->verify_result = ssl->verify_result;
+
+  sk_X509_pop_free(ssl->session->cert_chain, X509_free);
+  ssl->session->cert_chain = sk;
+  /* Inconsistency alert: cert_chain does *not* include the peer's own
+   * certificate, while we do include it in s3_clnt.c */
+
+  sk = NULL;
+
+  ret = 1;
+
+  if (0) {
+  f_err:
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
+  }
+
+err:
+  X509_free(x);
+  sk_X509_pop_free(sk, X509_free);
+  return ret;
+}
+
+static int ssl3_get_client_key_exchange(SSL *ssl) {
   int al;
   CBS client_key_exchange;
   uint32_t alg_k;
@@ -1683,7 +1835,7 @@
   return -1;
 }
 
-int ssl3_get_cert_verify(SSL *ssl) {
+static int ssl3_get_cert_verify(SSL *ssl) {
   int al, ok, ret = 0;
   long n;
   CBS certificate_verify, signature;
@@ -1789,159 +1941,159 @@
   return ret;
 }
 
-int ssl3_get_client_certificate(SSL *ssl) {
-  int ok, al, ret = -1;
-  X509 *x = NULL;
-  unsigned long n;
-  STACK_OF(X509) *sk = NULL;
-  SHA256_CTX sha256;
-  CBS certificate_msg, certificate_list;
-  int is_first_certificate = 1;
+/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
+ * sets the next_proto member in s if found */
+static int ssl3_get_next_proto(SSL *ssl) {
+  int ok;
+  long n;
+  CBS next_protocol, selected_protocol, padding;
 
-  assert(ssl->s3->tmp.cert_request);
-  n = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message, &ok);
+  /* Clients cannot send a NextProtocol message if we didn't see the extension
+   * in their ClientHello */
+  if (!ssl->s3->next_proto_neg_seen) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION);
+    return -1;
+  }
+
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message,
+                                   &ok);
 
   if (!ok) {
     return n;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE) {
-    if (ssl->version == SSL3_VERSION &&
-        ssl->s3->tmp.message_type == SSL3_MT_CLIENT_KEY_EXCHANGE) {
-      /* In SSL 3.0, the Certificate message is omitted to signal no certificate. */
-      if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
-          (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
-        OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-        al = SSL_AD_HANDSHAKE_FAILURE;
-        goto f_err;
-      }
+  CBS_init(&next_protocol, ssl->init_msg, n);
 
-      ssl->s3->tmp.reuse_message = 1;
-      return 1;
-    }
-
-    al = SSL_AD_UNEXPECTED_MESSAGE;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    goto f_err;
+  /* The payload looks like:
+   *   uint8 proto_len;
+   *   uint8 proto[proto_len];
+   *   uint8 padding_len;
+   *   uint8 padding[padding_len]; */
+  if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
+      !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
+      CBS_len(&next_protocol) != 0 ||
+      !CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
+                &ssl->s3->next_proto_negotiated_len)) {
+    return 0;
   }
 
-  CBS_init(&certificate_msg, ssl->init_msg, n);
+  return 1;
+}
 
-  sk = sk_X509_new_null();
-  if (sk == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
+static int ssl3_get_channel_id(SSL *ssl) {
+  int ret = -1, ok;
+  long n;
+  uint8_t channel_id_hash[EVP_MAX_MD_SIZE];
+  size_t channel_id_hash_len;
+  const uint8_t *p;
+  uint16_t extension_type;
+  EC_GROUP *p256 = NULL;
+  EC_KEY *key = NULL;
+  EC_POINT *point = NULL;
+  ECDSA_SIG sig;
+  BIGNUM x, y;
+  CBS encrypted_extensions, extension;
+
+  n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
+                                   ssl_dont_hash_message, &ok);
+
+  if (!ok) {
+    return n;
+  }
+
+  /* Before incorporating the EncryptedExtensions message to the handshake
+   * hash, compute the hash that should have been signed. */
+  if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) {
+    return -1;
+  }
+  assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
+
+  if (!ssl3_hash_current_message(ssl)) {
+    return -1;
+  }
+
+  CBS_init(&encrypted_extensions, ssl->init_msg, n);
+
+  /* EncryptedExtensions could include multiple extensions, but the only
+   * extension that could be negotiated is ChannelID, so there can only be one
+   * entry.
+   *
+   * The payload looks like:
+   *   uint16 extension_type
+   *   uint16 extension_len;
+   *   uint8 x[32];
+   *   uint8 y[32];
+   *   uint8 r[32];
+   *   uint8 s[32]; */
+
+  if (!CBS_get_u16(&encrypted_extensions, &extension_type) ||
+      !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) ||
+      CBS_len(&encrypted_extensions) != 0 ||
+      extension_type != TLSEXT_TYPE_channel_id ||
+      CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_MESSAGE);
+    return -1;
+  }
+
+  p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
+  if (!p256) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
+    return -1;
+  }
+
+  BN_init(&x);
+  BN_init(&y);
+  sig.r = BN_new();
+  sig.s = BN_new();
+  if (sig.r == NULL || sig.s == NULL) {
     goto err;
   }
 
-  if (!CBS_get_u24_length_prefixed(&certificate_msg, &certificate_list) ||
-      CBS_len(&certificate_msg) != 0) {
-    al = SSL_AD_DECODE_ERROR;
-    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-    goto f_err;
+  p = CBS_data(&extension);
+  if (BN_bin2bn(p + 0, 32, &x) == NULL ||
+      BN_bin2bn(p + 32, 32, &y) == NULL ||
+      BN_bin2bn(p + 64, 32, sig.r) == NULL ||
+      BN_bin2bn(p + 96, 32, sig.s) == NULL) {
+    goto err;
   }
 
-  while (CBS_len(&certificate_list) > 0) {
-    CBS certificate;
-    const uint8_t *data;
-
-    if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate)) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
-      goto f_err;
-    }
-
-    if (is_first_certificate && ssl->ctx->retain_only_sha256_of_client_certs) {
-      /* If this is the first certificate, and we don't want to keep peer
-       * certificates in memory, then we hash it right away. */
-      SHA256_Init(&sha256);
-      SHA256_Update(&sha256, CBS_data(&certificate), CBS_len(&certificate));
-      SHA256_Final(ssl->session->peer_sha256, &sha256);
-      ssl->session->peer_sha256_valid = 1;
-    }
-    is_first_certificate = 0;
-
-    /* A u24 length cannot overflow a long. */
-    data = CBS_data(&certificate);
-    x = d2i_X509(NULL, &data, (long)CBS_len(&certificate));
-    if (x == NULL) {
-      al = SSL_AD_BAD_CERTIFICATE;
-      OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-      goto f_err;
-    }
-    if (data != CBS_data(&certificate) + CBS_len(&certificate)) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
-      goto f_err;
-    }
-    if (!sk_X509_push(sk, x)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-    x = NULL;
+  point = EC_POINT_new(p256);
+  if (!point ||
+      !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
+    goto err;
   }
 
-  if (sk_X509_num(sk) <= 0) {
-    /* No client certificate so the handshake buffer may be discarded. */
-    ssl3_free_handshake_buffer(ssl);
-
-    /* TLS does not mind 0 certs returned */
-    if (ssl->version == SSL3_VERSION) {
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATES_RETURNED);
-      goto f_err;
-    } else if ((ssl->verify_mode & SSL_VERIFY_PEER) &&
-               (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT)) {
-      /* Fail for TLS only if we required a certificate */
-      OPENSSL_PUT_ERROR(SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE);
-      al = SSL_AD_HANDSHAKE_FAILURE;
-      goto f_err;
-    }
-  } else {
-    if (ssl_verify_cert_chain(ssl, sk) <= 0) {
-      al = ssl_verify_alarm_type(ssl->verify_result);
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CERTIFICATE_VERIFY_FAILED);
-      goto f_err;
-    }
+  key = EC_KEY_new();
+  if (!key || !EC_KEY_set_group(key, p256) ||
+      !EC_KEY_set_public_key(key, point)) {
+    goto err;
   }
 
-  X509_free(ssl->session->peer);
-  ssl->session->peer = sk_X509_shift(sk);
-  ssl->session->verify_result = ssl->verify_result;
+  /* We stored the handshake hash in |tlsext_channel_id| the first time that we
+   * were called. */
+  if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
+    ssl->s3->tlsext_channel_id_valid = 0;
+    goto err;
+  }
 
-  sk_X509_pop_free(ssl->session->cert_chain, X509_free);
-  ssl->session->cert_chain = sk;
-  /* Inconsistency alert: cert_chain does *not* include the peer's own
-   * certificate, while we do include it in s3_clnt.c */
-
-  sk = NULL;
-
+  memcpy(ssl->s3->tlsext_channel_id, p, 64);
   ret = 1;
 
-  if (0) {
-  f_err:
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-  }
-
 err:
-  X509_free(x);
-  sk_X509_pop_free(sk, X509_free);
+  BN_free(&x);
+  BN_free(&y);
+  BN_free(sig.r);
+  BN_free(sig.s);
+  EC_KEY_free(key);
+  EC_POINT_free(point);
+  EC_GROUP_free(p256);
   return ret;
 }
 
-int ssl3_send_server_certificate(SSL *ssl) {
-  if (ssl->state == SSL3_ST_SW_CERT_A) {
-    if (!ssl3_output_cert_chain(ssl)) {
-      return 0;
-    }
-    ssl->state = SSL3_ST_SW_CERT_B;
-  }
-
-  /* SSL3_ST_SW_CERT_B */
-  return ssl_do_write(ssl);
-}
-
 /* send a new session ticket (not necessarily for a new session) */
-int ssl3_send_new_session_ticket(SSL *ssl) {
+static int ssl3_send_new_session_ticket(SSL *ssl) {
   int ret = -1;
   uint8_t *session = NULL;
   size_t session_len;
@@ -2071,154 +2223,3 @@
   HMAC_CTX_cleanup(&hctx);
   return ret;
 }
-
-/* ssl3_get_next_proto reads a Next Protocol Negotiation handshake message. It
- * sets the next_proto member in s if found */
-int ssl3_get_next_proto(SSL *ssl) {
-  int ok;
-  long n;
-  CBS next_protocol, selected_protocol, padding;
-
-  /* Clients cannot send a NextProtocol message if we didn't see the extension
-   * in their ClientHello */
-  if (!ssl->s3->next_proto_neg_seen) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_GOT_NEXT_PROTO_WITHOUT_EXTENSION);
-    return -1;
-  }
-
-  n = ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message,
-                                   &ok);
-
-  if (!ok) {
-    return n;
-  }
-
-  CBS_init(&next_protocol, ssl->init_msg, n);
-
-  /* The payload looks like:
-   *   uint8 proto_len;
-   *   uint8 proto[proto_len];
-   *   uint8 padding_len;
-   *   uint8 padding[padding_len]; */
-  if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
-      !CBS_get_u8_length_prefixed(&next_protocol, &padding) ||
-      CBS_len(&next_protocol) != 0 ||
-      !CBS_stow(&selected_protocol, &ssl->s3->next_proto_negotiated,
-                &ssl->s3->next_proto_negotiated_len)) {
-    return 0;
-  }
-
-  return 1;
-}
-
-/* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
-int ssl3_get_channel_id(SSL *ssl) {
-  int ret = -1, ok;
-  long n;
-  uint8_t channel_id_hash[EVP_MAX_MD_SIZE];
-  size_t channel_id_hash_len;
-  const uint8_t *p;
-  uint16_t extension_type;
-  EC_GROUP *p256 = NULL;
-  EC_KEY *key = NULL;
-  EC_POINT *point = NULL;
-  ECDSA_SIG sig;
-  BIGNUM x, y;
-  CBS encrypted_extensions, extension;
-
-  n = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID_ENCRYPTED_EXTENSIONS,
-                                   ssl_dont_hash_message, &ok);
-
-  if (!ok) {
-    return n;
-  }
-
-  /* Before incorporating the EncryptedExtensions message to the handshake
-   * hash, compute the hash that should have been signed. */
-  if (!tls1_channel_id_hash(ssl, channel_id_hash, &channel_id_hash_len)) {
-    return -1;
-  }
-  assert(channel_id_hash_len == SHA256_DIGEST_LENGTH);
-
-  if (!ssl3_hash_current_message(ssl)) {
-    return -1;
-  }
-
-  CBS_init(&encrypted_extensions, ssl->init_msg, n);
-
-  /* EncryptedExtensions could include multiple extensions, but the only
-   * extension that could be negotiated is ChannelID, so there can only be one
-   * entry.
-   *
-   * The payload looks like:
-   *   uint16 extension_type
-   *   uint16 extension_len;
-   *   uint8 x[32];
-   *   uint8 y[32];
-   *   uint8 r[32];
-   *   uint8 s[32]; */
-
-  if (!CBS_get_u16(&encrypted_extensions, &extension_type) ||
-      !CBS_get_u16_length_prefixed(&encrypted_extensions, &extension) ||
-      CBS_len(&encrypted_extensions) != 0 ||
-      extension_type != TLSEXT_TYPE_channel_id ||
-      CBS_len(&extension) != TLSEXT_CHANNEL_ID_SIZE) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_MESSAGE);
-    return -1;
-  }
-
-  p256 = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1);
-  if (!p256) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_P256_SUPPORT);
-    return -1;
-  }
-
-  BN_init(&x);
-  BN_init(&y);
-  sig.r = BN_new();
-  sig.s = BN_new();
-  if (sig.r == NULL || sig.s == NULL) {
-    goto err;
-  }
-
-  p = CBS_data(&extension);
-  if (BN_bin2bn(p + 0, 32, &x) == NULL ||
-      BN_bin2bn(p + 32, 32, &y) == NULL ||
-      BN_bin2bn(p + 64, 32, sig.r) == NULL ||
-      BN_bin2bn(p + 96, 32, sig.s) == NULL) {
-    goto err;
-  }
-
-  point = EC_POINT_new(p256);
-  if (!point ||
-      !EC_POINT_set_affine_coordinates_GFp(p256, point, &x, &y, NULL)) {
-    goto err;
-  }
-
-  key = EC_KEY_new();
-  if (!key || !EC_KEY_set_group(key, p256) ||
-      !EC_KEY_set_public_key(key, point)) {
-    goto err;
-  }
-
-  /* We stored the handshake hash in |tlsext_channel_id| the first time that we
-   * were called. */
-  if (!ECDSA_do_verify(channel_id_hash, channel_id_hash_len, &sig, key)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_SIGNATURE_INVALID);
-    ssl->s3->tlsext_channel_id_valid = 0;
-    goto err;
-  }
-
-  memcpy(ssl->s3->tlsext_channel_id, p, 64);
-  ret = 1;
-
-err:
-  BN_free(&x);
-  BN_free(&y);
-  BN_free(sig.r);
-  BN_free(sig.s);
-  EC_KEY_free(key);
-  EC_POINT_free(point);
-  EC_GROUP_free(p256);
-  return ret;
-}
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 3b56e78..457a8b4 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -151,9 +151,9 @@
 
 #if defined(OPENSSL_WINDOWS)
 /* Windows defines struct timeval in winsock2.h. */
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #else
 #include <sys/time.h>
 #endif
@@ -313,21 +313,19 @@
  * |SSL_AEAD_CTX_seal|. |ctx| may be NULL to denote the null cipher. */
 size_t SSL_AEAD_CTX_max_overhead(SSL_AEAD_CTX *ctx);
 
-/* SSL_AEAD_CTX_open authenticates and decrypts |in_len| bytes from |in| and
- * writes the result to |out|. It returns one on success and zero on
- * error. |ctx| may be NULL to denote the null cipher.
- *
- * If |in| and |out| alias then |out| must be <= |in| + |explicit_nonce_len|. */
-int SSL_AEAD_CTX_open(SSL_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
-                      size_t max_out, uint8_t type, uint16_t wire_version,
-                      const uint8_t seqnum[8], const uint8_t *in,
-                      size_t in_len);
+/* SSL_AEAD_CTX_open authenticates and decrypts |in_len| bytes from |in|
+ * in-place. On success, it sets |*out| to the plaintext in |in| and returns
+ * one. Otherwise, it returns zero. |ctx| may be NULL to denote the null cipher.
+ * The output will always be |explicit_nonce_len| bytes ahead of |in|. */
+int SSL_AEAD_CTX_open(SSL_AEAD_CTX *ctx, CBS *out, uint8_t type,
+                      uint16_t wire_version, const uint8_t seqnum[8],
+                      uint8_t *in, size_t in_len);
 
 /* SSL_AEAD_CTX_seal encrypts and authenticates |in_len| bytes from |in| and
  * writes the result to |out|. It returns one on success and zero on
  * error. |ctx| may be NULL to denote the null cipher.
  *
- * If |in| and |out| alias then |out| + |explicit_nonce_len| must be <= |in| */
+ * If |in| and |out| alias then |out| + |explicit_nonce_len| must be == |in|. */
 int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *ctx, uint8_t *out, size_t *out_len,
                       size_t max_out, uint8_t type, uint16_t wire_version,
                       const uint8_t seqnum[8], const uint8_t *in,
@@ -365,52 +363,60 @@
   ssl_open_record_success,
   ssl_open_record_discard,
   ssl_open_record_partial,
+  ssl_open_record_close_notify,
+  ssl_open_record_fatal_alert,
   ssl_open_record_error,
 };
 
-/* tls_open_record decrypts a record from |in|.
- *
- * On success, it returns |ssl_open_record_success|. It sets |*out_type| to the
- * record type, |*out_len| to the plaintext length, and writes the record body
- * to |out|. It sets |*out_consumed| to the number of bytes of |in| consumed.
- * Note that |*out_len| may be zero.
- *
- * If a record was successfully processed but should be discarded, it returns
- * |ssl_open_record_discard| and sets |*out_consumed| to the number of bytes
- * consumed.
+/* tls_open_record decrypts a record from |in| in-place.
  *
  * If the input did not contain a complete record, it returns
  * |ssl_open_record_partial|. It sets |*out_consumed| to the total number of
  * bytes necessary. It is guaranteed that a successful call to |tls_open_record|
  * will consume at least that many bytes.
  *
- * On failure, it returns |ssl_open_record_error| and sets |*out_alert| to an
- * alert to emit.
+ * Otherwise, it sets |*out_consumed| to the number of bytes of input
+ * consumed. Note that input may be consumed on all return codes if a record was
+ * decrypted.
  *
- * If |in| and |out| alias, |out| must be <= |in| + |ssl_record_prefix_len|. */
-enum ssl_open_record_t tls_open_record(
-    SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len,
-    size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in,
-    size_t in_len);
+ * On success, it returns |ssl_open_record_success|. It sets |*out_type| to the
+ * record type and |*out| to the record body in |in|. Note that |*out| may be
+ * empty.
+ *
+ * If a record was successfully processed but should be discarded, it returns
+ * |ssl_open_record_discard|.
+ *
+ * If a record was successfully processed but is a close_notify or fatal alert,
+ * it returns |ssl_open_record_close_notify| or |ssl_open_record_fatal_alert|.
+ *
+ * On failure, it returns |ssl_open_record_error| and sets |*out_alert| to an
+ * alert to emit. */
+enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+                                       size_t *out_consumed, uint8_t *out_alert,
+                                       uint8_t *in, size_t in_len);
 
 /* dtls_open_record implements |tls_open_record| for DTLS. It never returns
  * |ssl_open_record_partial| but otherwise behaves analogously. */
-enum ssl_open_record_t dtls_open_record(
-    SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len,
-    size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in,
-    size_t in_len);
+enum ssl_open_record_t dtls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+                                        size_t *out_consumed,
+                                        uint8_t *out_alert, uint8_t *in,
+                                        size_t in_len);
 
-/* ssl_seal_prefix_len returns the length of the prefix before the ciphertext
- * when sealing a record with |ssl|. Note that this value may differ from
- * |ssl_record_prefix_len| when TLS 1.0 CBC record-splitting is enabled. Sealing
- * a small record may also result in a smaller output than this value.
+/* ssl_seal_align_prefix_len returns the length of the prefix before the start
+ * of the bulk of the ciphertext when sealing a record with |ssl|. Callers may
+ * use this to align buffers.
+ *
+ * Note when TLS 1.0 CBC record-splitting is enabled, this includes the one byte
+ * record and is the offset into second record's ciphertext. Thus this value may
+ * differ from |ssl_record_prefix_len| and sealing a small record may result in
+ * a smaller output than this value.
  *
  * TODO(davidben): Expose this as part of public API once the high-level
  * buffer-free APIs are available. */
-size_t ssl_seal_prefix_len(const SSL *ssl);
+size_t ssl_seal_align_prefix_len(const SSL *ssl);
 
 /* ssl_max_seal_overhead returns the maximum overhead of sealing a record with
- * |ssl|. This includes |ssl_seal_prefix_len|.
+ * |ssl|.
  *
  * TODO(davidben): Expose this as part of public API once the high-level
  * buffer-free APIs are available. */
@@ -421,11 +427,12 @@
  * and zero on error. If enabled, |tls_seal_record| implements TLS 1.0 CBC 1/n-1
  * record splitting and may write two records concatenated.
  *
- * For a large record, the ciphertext will begin |ssl_seal_prefix_len| bytes
- * into out. Aligning |out| appropriately may improve performance. It writes at
- * most |in_len| + |ssl_max_seal_overhead| bytes to |out|.
+ * For a large record, the bulk of the ciphertext will begin
+ * |ssl_seal_align_prefix_len| bytes into out. Aligning |out| appropriately may
+ * improve performance. It writes at most |in_len| + |ssl_max_seal_overhead|
+ * bytes to |out|.
  *
- * If |in| and |out| alias, |out| + |ssl_seal_prefix_len| must be <= |in|. */
+ * |in| and |out| may not alias. */
 int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
                     uint8_t type, const uint8_t *in, size_t in_len);
 
@@ -448,6 +455,13 @@
  * ownership of |aead_ctx|. */
 void ssl_set_write_state(SSL *ssl, SSL_AEAD_CTX *aead_ctx);
 
+/* ssl_process_alert processes |in| as an alert and updates |ssl|'s shutdown
+ * state. It returns one of |ssl_open_record_discard|, |ssl_open_record_error|,
+ * |ssl_open_record_close_notify|, or |ssl_open_record_fatal_alert| as
+ * appropriate. */
+enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
+                                         const uint8_t *in, size_t in_len);
+
 
 /* Private key operations. */
 
@@ -626,6 +640,16 @@
 size_t ssl_max_handshake_message_len(const SSL *ssl);
 
 
+/* Callbacks. */
+
+/* ssl_do_info_callback calls |ssl|'s info callback, if set. */
+void ssl_do_info_callback(const SSL *ssl, int type, int value);
+
+/* ssl_do_msg_callback calls |ssl|'s message callback, if set. */
+void ssl_do_msg_callback(SSL *ssl, int is_write, int version, int content_type,
+                         const void *buf, size_t len);
+
+
 /* Transport buffers. */
 
 /* ssl_read_buffer returns a pointer to contents of the read buffer. */
@@ -685,100 +709,17 @@
  *
  * Functions below here haven't been touched up and may be underdocumented. */
 
-#define c2l(c, l)                                                            \
-  (l = ((unsigned long)(*((c)++))), l |= (((unsigned long)(*((c)++))) << 8), \
-   l |= (((unsigned long)(*((c)++))) << 16),                                 \
-   l |= (((unsigned long)(*((c)++))) << 24))
-
-/* NOTE - c is not incremented as per c2l */
-#define c2ln(c, l1, l2, n)                       \
-  {                                              \
-    c += n;                                      \
-    l1 = l2 = 0;                                 \
-    switch (n) {                                 \
-      case 8:                                    \
-        l2 = ((unsigned long)(*(--(c)))) << 24;  \
-      case 7:                                    \
-        l2 |= ((unsigned long)(*(--(c)))) << 16; \
-      case 6:                                    \
-        l2 |= ((unsigned long)(*(--(c)))) << 8;  \
-      case 5:                                    \
-        l2 |= ((unsigned long)(*(--(c))));       \
-      case 4:                                    \
-        l1 = ((unsigned long)(*(--(c)))) << 24;  \
-      case 3:                                    \
-        l1 |= ((unsigned long)(*(--(c)))) << 16; \
-      case 2:                                    \
-        l1 |= ((unsigned long)(*(--(c)))) << 8;  \
-      case 1:                                    \
-        l1 |= ((unsigned long)(*(--(c))));       \
-    }                                            \
-  }
-
-#define l2c(l, c)                            \
-  (*((c)++) = (uint8_t)(((l)) & 0xff),       \
-   *((c)++) = (uint8_t)(((l) >> 8) & 0xff),  \
-   *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 24) & 0xff))
-
-#define n2l(c, l)                          \
-  (l = ((unsigned long)(*((c)++))) << 24,  \
-   l |= ((unsigned long)(*((c)++))) << 16, \
-   l |= ((unsigned long)(*((c)++))) << 8, l |= ((unsigned long)(*((c)++))))
-
 #define l2n(l, c)                            \
   (*((c)++) = (uint8_t)(((l) >> 24) & 0xff), \
    *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \
    *((c)++) = (uint8_t)(((l) >> 8) & 0xff),  \
    *((c)++) = (uint8_t)(((l)) & 0xff))
 
-#define l2n8(l, c)                           \
-  (*((c)++) = (uint8_t)(((l) >> 56) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 48) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 40) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 32) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 24) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 16) & 0xff), \
-   *((c)++) = (uint8_t)(((l) >> 8) & 0xff),  \
-   *((c)++) = (uint8_t)(((l)) & 0xff))
-
-/* NOTE - c is not incremented as per l2c */
-#define l2cn(l1, l2, c, n)                               \
-  {                                                      \
-    c += n;                                              \
-    switch (n) {                                         \
-      case 8:                                            \
-        *(--(c)) = (uint8_t)(((l2) >> 24) & 0xff); \
-      case 7:                                            \
-        *(--(c)) = (uint8_t)(((l2) >> 16) & 0xff); \
-      case 6:                                            \
-        *(--(c)) = (uint8_t)(((l2) >> 8) & 0xff);  \
-      case 5:                                            \
-        *(--(c)) = (uint8_t)(((l2)) & 0xff);       \
-      case 4:                                            \
-        *(--(c)) = (uint8_t)(((l1) >> 24) & 0xff); \
-      case 3:                                            \
-        *(--(c)) = (uint8_t)(((l1) >> 16) & 0xff); \
-      case 2:                                            \
-        *(--(c)) = (uint8_t)(((l1) >> 8) & 0xff);  \
-      case 1:                                            \
-        *(--(c)) = (uint8_t)(((l1)) & 0xff);       \
-    }                                                    \
-  }
-
-#define n2s(c, s) \
-  ((s = (((unsigned int)(c[0])) << 8) | (((unsigned int)(c[1])))), c += 2)
-
 #define s2n(s, c)                              \
   ((c[0] = (uint8_t)(((s) >> 8) & 0xff), \
     c[1] = (uint8_t)(((s)) & 0xff)),     \
    c += 2)
 
-#define n2l3(c, l)                                                         \
-  ((l = (((unsigned long)(c[0])) << 16) | (((unsigned long)(c[1])) << 8) | \
-        (((unsigned long)(c[2])))),                                        \
-   c += 3)
-
 #define l2n3(l, c)                              \
   ((c[0] = (uint8_t)(((l) >> 16) & 0xff), \
     c[1] = (uint8_t)(((l) >> 8) & 0xff),  \
@@ -870,8 +811,6 @@
   char is_dtls;
   int (*ssl_new)(SSL *ssl);
   void (*ssl_free)(SSL *ssl);
-  int (*ssl_accept)(SSL *ssl);
-  int (*ssl_connect)(SSL *ssl);
   long (*ssl_get_message)(SSL *ssl, int msg_type,
                           enum ssl_hash_message_t hash_message, int *ok);
   int (*ssl_read_app_data)(SSL *ssl, uint8_t *buf, int len, int peek);
@@ -888,6 +827,14 @@
   int (*set_handshake_header)(SSL *ssl, int type, unsigned long len);
   /* Write out handshake message */
   int (*do_write)(SSL *ssl);
+  /* send_change_cipher_spec sends a ChangeCipherSpec message. */
+  int (*send_change_cipher_spec)(SSL *ssl, int a, int b);
+  /* expect_flight is called when the handshake expects a flight of messages from
+   * the peer. */
+  void (*expect_flight)(SSL *ssl);
+  /* received_flight is called when the handshake has received a flight of
+   * messages from the peer. */
+  void (*received_flight)(SSL *ssl);
 };
 
 /* This is for the SSLv3/TLSv1.0 differences in crypto/hash stuff It is a bit
@@ -1059,9 +1006,6 @@
  * |len|. It returns one on success and zero on failure. */
 int ssl_fill_hello_random(uint8_t *out, size_t len, int is_server);
 
-int ssl3_send_server_certificate(SSL *ssl);
-int ssl3_send_new_session_ticket(SSL *ssl);
-int ssl3_send_certificate_status(SSL *ssl);
 int ssl3_get_finished(SSL *ssl);
 int ssl3_send_change_cipher_spec(SSL *ssl, int state_a, int state_b);
 void ssl3_cleanup_key_block(SSL *ssl);
@@ -1105,30 +1049,42 @@
 
 int ssl3_set_handshake_header(SSL *ssl, int htype, unsigned long len);
 int ssl3_handshake_write(SSL *ssl);
+void ssl3_expect_flight(SSL *ssl);
+void ssl3_received_flight(SSL *ssl);
 
 int dtls1_do_handshake_write(SSL *ssl, enum dtls1_use_epoch_t use_epoch);
+
+/* dtls1_get_record reads a new input record. On success, it places it in
+ * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
+ * more data is needed. */
+int dtls1_get_record(SSL *ssl);
+
 int dtls1_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek);
 int dtls1_read_change_cipher_spec(SSL *ssl);
 void dtls1_read_close_notify(SSL *ssl);
-int dtls1_read_bytes(SSL *ssl, int type, uint8_t *buf, int len, int peek);
 void dtls1_set_message_header(SSL *ssl, uint8_t mt, unsigned long len,
                               unsigned short seq_num, unsigned long frag_off,
                               unsigned long frag_len);
 
 int dtls1_write_app_data(SSL *ssl, const void *buf, int len);
-int dtls1_write_bytes(SSL *ssl, int type, const void *buf, int len,
-                      enum dtls1_use_epoch_t use_epoch);
+
+/* dtls1_write_record sends a record. It returns one on success and <= 0 on
+ * error. */
+int dtls1_write_record(SSL *ssl, int type, const uint8_t *buf, size_t len,
+                       enum dtls1_use_epoch_t use_epoch);
 
 int dtls1_send_change_cipher_spec(SSL *ssl, int a, int b);
 int dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
-int dtls1_read_failed(SSL *ssl, int code);
 int dtls1_buffer_message(SSL *ssl);
 int dtls1_retransmit_buffered_messages(SSL *ssl);
 void dtls1_clear_record_buffer(SSL *ssl);
-void dtls1_get_message_header(uint8_t *data, struct hm_header_st *msg_hdr);
+int dtls1_parse_fragment(CBS *cbs, struct hm_header_st *out_hdr,
+                         CBS *out_body);
 int dtls1_check_timeout_num(SSL *ssl);
 int dtls1_set_handshake_header(SSL *ssl, int type, unsigned long len);
 int dtls1_handshake_write(SSL *ssl);
+void dtls1_expect_flight(SSL *ssl);
+void dtls1_received_flight(SSL *ssl);
 
 int dtls1_supports_cipher(const SSL_CIPHER *cipher);
 void dtls1_start_timer(SSL *ssl);
@@ -1138,37 +1094,6 @@
 unsigned int dtls1_min_mtu(void);
 void dtls1_hm_fragment_free(hm_fragment *frag);
 
-/* some client-only functions */
-int ssl3_send_client_hello(SSL *ssl);
-int ssl3_get_server_hello(SSL *ssl);
-int ssl3_get_certificate_request(SSL *ssl);
-int ssl3_get_new_session_ticket(SSL *ssl);
-int ssl3_get_cert_status(SSL *ssl);
-int ssl3_get_server_done(SSL *ssl);
-int ssl3_send_cert_verify(SSL *ssl);
-int ssl3_send_client_certificate(SSL *ssl);
-int ssl_do_client_cert_cb(SSL *ssl, X509 **px509, EVP_PKEY **ppkey);
-int ssl3_send_client_key_exchange(SSL *ssl);
-int ssl3_get_server_key_exchange(SSL *ssl);
-int ssl3_get_server_certificate(SSL *ssl);
-int ssl3_send_next_proto(SSL *ssl);
-int ssl3_send_channel_id(SSL *ssl);
-int ssl3_verify_server_cert(SSL *ssl);
-
-/* some server-only functions */
-int ssl3_get_initial_bytes(SSL *ssl);
-int ssl3_get_v2_client_hello(SSL *ssl);
-int ssl3_get_client_hello(SSL *ssl);
-int ssl3_send_server_hello(SSL *ssl);
-int ssl3_send_server_key_exchange(SSL *ssl);
-int ssl3_send_certificate_request(SSL *ssl);
-int ssl3_send_server_done(SSL *ssl);
-int ssl3_get_client_certificate(SSL *ssl);
-int ssl3_get_client_key_exchange(SSL *ssl);
-int ssl3_get_cert_verify(SSL *ssl);
-int ssl3_get_next_proto(SSL *ssl);
-int ssl3_get_channel_id(SSL *ssl);
-
 int dtls1_new(SSL *ssl);
 int dtls1_accept(SSL *ssl);
 int dtls1_connect(SSL *ssl);
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index d5e304d..f081066 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -130,29 +130,20 @@
 
 
 /* ssl3_do_write sends |ssl->init_buf| in records of type 'type'
- * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns -1 on error, 1
- * on success or zero if the transmission is still incomplete. */
+ * (SSL3_RT_HANDSHAKE or SSL3_RT_CHANGE_CIPHER_SPEC). It returns 1 on success
+ * and <= 0 on error. */
 int ssl3_do_write(SSL *ssl, int type) {
-  int n;
-
-  n = ssl3_write_bytes(ssl, type, &ssl->init_buf->data[ssl->init_off],
-                       ssl->init_num);
-  if (n < 0) {
-    return -1;
+  int ret = ssl3_write_bytes(ssl, type, ssl->init_buf->data, ssl->init_num);
+  if (ret <= 0) {
+    return ret;
   }
 
-  if (n == ssl->init_num) {
-    if (ssl->msg_callback) {
-      ssl->msg_callback(1, ssl->version, type, ssl->init_buf->data,
-                      (size_t)(ssl->init_off + ssl->init_num), ssl,
-                      ssl->msg_callback_arg);
-    }
-    return 1;
-  }
-
-  ssl->init_off += n;
-  ssl->init_num -= n;
-  return 0;
+  /* ssl3_write_bytes writes the data in its entirety. */
+  assert(ret == ssl->init_num);
+  ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, type,
+                      ssl->init_buf->data, (size_t)ssl->init_num);
+  ssl->init_num = 0;
+  return 1;
 }
 
 int ssl3_send_finished(SSL *ssl, int a, int b) {
@@ -274,7 +265,6 @@
   if (ssl->state == a) {
     *((uint8_t *)ssl->init_buf->data) = SSL3_MT_CCS;
     ssl->init_num = 1;
-    ssl->init_off = 0;
 
     ssl->state = b;
   }
@@ -382,10 +372,8 @@
 
   /* We have now received a complete message. */
   ssl->s3->tmp.message_complete = 1;
-  if (ssl->msg_callback) {
-    ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, ssl->init_buf->data,
-                      ssl->init_buf->length, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE,
+                      ssl->init_buf->data, ssl->init_buf->length);
 
   static const uint8_t kHelloRequest[4] = {SSL3_MT_HELLO_REQUEST, 0, 0, 0};
   if (!ssl->server && ssl->init_buf->length == sizeof(kHelloRequest) &&
@@ -491,6 +479,9 @@
     case X509_V_ERR_CRL_NOT_YET_VALID:
     case X509_V_ERR_CERT_UNTRUSTED:
     case X509_V_ERR_CERT_REJECTED:
+    case X509_V_ERR_HOSTNAME_MISMATCH:
+    case X509_V_ERR_EMAIL_MISMATCH:
+    case X509_V_ERR_IP_ADDRESS_MISMATCH:
       al = SSL_AD_BAD_CERTIFICATE;
       break;
 
@@ -508,7 +499,10 @@
       al = SSL_AD_CERTIFICATE_REVOKED;
       break;
 
+    case X509_V_ERR_UNSPECIFIED:
     case X509_V_ERR_OUT_OF_MEM:
+    case X509_V_ERR_INVALID_CALL:
+    case X509_V_ERR_STORE_LOOKUP:
       al = SSL_AD_INTERNAL_ERROR;
       break;
 
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 4cf5aa3..43dcb02 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -171,7 +171,6 @@
   *(p++) = htype;
   l2n3(len, p);
   ssl->init_num = (int)len + SSL3_HM_HEADER_LENGTH;
-  ssl->init_off = 0;
 
   /* Add the message to the handshake hash. */
   return ssl3_update_handshake_hash(ssl, (uint8_t *)ssl->init_buf->data,
@@ -182,6 +181,10 @@
   return ssl3_do_write(ssl, SSL3_RT_HANDSHAKE);
 }
 
+void ssl3_expect_flight(SSL *ssl) {}
+
+void ssl3_received_flight(SSL *ssl) {}
+
 int ssl3_new(SSL *ssl) {
   SSL3_STATE *s3;
 
@@ -234,214 +237,6 @@
   ssl->s3 = NULL;
 }
 
-int SSL_session_reused(const SSL *ssl) {
-  return ssl->hit;
-}
-
-int SSL_total_renegotiations(const SSL *ssl) {
-  return ssl->s3->total_renegotiations;
-}
-
-int SSL_num_renegotiations(const SSL *ssl) {
-  return SSL_total_renegotiations(ssl);
-}
-
-int SSL_CTX_need_tmp_RSA(const SSL_CTX *ctx) {
-  return 0;
-}
-
-int SSL_need_tmp_RSA(const SSL *ssl) {
-  return 0;
-}
-
-int SSL_CTX_set_tmp_rsa(SSL_CTX *ctx, const RSA *rsa) {
-  return 1;
-}
-
-int SSL_set_tmp_rsa(SSL *ssl, const RSA *rsa) {
-  return 1;
-}
-
-int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) {
-  DH_free(ctx->cert->dh_tmp);
-  ctx->cert->dh_tmp = DHparams_dup(dh);
-  if (ctx->cert->dh_tmp == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-    return 0;
-  }
-  return 1;
-}
-
-int SSL_set_tmp_dh(SSL *ssl, const DH *dh) {
-  DH_free(ssl->cert->dh_tmp);
-  ssl->cert->dh_tmp = DHparams_dup(dh);
-  if (ssl->cert->dh_tmp == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
-    return 0;
-  }
-  return 1;
-}
-
-int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) {
-  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
-  return SSL_CTX_set1_curves(ctx, &nid, 1);
-}
-
-int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) {
-  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
-    return 0;
-  }
-  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
-  return SSL_set1_curves(ssl, &nid, 1);
-}
-
-int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
-  ctx->tlsext_channel_id_enabled = 1;
-  return 1;
-}
-
-int SSL_enable_tls_channel_id(SSL *ssl) {
-  ssl->tlsext_channel_id_enabled = 1;
-  return 1;
-}
-
-static int is_p256_key(EVP_PKEY *private_key) {
-  const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key);
-  return ec_key != NULL &&
-         EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) ==
-             NID_X9_62_prime256v1;
-}
-
-int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) {
-  if (!is_p256_key(private_key)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
-    return 0;
-  }
-
-  EVP_PKEY_free(ctx->tlsext_channel_id_private);
-  ctx->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
-  ctx->tlsext_channel_id_enabled = 1;
-
-  return 1;
-}
-
-int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
-  if (!is_p256_key(private_key)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
-    return 0;
-  }
-
-  EVP_PKEY_free(ssl->tlsext_channel_id_private);
-  ssl->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
-  ssl->tlsext_channel_id_enabled = 1;
-
-  return 1;
-}
-
-size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) {
-  if (!ssl->s3->tlsext_channel_id_valid) {
-    return 0;
-  }
-  memcpy(out, ssl->s3->tlsext_channel_id, (max_out < 64) ? max_out : 64);
-  return 64;
-}
-
-int SSL_set_tlsext_host_name(SSL *ssl, const char *name) {
-  OPENSSL_free(ssl->tlsext_hostname);
-  ssl->tlsext_hostname = NULL;
-
-  if (name == NULL) {
-    return 1;
-  }
-
-  size_t len = strlen(name);
-  if (len == 0 || len > TLSEXT_MAXLEN_host_name) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
-    return 0;
-  }
-  ssl->tlsext_hostname = BUF_strdup(name);
-  if (ssl->tlsext_hostname == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-  return 1;
-}
-
-size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
-  if (ssl->server || !ssl->s3->tmp.cert_req) {
-    *out_types = NULL;
-    return 0;
-  }
-  *out_types = ssl->s3->tmp.certificate_types;
-  return ssl->s3->tmp.num_certificate_types;
-}
-
-int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) {
-  return tls1_set_curves(&ctx->supported_group_list,
-                         &ctx->supported_group_list_len, curves,
-                         curves_len);
-}
-
-int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
-  return tls1_set_curves(&ssl->supported_group_list,
-                         &ssl->supported_group_list_len, curves,
-                         curves_len);
-}
-
-int SSL_CTX_set_tlsext_servername_callback(
-    SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) {
-  ctx->tlsext_servername_callback = callback;
-  return 1;
-}
-
-int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) {
-  ctx->tlsext_servername_arg = arg;
-  return 1;
-}
-
-int SSL_CTX_get_tlsext_ticket_keys(SSL_CTX *ctx, void *out, size_t len) {
-  if (out == NULL) {
-    return 48;
-  }
-  if (len != 48) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
-    return 0;
-  }
-  uint8_t *out_bytes = out;
-  memcpy(out_bytes, ctx->tlsext_tick_key_name, 16);
-  memcpy(out_bytes + 16, ctx->tlsext_tick_hmac_key, 16);
-  memcpy(out_bytes + 32, ctx->tlsext_tick_aes_key, 16);
-  return 1;
-}
-
-int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in, size_t len) {
-  if (in == NULL) {
-    return 48;
-  }
-  if (len != 48) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
-    return 0;
-  }
-  const uint8_t *in_bytes = in;
-  memcpy(ctx->tlsext_tick_key_name, in_bytes, 16);
-  memcpy(ctx->tlsext_tick_hmac_key, in_bytes + 16, 16);
-  memcpy(ctx->tlsext_tick_aes_key, in_bytes + 32, 16);
-  return 1;
-}
-
-int SSL_CTX_set_tlsext_ticket_key_cb(
-    SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv,
-                                  EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
-                                  int encrypt)) {
-  ctx->tlsext_ticket_key_cb = callback;
-  return 1;
-}
-
 struct ssl_cipher_preference_list_st *ssl_get_cipher_preferences(SSL *ssl) {
   if (ssl->cipher_list != NULL) {
     return ssl->cipher_list;
diff --git a/src/ssl/s3_meth.c b/src/ssl/s3_meth.c
index b60b5f2..8370f23 100644
--- a/src/ssl/s3_meth.c
+++ b/src/ssl/s3_meth.c
@@ -63,8 +63,6 @@
     0 /* is_dtls */,
     ssl3_new,
     ssl3_free,
-    ssl3_accept,
-    ssl3_connect,
     ssl3_get_message,
     ssl3_read_app_data,
     ssl3_read_change_cipher_spec,
@@ -75,6 +73,9 @@
     SSL3_HM_HEADER_LENGTH,
     ssl3_set_handshake_header,
     ssl3_handshake_write,
+    ssl3_send_change_cipher_spec,
+    ssl3_expect_flight,
+    ssl3_received_flight,
 };
 
 const SSL_METHOD *TLS_method(void) {
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index c54c10b..2396a7f 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -123,15 +123,10 @@
 
 static int do_ssl3_write(SSL *ssl, int type, const uint8_t *buf, unsigned len);
 
-/* kMaxWarningAlerts is the number of consecutive warning alerts that will be
- * processed. */
-static const uint8_t kMaxWarningAlerts = 4;
-
 /* ssl3_get_record reads a new input record. On success, it places it in
  * |ssl->s3->rrec| and returns one. Otherwise it returns <= 0 on error or if
  * more data is needed. */
 static int ssl3_get_record(SSL *ssl) {
-  int ret;
 again:
   switch (ssl->s3->recv_shutdown) {
     case ssl_shutdown_none:
@@ -143,43 +138,44 @@
       return 0;
   }
 
-  /* Ensure the buffer is large enough to decrypt in-place. */
-  ret = ssl_read_buffer_extend_to(ssl, ssl_record_prefix_len(ssl));
-  if (ret <= 0) {
-    return ret;
-  }
-  assert(ssl_read_buffer_len(ssl) >= ssl_record_prefix_len(ssl));
-
-  uint8_t *out = ssl_read_buffer(ssl) + ssl_record_prefix_len(ssl);
-  size_t max_out = ssl_read_buffer_len(ssl) - ssl_record_prefix_len(ssl);
+  CBS body;
   uint8_t type, alert;
-  size_t len, consumed;
-  switch (tls_open_record(ssl, &type, out, &len, &consumed, &alert, max_out,
-                          ssl_read_buffer(ssl), ssl_read_buffer_len(ssl))) {
-    case ssl_open_record_success:
-      ssl_read_buffer_consume(ssl, consumed);
+  size_t consumed;
+  enum ssl_open_record_t open_ret =
+      tls_open_record(ssl, &type, &body, &consumed, &alert,
+                      ssl_read_buffer(ssl), ssl_read_buffer_len(ssl));
+  if (open_ret != ssl_open_record_partial) {
+    ssl_read_buffer_consume(ssl, consumed);
+  }
+  switch (open_ret) {
+    case ssl_open_record_partial: {
+      int read_ret = ssl_read_buffer_extend_to(ssl, consumed);
+      if (read_ret <= 0) {
+        return read_ret;
+      }
+      goto again;
+    }
 
-      if (len > 0xffff) {
+    case ssl_open_record_success:
+      if (CBS_len(&body) > 0xffff) {
         OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
         return -1;
       }
 
       SSL3_RECORD *rr = &ssl->s3->rrec;
       rr->type = type;
-      rr->length = (uint16_t)len;
-      rr->data = out;
+      rr->length = (uint16_t)CBS_len(&body);
+      rr->data = (uint8_t *)CBS_data(&body);
       return 1;
 
-    case ssl_open_record_partial:
-      ret = ssl_read_buffer_extend_to(ssl, consumed);
-      if (ret <= 0) {
-        return ret;
-      }
+    case ssl_open_record_discard:
       goto again;
 
-    case ssl_open_record_discard:
-      ssl_read_buffer_consume(ssl, consumed);
-      goto again;
+    case ssl_open_record_close_notify:
+      return 0;
+
+    case ssl_open_record_fatal_alert:
+      return -1;
 
     case ssl_open_record_error:
       ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
@@ -314,34 +310,46 @@
 
 int ssl3_read_app_data(SSL *ssl, uint8_t *buf, int len, int peek) {
   assert(!SSL_in_init(ssl));
+  assert(ssl->s3->initial_handshake_complete);
+
   return ssl3_read_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, len, peek);
 }
 
 int ssl3_read_change_cipher_spec(SSL *ssl) {
-  uint8_t byte;
-  int ret = ssl3_read_bytes(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1 /* len */,
-                            0 /* no peek */);
-  if (ret <= 0) {
-    return ret;
-  }
-  assert(ret == 1);
+  SSL3_RECORD *rr = &ssl->s3->rrec;
 
-  if (ssl->s3->rrec.length != 0 || byte != SSL3_MT_CCS) {
+  if (rr->length == 0) {
+    int ret = ssl3_get_record(ssl);
+    if (ret <= 0) {
+      return ret;
+    }
+  }
+
+  if (rr->type != SSL3_RT_CHANGE_CIPHER_SPEC) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
+    return -1;
+  }
+
+  if (rr->length != 1 || rr->data[0] != SSL3_MT_CCS) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
     return -1;
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(0, ssl->version, SSL3_RT_CHANGE_CIPHER_SPEC, &byte, 1,
-                      ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 0 /* read */, ssl->version,
+                      SSL3_RT_CHANGE_CIPHER_SPEC, rr->data, rr->length);
 
+  rr->length = 0;
+  ssl_read_buffer_discard(ssl);
   return 1;
 }
 
 void ssl3_read_close_notify(SSL *ssl) {
-  ssl3_read_bytes(ssl, 0, NULL, 0, 0);
+  /* Read records until an error or close_notify. */
+  while (ssl3_get_record(ssl) > 0) {
+    ;
+  }
 }
 
 static int ssl3_can_renegotiate(SSL *ssl) {
@@ -364,9 +372,7 @@
  * 'type' is one of the following:
  *
  *   -  SSL3_RT_HANDSHAKE (when ssl3_get_message calls us)
- *   -  SSL3_RT_CHANGE_CIPHER_SPEC (when ssl3_read_change_cipher_spec calls us)
  *   -  SSL3_RT_APPLICATION_DATA (when ssl3_read_app_data calls us)
- *   -  0 (during a shutdown, no data has to be returned)
  *
  * If we don't have stored data to work from, read a SSL/TLS record first
  * (possibly multiple records if we still don't have anything to return).
@@ -377,10 +383,8 @@
   int al, i, ret;
   unsigned int n;
   SSL3_RECORD *rr;
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
 
-  if ((type && type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE &&
-       type != SSL3_RT_CHANGE_CIPHER_SPEC) ||
+  if ((type != SSL3_RT_APPLICATION_DATA && type != SSL3_RT_HANDSHAKE) ||
       (peek && type != SSL3_RT_APPLICATION_DATA)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
@@ -403,9 +407,7 @@
 
   /* we now have a packet which can be read and processed */
 
-  if (type != 0 && type == rr->type) {
-    ssl->s3->warning_alert_count = 0;
-
+  if (type == rr->type) {
     /* Discard empty records. */
     if (rr->length == 0) {
       goto start;
@@ -464,18 +466,8 @@
     }
     ssl->s3->hello_request_len = 0;
 
-    if (ssl->msg_callback) {
-      ssl->msg_callback(0, ssl->version, SSL3_RT_HANDSHAKE, kHelloRequest,
-                      sizeof(kHelloRequest), ssl, ssl->msg_callback_arg);
-    }
-
-    if (!SSL_is_init_finished(ssl) || !ssl->s3->initial_handshake_complete) {
-      /* This cannot happen. If a handshake is in progress, |type| must be
-       * |SSL3_RT_HANDSHAKE|. */
-      assert(0);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
+    ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_HANDSHAKE,
+                        kHelloRequest, sizeof(kHelloRequest));
 
     if (ssl->renegotiate_mode == ssl_renegotiate_ignore) {
       goto start;
@@ -507,79 +499,11 @@
     goto start;
   }
 
-  /* If an alert record, process the alert. */
-  if (rr->type == SSL3_RT_ALERT) {
-    /* Alerts records may not contain fragmented or multiple alerts. */
-    if (rr->length != 2) {
-      al = SSL_AD_DECODE_ERROR;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
-      goto f_err;
-    }
-
-    if (ssl->msg_callback) {
-      ssl->msg_callback(0, ssl->version, SSL3_RT_ALERT, rr->data, 2, ssl,
-                        ssl->msg_callback_arg);
-    }
-    const uint8_t alert_level = rr->data[0];
-    const uint8_t alert_descr = rr->data[1];
-    rr->length -= 2;
-    rr->data += 2;
-
-    if (ssl->info_callback != NULL) {
-      cb = ssl->info_callback;
-    } else if (ssl->ctx->info_callback != NULL) {
-      cb = ssl->ctx->info_callback;
-    }
-
-    if (cb != NULL) {
-      uint16_t alert = (alert_level << 8) | alert_descr;
-      cb(ssl, SSL_CB_READ_ALERT, alert);
-    }
-
-    if (alert_level == SSL3_AL_WARNING) {
-      if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
-        ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
-        return 0;
-      }
-
-      ssl->s3->warning_alert_count++;
-      if (ssl->s3->warning_alert_count > kMaxWarningAlerts) {
-        al = SSL_AD_UNEXPECTED_MESSAGE;
-        OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS);
-        goto f_err;
-      }
-    } else if (alert_level == SSL3_AL_FATAL) {
-      char tmp[16];
-
-      OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
-      BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
-      ERR_add_error_data(2, "SSL alert number ", tmp);
-      ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
-      SSL_CTX_remove_session(ssl->ctx, ssl->session);
-      return 0;
-    } else {
-      al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE);
-      goto f_err;
-    }
-
-    goto start;
-  }
-
-  if (type == 0) {
-    /* This may only occur from read_close_notify. */
-    assert(ssl->s3->send_shutdown == ssl_shutdown_close_notify);
-    /* close_notify has been sent, so discard all records other than alerts. */
-    rr->length = 0;
-    goto start;
-  }
-
   al = SSL_AD_UNEXPECTED_MESSAGE;
   OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_RECORD);
 
 f_err:
   ssl3_send_alert(ssl, SSL3_AL_FATAL, al);
-err:
   return -1;
 }
 
@@ -625,22 +549,11 @@
     BIO_flush(ssl->wbio);
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(1 /* write */, ssl->version, SSL3_RT_ALERT,
-                      ssl->s3->send_alert, 2, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 1 /* write */, ssl->version, SSL3_RT_ALERT,
+                      ssl->s3->send_alert, 2);
 
-  void (*cb)(const SSL *ssl, int type, int value) = NULL;
-  if (ssl->info_callback != NULL) {
-    cb = ssl->info_callback;
-  } else if (ssl->ctx->info_callback != NULL) {
-    cb = ssl->ctx->info_callback;
-  }
-
-  if (cb != NULL) {
-    int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
-    cb(ssl, SSL_CB_WRITE_ALERT, alert);
-  }
+  int alert = (ssl->s3->send_alert[0] << 8) | ssl->s3->send_alert[1];
+  ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, alert);
 
   return 1;
 }
diff --git a/src/ssl/ssl_aead_ctx.c b/src/ssl/ssl_aead_ctx.c
index 1e549ea..88daddd 100644
--- a/src/ssl/ssl_aead_ctx.c
+++ b/src/ssl/ssl_aead_ctx.c
@@ -166,22 +166,16 @@
   return len;
 }
 
-int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len,
-                      size_t max_out, uint8_t type, uint16_t wire_version,
-                      const uint8_t seqnum[8], const uint8_t *in,
-                      size_t in_len) {
+int SSL_AEAD_CTX_open(SSL_AEAD_CTX *aead, CBS *out, uint8_t type,
+                      uint16_t wire_version, const uint8_t seqnum[8],
+                      uint8_t *in, size_t in_len) {
 #if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
   aead = NULL;
 #endif
 
   if (aead == NULL) {
     /* Handle the initial NULL cipher. */
-    if (in_len > max_out) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
-      return 0;
-    }
-    memmove(out, in, in_len);
-    *out_len = in_len;
+    CBS_init(out, in, in_len);
     return 1;
   }
 
@@ -239,8 +233,14 @@
     }
   }
 
-  return EVP_AEAD_CTX_open(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
-                           in, in_len, ad, ad_len);
+  /* Decrypt in-place. */
+  size_t len;
+  if (!EVP_AEAD_CTX_open(&aead->ctx, in, &len, in_len, nonce, nonce_len,
+                         in, in_len, ad, ad_len)) {
+    return 0;
+  }
+  CBS_init(out, in, len);
+  return 1;
 }
 
 int SSL_AEAD_CTX_seal(SSL_AEAD_CTX *aead, uint8_t *out, size_t *out_len,
diff --git a/src/ssl/ssl_buffer.c b/src/ssl/ssl_buffer.c
index 272b13b..a3cf360 100644
--- a/src/ssl/ssl_buffer.c
+++ b/src/ssl/ssl_buffer.c
@@ -162,8 +162,6 @@
     return -1;
   }
 
-  ERR_clear_system_error();
-
   int ret;
   if (SSL_IS_DTLS(ssl)) {
     /* |len| is ignored for a datagram transport. */
@@ -184,14 +182,13 @@
   SSL3_BUFFER *buf = &ssl->s3->read_buffer;
 
   consume_buffer(buf, len);
-  if (!SSL_IS_DTLS(ssl)) {
-    /* The TLS stack never reads beyond the current record, so there will never
-     * be unconsumed data. If read-ahead is ever reimplemented,
-     * |ssl_read_buffer_discard| will require a |memcpy| to shift the excess
-     * back to the front of the buffer, to ensure there is enough space for the
-     * next record. */
-     assert(buf->len == 0);
-  }
+
+  /* The TLS stack never reads beyond the current record, so there will never be
+   * unconsumed data. If read-ahead is ever reimplemented,
+   * |ssl_read_buffer_discard| will require a |memcpy| to shift the excess back
+   * to the front of the buffer, to ensure there is enough space for the next
+   * record. */
+  assert(SSL_IS_DTLS(ssl) || len == 0 || buf->len == 0);
 }
 
 void ssl_read_buffer_discard(SSL *ssl) {
@@ -227,7 +224,7 @@
     return 0;
   }
 
-  size_t header_len = ssl_seal_prefix_len(ssl);
+  size_t header_len = ssl_seal_align_prefix_len(ssl);
 
   /* TODO(davidben): This matches the original behavior in keeping the malloc
    * size consistent. Does this matter? |cap| could just be |max_len|. */
@@ -301,7 +298,6 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_BIO_NOT_SET);
     return -1;
   }
-  ERR_clear_system_error();
 
   if (SSL_IS_DTLS(ssl)) {
     return dtls_write_buffer_flush(ssl);
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index dcee293..e78374b 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -662,6 +662,28 @@
      SSL_HANDSHAKE_MAC_SHA256,
     },
 
+    /* Cipher D001 */
+    {
+     TLS1_TXT_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+     TLS1_CK_ECDHE_PSK_WITH_AES_128_GCM_SHA256,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_AES128GCM,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher D002 */
+    {
+     TLS1_TXT_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
+     TLS1_CK_ECDHE_PSK_WITH_AES_256_GCM_SHA384,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_AES256GCM,
+     SSL_SHA384,
+     SSL_HANDSHAKE_MAC_SHA384,
+    },
+
 };
 
 static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]);
@@ -1669,6 +1691,10 @@
   return (cipher->algorithm_auth & SSL_aECDSA) != 0;
 }
 
+int SSL_CIPHER_is_DHE(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mkey & SSL_kDHE) != 0;
+}
+
 int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_mkey & SSL_kECDHE) != 0;
 }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index 8e9b196..7f7bf5a 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -514,13 +514,13 @@
 void SSL_set_connect_state(SSL *ssl) {
   ssl->server = 0;
   ssl->state = SSL_ST_CONNECT;
-  ssl->handshake_func = ssl->method->ssl_connect;
+  ssl->handshake_func = ssl3_connect;
 }
 
 void SSL_set_accept_state(SSL *ssl) {
   ssl->server = 1;
   ssl->state = SSL_ST_ACCEPT;
-  ssl->handshake_func = ssl->method->ssl_accept;
+  ssl->handshake_func = ssl3_accept;
 }
 
 void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
@@ -559,6 +559,7 @@
   ssl->rwstate = SSL_NOTHING;
   /* Functions which use SSL_get_error must clear the error queue on entry. */
   ERR_clear_error();
+  ERR_clear_system_error();
 
   if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_CONNECTION_TYPE_NOT_SET);
@@ -578,8 +579,6 @@
     SSL_set_connect_state(ssl);
   }
 
-  assert(ssl->handshake_func == ssl->method->ssl_connect);
-
   return SSL_do_handshake(ssl);
 }
 
@@ -589,8 +588,6 @@
     SSL_set_accept_state(ssl);
   }
 
-  assert(ssl->handshake_func == ssl->method->ssl_accept);
-
   return SSL_do_handshake(ssl);
 }
 
@@ -665,6 +662,7 @@
   ssl->rwstate = SSL_NOTHING;
   /* Functions which use SSL_get_error must clear the error queue on entry. */
   ERR_clear_error();
+  ERR_clear_system_error();
 
   if (ssl->handshake_func == NULL) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNINITIALIZED);
@@ -1184,6 +1182,10 @@
   return SSL_in_init(ssl) && ssl->s3->initial_handshake_complete;
 }
 
+int SSL_total_renegotiations(const SSL *ssl) {
+  return ssl->s3->total_renegotiations;
+}
+
 size_t SSL_CTX_get_max_cert_list(const SSL_CTX *ctx) {
   return ctx->max_cert_list;
 }
@@ -1268,6 +1270,77 @@
   return ctx->session_cache_mode;
 }
 
+
+int SSL_CTX_get_tlsext_ticket_keys(SSL_CTX *ctx, void *out, size_t len) {
+  if (out == NULL) {
+    return 48;
+  }
+  if (len != 48) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
+    return 0;
+  }
+  uint8_t *out_bytes = out;
+  memcpy(out_bytes, ctx->tlsext_tick_key_name, 16);
+  memcpy(out_bytes + 16, ctx->tlsext_tick_hmac_key, 16);
+  memcpy(out_bytes + 32, ctx->tlsext_tick_aes_key, 16);
+  return 1;
+}
+
+int SSL_CTX_set_tlsext_ticket_keys(SSL_CTX *ctx, const void *in, size_t len) {
+  if (in == NULL) {
+    return 48;
+  }
+  if (len != 48) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH);
+    return 0;
+  }
+  const uint8_t *in_bytes = in;
+  memcpy(ctx->tlsext_tick_key_name, in_bytes, 16);
+  memcpy(ctx->tlsext_tick_hmac_key, in_bytes + 16, 16);
+  memcpy(ctx->tlsext_tick_aes_key, in_bytes + 32, 16);
+  return 1;
+}
+
+int SSL_CTX_set_tlsext_ticket_key_cb(
+    SSL_CTX *ctx, int (*callback)(SSL *ssl, uint8_t *key_name, uint8_t *iv,
+                                  EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
+                                  int encrypt)) {
+  ctx->tlsext_ticket_key_cb = callback;
+  return 1;
+}
+
+int SSL_CTX_set1_curves(SSL_CTX *ctx, const int *curves, size_t curves_len) {
+  return tls1_set_curves(&ctx->supported_group_list,
+                         &ctx->supported_group_list_len, curves,
+                         curves_len);
+}
+
+int SSL_set1_curves(SSL *ssl, const int *curves, size_t curves_len) {
+  return tls1_set_curves(&ssl->supported_group_list,
+                         &ssl->supported_group_list_len, curves,
+                         curves_len);
+}
+
+int SSL_CTX_set_tmp_dh(SSL_CTX *ctx, const DH *dh) {
+  DH_free(ctx->cert->dh_tmp);
+  ctx->cert->dh_tmp = DHparams_dup(dh);
+  if (ctx->cert->dh_tmp == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
+    return 0;
+  }
+  return 1;
+}
+
+int SSL_set_tmp_dh(SSL *ssl, const DH *dh) {
+  DH_free(ssl->cert->dh_tmp);
+  ssl->cert->dh_tmp = DHparams_dup(dh);
+  if (ssl->cert->dh_tmp == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_DH_LIB);
+    return 0;
+  }
+  return 1;
+}
+
 STACK_OF(SSL_CIPHER) *SSL_get_ciphers(const SSL *ssl) {
   if (ssl == NULL) {
     return NULL;
@@ -1559,6 +1632,38 @@
   return 1;
 }
 
+int SSL_set_tlsext_host_name(SSL *ssl, const char *name) {
+  OPENSSL_free(ssl->tlsext_hostname);
+  ssl->tlsext_hostname = NULL;
+
+  if (name == NULL) {
+    return 1;
+  }
+
+  size_t len = strlen(name);
+  if (len == 0 || len > TLSEXT_MAXLEN_host_name) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME);
+    return 0;
+  }
+  ssl->tlsext_hostname = BUF_strdup(name);
+  if (ssl->tlsext_hostname == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    return 0;
+  }
+  return 1;
+}
+
+int SSL_CTX_set_tlsext_servername_callback(
+    SSL_CTX *ctx, int (*callback)(SSL *ssl, int *out_alert, void *arg)) {
+  ctx->tlsext_servername_callback = callback;
+  return 1;
+}
+
+int SSL_CTX_set_tlsext_servername_arg(SSL_CTX *ctx, void *arg) {
+  ctx->tlsext_servername_arg = arg;
+  return 1;
+}
+
 int SSL_select_next_proto(uint8_t **out, uint8_t *out_len,
                           const uint8_t *server, unsigned server_len,
                           const uint8_t *client, unsigned client_len) {
@@ -1664,6 +1769,58 @@
   }
 }
 
+
+int SSL_CTX_enable_tls_channel_id(SSL_CTX *ctx) {
+  ctx->tlsext_channel_id_enabled = 1;
+  return 1;
+}
+
+int SSL_enable_tls_channel_id(SSL *ssl) {
+  ssl->tlsext_channel_id_enabled = 1;
+  return 1;
+}
+
+static int is_p256_key(EVP_PKEY *private_key) {
+  const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(private_key);
+  return ec_key != NULL &&
+         EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)) ==
+             NID_X9_62_prime256v1;
+}
+
+int SSL_CTX_set1_tls_channel_id(SSL_CTX *ctx, EVP_PKEY *private_key) {
+  if (!is_p256_key(private_key)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
+    return 0;
+  }
+
+  EVP_PKEY_free(ctx->tlsext_channel_id_private);
+  ctx->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
+  ctx->tlsext_channel_id_enabled = 1;
+
+  return 1;
+}
+
+int SSL_set1_tls_channel_id(SSL *ssl, EVP_PKEY *private_key) {
+  if (!is_p256_key(private_key)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_CHANNEL_ID_NOT_P256);
+    return 0;
+  }
+
+  EVP_PKEY_free(ssl->tlsext_channel_id_private);
+  ssl->tlsext_channel_id_private = EVP_PKEY_up_ref(private_key);
+  ssl->tlsext_channel_id_enabled = 1;
+
+  return 1;
+}
+
+size_t SSL_get_tls_channel_id(SSL *ssl, uint8_t *out, size_t max_out) {
+  if (!ssl->s3->tlsext_channel_id_valid) {
+    return 0;
+  }
+  memcpy(out, ssl->s3->tlsext_channel_id, (max_out < 64) ? max_out : 64);
+  return 64;
+}
+
 void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,
                                       int (*cb)(X509_STORE_CTX *store_ctx,
                                                 void *arg),
@@ -1691,6 +1848,15 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
+size_t SSL_get0_certificate_types(SSL *ssl, const uint8_t **out_types) {
+  if (ssl->server || !ssl->s3->tmp.cert_req) {
+    *out_types = NULL;
+    return 0;
+  }
+  *out_types = ssl->s3->tmp.certificate_types;
+  return ssl->s3->tmp.num_certificate_types;
+}
+
 void ssl_get_compatible_server_ciphers(SSL *ssl, uint32_t *out_mask_k,
                                        uint32_t *out_mask_a) {
   uint32_t mask_k = 0;
@@ -1857,6 +2023,10 @@
   return ssl->s3->aead_write_ctx->cipher;
 }
 
+int SSL_session_reused(const SSL *ssl) {
+  return ssl->hit;
+}
+
 const COMP_METHOD *SSL_get_current_compression(SSL *ssl) { return NULL; }
 
 const COMP_METHOD *SSL_get_current_expansion(SSL *ssl) { return NULL; }
@@ -2186,6 +2356,12 @@
   ctx->keylog_callback = cb;
 }
 
+void SSL_CTX_set_current_time_cb(SSL_CTX *ctx,
+                                 void (*cb)(const SSL *ssl,
+                                            struct timeval *out_clock)) {
+  ctx->current_time_cb = cb;
+}
+
 static int cbb_add_hex(CBB *cbb, const uint8_t *in, size_t in_len) {
   static const char hextable[] = "0123456789abcdef";
   uint8_t *out;
@@ -2535,8 +2711,6 @@
   return ssl3_version_from_wire(ssl, ssl->version);
 }
 
-int SSL_cache_hit(SSL *ssl) { return SSL_session_reused(ssl); }
-
 int SSL_is_server(SSL *ssl) { return ssl->server; }
 
 void SSL_CTX_set_select_certificate_cb(
@@ -2709,6 +2883,27 @@
   return 1;
 }
 
+void ssl_do_info_callback(const SSL *ssl, int type, int value) {
+  void (*cb)(const SSL *ssl, int type, int value) = NULL;
+  if (ssl->info_callback != NULL) {
+    cb = ssl->info_callback;
+  } else if (ssl->ctx->info_callback != NULL) {
+    cb = ssl->ctx->info_callback;
+  }
+
+  if (cb != NULL) {
+    cb(ssl, type, value);
+  }
+}
+
+void ssl_do_msg_callback(SSL *ssl, int is_write, int version, int content_type,
+                         const void *buf, size_t len) {
+  if (ssl->msg_callback != NULL) {
+    ssl->msg_callback(is_write, version, content_type, buf, len, ssl,
+                      ssl->msg_callback_arg);
+  }
+}
+
 int SSL_CTX_sess_connect(const SSL_CTX *ctx) { return 0; }
 int SSL_CTX_sess_connect_good(const SSL_CTX *ctx) { return 0; }
 int SSL_CTX_sess_connect_renegotiate(const SSL_CTX *ctx) { return 0; }
@@ -2720,5 +2915,33 @@
 int SSL_CTX_sess_misses(const SSL_CTX *ctx) { return 0; }
 int SSL_CTX_sess_timeouts(const SSL_CTX *ctx) { return 0; }
 int SSL_CTX_sess_cache_full(const SSL_CTX *ctx) { return 0; }
+
+int SSL_num_renegotiations(const SSL *ssl) {
+  return SSL_total_renegotiations(ssl);
+}
+
+int SSL_CTX_need_tmp_RSA(const SSL_CTX *ctx) { return 0; }
+int SSL_need_tmp_RSA(const SSL *ssl) { return 0; }
+int SSL_CTX_set_tmp_rsa(SSL_CTX *ctx, const RSA *rsa) { return 1; }
+int SSL_set_tmp_rsa(SSL *ssl, const RSA *rsa) { return 1; }
 void ERR_load_SSL_strings(void) {}
 void SSL_load_error_strings(void) {}
+int SSL_cache_hit(SSL *ssl) { return SSL_session_reused(ssl); }
+
+int SSL_CTX_set_tmp_ecdh(SSL_CTX *ctx, const EC_KEY *ec_key) {
+  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return SSL_CTX_set1_curves(ctx, &nid, 1);
+}
+
+int SSL_set_tmp_ecdh(SSL *ssl, const EC_KEY *ec_key) {
+  if (ec_key == NULL || EC_KEY_get0_group(ec_key) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
+    return 0;
+  }
+  int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key));
+  return SSL_set1_curves(ssl, &nid, 1);
+}
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 519736d..1841ece 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -28,14 +28,15 @@
 #include <unistd.h>
 #else
 #include <io.h>
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #pragma comment(lib, "Ws2_32.lib")
 #endif
 
+#include <assert.h>
 #include <inttypes.h>
 #include <string.h>
 
@@ -81,19 +82,10 @@
 }
 
 struct TestState {
-  TestState() {
-    // MSVC cannot initialize these inline.
-    memset(&clock, 0, sizeof(clock));
-    memset(&clock_delta, 0, sizeof(clock_delta));
-  }
-
   // async_bio is async BIO which pauses reads and writes.
   BIO *async_bio = nullptr;
-  // clock is the current time for the SSL connection.
-  timeval clock;
-  // clock_delta is how far the clock advanced in the most recent failed
-  // |BIO_read|.
-  timeval clock_delta;
+  // packeted_bio is the packeted BIO which simulates read timeouts.
+  BIO *packeted_bio = nullptr;
   ScopedEVP_PKEY channel_id;
   bool cert_ready = false;
   ScopedSSL_SESSION session;
@@ -553,7 +545,7 @@
 }
 
 static void CurrentTimeCallback(const SSL *ssl, timeval *out_clock) {
-  *out_clock = GetTestState(ssl)->clock;
+  *out_clock = PacketedBioGetClock(GetTestState(ssl)->packeted_bio);
 }
 
 static void ChannelIdCallback(SSL *ssl, EVP_PKEY **out_pkey) {
@@ -838,7 +830,7 @@
   SSL_CTX_enable_tls_channel_id(ssl_ctx.get());
   SSL_CTX_set_channel_id_cb(ssl_ctx.get(), ChannelIdCallback);
 
-  ssl_ctx->current_time_cb = CurrentTimeCallback;
+  SSL_CTX_set_current_time_cb(ssl_ctx.get(), CurrentTimeCallback);
 
   SSL_CTX_set_info_callback(ssl_ctx.get(), InfoCallback);
   SSL_CTX_sess_set_new_cb(ssl_ctx.get(), NewSessionCallback);
@@ -890,24 +882,15 @@
 
   const TestConfig *config = GetTestConfig(ssl);
   TestState *test_state = GetTestState(ssl);
-  if (test_state->clock_delta.tv_usec != 0 ||
-      test_state->clock_delta.tv_sec != 0) {
-    // Process the timeout and retry.
-    test_state->clock.tv_usec += test_state->clock_delta.tv_usec;
-    test_state->clock.tv_sec += test_state->clock.tv_usec / 1000000;
-    test_state->clock.tv_usec %= 1000000;
-    test_state->clock.tv_sec += test_state->clock_delta.tv_sec;
-    memset(&test_state->clock_delta, 0, sizeof(test_state->clock_delta));
+  assert(config->async);
 
+  if (test_state->packeted_bio != nullptr &&
+      PacketedBioAdvanceClock(test_state->packeted_bio)) {
     // The DTLS retransmit logic silently ignores write failures. So the test
     // may progress, allow writes through synchronously.
-    if (config->async) {
-      AsyncBioEnforceWriteQuota(test_state->async_bio, false);
-    }
+    AsyncBioEnforceWriteQuota(test_state->async_bio, false);
     int timeout_ret = DTLSv1_handle_timeout(ssl);
-    if (config->async) {
-      AsyncBioEnforceWriteQuota(test_state->async_bio, true);
-    }
+    AsyncBioEnforceWriteQuota(test_state->async_bio, true);
 
     if (timeout_ret < 0) {
       fprintf(stderr, "Error retransmitting.\n");
@@ -1336,11 +1319,11 @@
     return false;
   }
   if (config->is_dtls) {
-    ScopedBIO packeted =
-        PacketedBioCreate(&GetTestState(ssl.get())->clock_delta);
+    ScopedBIO packeted = PacketedBioCreate(!config->async);
     if (!packeted) {
       return false;
     }
+    GetTestState(ssl.get())->packeted_bio = packeted.get();
     BIO_push(packeted.get(), bio.release());
     bio = std::move(packeted);
   }
diff --git a/src/ssl/test/packeted_bio.cc b/src/ssl/test/packeted_bio.cc
index e831082..b0982b0 100644
--- a/src/ssl/test/packeted_bio.cc
+++ b/src/ssl/test/packeted_bio.cc
@@ -30,6 +30,46 @@
 const uint8_t kOpcodeTimeout = 'T';
 const uint8_t kOpcodeTimeoutAck = 't';
 
+struct PacketedBio {
+  explicit PacketedBio(bool advance_clock_arg)
+      : advance_clock(advance_clock_arg) {
+    memset(&timeout, 0, sizeof(timeout));
+    memset(&clock, 0, sizeof(clock));
+    memset(&read_deadline, 0, sizeof(read_deadline));
+  }
+
+  bool HasTimeout() const {
+    return timeout.tv_sec != 0 || timeout.tv_usec != 0;
+  }
+
+  bool CanRead() const {
+    if (read_deadline.tv_sec == 0 && read_deadline.tv_usec == 0) {
+      return true;
+    }
+
+    if (clock.tv_sec == read_deadline.tv_sec) {
+      return clock.tv_usec < read_deadline.tv_usec;
+    }
+    return clock.tv_sec < read_deadline.tv_sec;
+  }
+
+  timeval timeout;
+  timeval clock;
+  timeval read_deadline;
+  bool advance_clock;
+};
+
+PacketedBio *GetData(BIO *bio) {
+  if (bio->method != &g_packeted_bio_method) {
+    return NULL;
+  }
+  return (PacketedBio *)bio->ptr;
+}
+
+const PacketedBio *GetData(const BIO *bio) {
+  return GetData(const_cast<BIO*>(bio));
+}
+
 // ReadAll reads |len| bytes from |bio| into |out|. It returns 1 on success and
 // 0 or -1 on error.
 static int ReadAll(BIO *bio, uint8_t *out, size_t len) {
@@ -79,90 +119,113 @@
 }
 
 static int PacketedRead(BIO *bio, char *out, int outl) {
+  PacketedBio *data = GetData(bio);
   if (bio->next_bio == NULL) {
     return 0;
   }
 
   BIO_clear_retry_flags(bio);
 
-  // Read the opcode.
-  uint8_t opcode;
-  int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
-  if (ret <= 0) {
-    BIO_copy_next_retry(bio);
-    return ret;
-  }
+  for (;;) {
+    // Check if the read deadline has passed.
+    if (!data->CanRead()) {
+      BIO_set_retry_read(bio);
+      return -1;
+    }
 
-  if (opcode == kOpcodeTimeout) {
-    // Process the timeout.
-    uint8_t buf[8];
-    ret = ReadAll(bio->next_bio, buf, sizeof(buf));
+    // Read the opcode.
+    uint8_t opcode;
+    int ret = ReadAll(bio->next_bio, &opcode, sizeof(opcode));
     if (ret <= 0) {
       BIO_copy_next_retry(bio);
       return ret;
     }
-    uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
-        (static_cast<uint64_t>(buf[1]) << 48) |
-        (static_cast<uint64_t>(buf[2]) << 40) |
-        (static_cast<uint64_t>(buf[3]) << 32) |
-        (static_cast<uint64_t>(buf[4]) << 24) |
-        (static_cast<uint64_t>(buf[5]) << 16) |
-        (static_cast<uint64_t>(buf[6]) << 8) |
-        static_cast<uint64_t>(buf[7]);
-    timeout /= 1000;  // Convert nanoseconds to microseconds.
-    timeval *out_timeout = reinterpret_cast<timeval *>(bio->ptr);
-    assert(out_timeout->tv_usec == 0);
-    assert(out_timeout->tv_sec == 0);
-    out_timeout->tv_usec = timeout % 1000000;
-    out_timeout->tv_sec = timeout / 1000000;
 
-    // Send an ACK to the peer.
-    ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
+    if (opcode == kOpcodeTimeout) {
+      // The caller is required to advance any pending timeouts before
+      // continuing.
+      if (data->HasTimeout()) {
+        fprintf(stderr, "Unprocessed timeout!\n");
+        return -1;
+      }
+
+      // Process the timeout.
+      uint8_t buf[8];
+      ret = ReadAll(bio->next_bio, buf, sizeof(buf));
+      if (ret <= 0) {
+        BIO_copy_next_retry(bio);
+        return ret;
+      }
+      uint64_t timeout = (static_cast<uint64_t>(buf[0]) << 56) |
+          (static_cast<uint64_t>(buf[1]) << 48) |
+          (static_cast<uint64_t>(buf[2]) << 40) |
+          (static_cast<uint64_t>(buf[3]) << 32) |
+          (static_cast<uint64_t>(buf[4]) << 24) |
+          (static_cast<uint64_t>(buf[5]) << 16) |
+          (static_cast<uint64_t>(buf[6]) << 8) |
+          static_cast<uint64_t>(buf[7]);
+      timeout /= 1000;  // Convert nanoseconds to microseconds.
+
+      data->timeout.tv_usec = timeout % 1000000;
+      data->timeout.tv_sec = timeout / 1000000;
+
+      // Send an ACK to the peer.
+      ret = BIO_write(bio->next_bio, &kOpcodeTimeoutAck, 1);
+      if (ret <= 0) {
+        return ret;
+      }
+      assert(ret == 1);
+
+      if (!data->advance_clock) {
+        // Signal to the caller to retry the read, after advancing the clock.
+        BIO_set_retry_read(bio);
+        return -1;
+      }
+
+      PacketedBioAdvanceClock(bio);
+      continue;
+    }
+
+    if (opcode != kOpcodePacket) {
+      fprintf(stderr, "Unknown opcode, %u\n", opcode);
+      return -1;
+    }
+
+    // Read the length prefix.
+    uint8_t len_bytes[4];
+    ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
     if (ret <= 0) {
+      BIO_copy_next_retry(bio);
       return ret;
     }
-    assert(ret == 1);
 
-    // Signal to the caller to retry the read, after processing the
-    // new clock.
-    BIO_set_retry_read(bio);
-    return -1;
-  }
+    uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
+        (len_bytes[2] << 8) | len_bytes[3];
+    uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
+    if (buf == NULL) {
+      return -1;
+    }
+    ret = ReadAll(bio->next_bio, buf, len);
+    if (ret <= 0) {
+      fprintf(stderr, "Packeted BIO was truncated\n");
+      return -1;
+    }
 
-  if (opcode != kOpcodePacket) {
-    fprintf(stderr, "Unknown opcode, %u\n", opcode);
-    return -1;
+    if (outl > (int)len) {
+      outl = len;
+    }
+    memcpy(out, buf, outl);
+    OPENSSL_free(buf);
+    return outl;
   }
-
-  // Read the length prefix.
-  uint8_t len_bytes[4];
-  ret = ReadAll(bio->next_bio, len_bytes, sizeof(len_bytes));
-  if (ret <= 0) {
-    BIO_copy_next_retry(bio);
-    return ret;
-  }
-
-  uint32_t len = (len_bytes[0] << 24) | (len_bytes[1] << 16) |
-      (len_bytes[2] << 8) | len_bytes[3];
-  uint8_t *buf = (uint8_t *)OPENSSL_malloc(len);
-  if (buf == NULL) {
-    return -1;
-  }
-  ret = ReadAll(bio->next_bio, buf, len);
-  if (ret <= 0) {
-    fprintf(stderr, "Packeted BIO was truncated\n");
-    return -1;
-  }
-
-  if (outl > (int)len) {
-    outl = len;
-  }
-  memcpy(out, buf, outl);
-  OPENSSL_free(buf);
-  return outl;
 }
 
 static long PacketedCtrl(BIO *bio, int cmd, long num, void *ptr) {
+  if (cmd == BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT) {
+    memcpy(&GetData(bio)->read_deadline, ptr, sizeof(timeval));
+    return 1;
+  }
+
   if (bio->next_bio == NULL) {
     return 0;
   }
@@ -182,6 +245,7 @@
     return 0;
   }
 
+  delete GetData(bio);
   bio->init = 0;
   return 1;
 }
@@ -208,11 +272,33 @@
 
 }  // namespace
 
-ScopedBIO PacketedBioCreate(timeval *out_timeout) {
+ScopedBIO PacketedBioCreate(bool advance_clock) {
   ScopedBIO bio(BIO_new(&g_packeted_bio_method));
   if (!bio) {
     return nullptr;
   }
-  bio->ptr = out_timeout;
+  bio->ptr = new PacketedBio(advance_clock);
   return bio;
 }
+
+timeval PacketedBioGetClock(const BIO *bio) {
+  return GetData(bio)->clock;
+}
+
+bool PacketedBioAdvanceClock(BIO *bio) {
+  PacketedBio *data = GetData(bio);
+  if (data == nullptr) {
+    return false;
+  }
+
+  if (!data->HasTimeout()) {
+    return false;
+  }
+
+  data->clock.tv_usec += data->timeout.tv_usec;
+  data->clock.tv_sec += data->clock.tv_usec / 1000000;
+  data->clock.tv_usec %= 1000000;
+  data->clock.tv_sec += data->timeout.tv_sec;
+  memset(&data->timeout, 0, sizeof(data->timeout));
+  return true;
+}
diff --git a/src/ssl/test/packeted_bio.h b/src/ssl/test/packeted_bio.h
index 75cfa13..9bab635 100644
--- a/src/ssl/test/packeted_bio.h
+++ b/src/ssl/test/packeted_bio.h
@@ -21,24 +21,31 @@
 #include "../../crypto/test/scoped_types.h"
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #else
 #include <sys/time.h>
 #endif
 
 
 // PacketedBioCreate creates a filter BIO which implements a reliable in-order
-// blocking datagram socket. The resulting BIO, on |BIO_read|, may simulate a
-// timeout which sets |*out_timeout| to the timeout and fails the read.
-// |*out_timeout| must be zero on entry to |BIO_read|; it is an error to not
-// apply the timeout before the next |BIO_read|.
+// blocking datagram socket. It internally maintains a clock and honors
+// |BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT| based on it.
 //
-// Note: The read timeout simulation is intended to be used with the async BIO
-// wrapper. It doesn't simulate BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, used in DTLS's
-// blocking mode.
-ScopedBIO PacketedBioCreate(timeval *out_timeout);
+// During a |BIO_read|, the peer may signal the filter BIO to simulate a
+// timeout. If |advance_clock| is true, it automatically advances the clock and
+// continues reading, subject to the read deadline. Otherwise, it fails
+// immediately. The caller must then call |PacketedBioAdvanceClock| before
+// retrying |BIO_read|.
+ScopedBIO PacketedBioCreate(bool advance_clock);
+
+// PacketedBioGetClock returns the current time for |bio|.
+timeval PacketedBioGetClock(const BIO *bio);
+
+// PacketedBioAdvanceClock advances |bio|'s internal clock and returns true if
+// there is a pending timeout. Otherwise, it returns false.
+bool PacketedBioAdvanceClock(BIO *bio);
 
 
 #endif  // HEADER_PACKETED_BIO
diff --git a/src/ssl/test/runner/cipher_suites.go b/src/ssl/test/runner/cipher_suites.go
index 799f2d5..26f51b0 100644
--- a/src/ssl/test/runner/cipher_suites.go
+++ b/src/ssl/test/runner/cipher_suites.go
@@ -129,6 +129,8 @@
 	{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_CHACHA20_POLY1305_SHA256, 32, 0, 12, ecdhePSKKA, suiteECDHE | suitePSK | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256, 16, 0, 4, ecdhePSKKA, suiteECDHE | suitePSK | suiteTLS12, nil, nil, aeadAESGCM},
+	{TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384, 32, 0, 4, ecdhePSKKA, suiteECDHE | suitePSK | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, 16, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
 	{TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA, 32, 20, 16, ecdhePSKKA, suiteECDHE | suitePSK, cipherAES, macSHA1, nil},
 	{TLS_PSK_WITH_RC4_128_SHA, 16, 20, 0, pskKA, suiteNoDTLS | suitePSK, cipherRC4, macSHA1, nil},
@@ -497,6 +499,8 @@
 const (
 	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD   uint16 = 0xcc13
 	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc14
+	TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256             uint16 = 0xd001
+	TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384             uint16 = 0xd002
 	TLS_CECPQ1_RSA_WITH_CHACHA20_POLY1305_SHA256      uint16 = 0x16b7
 	TLS_CECPQ1_ECDSA_WITH_CHACHA20_POLY1305_SHA256    uint16 = 0x16b8
 	TLS_CECPQ1_RSA_WITH_AES_256_GCM_SHA384            uint16 = 0x16b9
diff --git a/src/ssl/test/runner/deterministic.go b/src/ssl/test/runner/deterministic.go
new file mode 100644
index 0000000..4a61ee0
--- /dev/null
+++ b/src/ssl/test/runner/deterministic.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2016, Google Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+package runner
+
+import (
+	"encoding/binary"
+)
+
+// Use a different key from crypto/rand/deterministic.c.
+var deterministicRandKey = []byte("runner deterministic key 0123456")
+
+type deterministicRand struct {
+	numCalls uint64
+}
+
+func (d *deterministicRand) Read(buf []byte) (int, error) {
+	for i := range buf {
+		buf[i] = 0
+	}
+	var nonce [12]byte
+	binary.LittleEndian.PutUint64(nonce[:8], d.numCalls)
+	chaCha20(buf, buf, deterministicRandKey, nonce[:], 0)
+	d.numCalls++
+	return len(buf), nil
+}
diff --git a/src/ssl/test/runner/newhope/newhope.go b/src/ssl/test/runner/newhope/newhope.go
index 36c2c85..8f7a530 100644
--- a/src/ssl/test/runner/newhope/newhope.go
+++ b/src/ssl/test/runner/newhope/newhope.go
@@ -153,24 +153,24 @@
 // details of how this works.
 func ntt(in *Poly, omega, preScaleBase, postScaleBase, postScale uint16) *Poly {
 	out := new(Poly)
-	omega_to_the_i := 1
+	omega_to_the_i := uint64(1)
 
 	for i := range out {
-		omegaToTheIJ := 1
-		preScale := int(1)
-		sum := 0
+		omegaToTheIJ := uint64(1)
+		preScale := uint64(1)
+		sum := uint64(0)
 
 		for j := range in {
-			t := (int(in[j]) * preScale) % q
+			t := (uint64(in[j]) * preScale) % q
 			sum += (t * omegaToTheIJ) % q
 			omegaToTheIJ = (omegaToTheIJ * omega_to_the_i) % q
-			preScale = (int(preScaleBase) * preScale) % q
+			preScale = (uint64(preScaleBase) * preScale) % q
 		}
 
-		out[i] = uint16((sum * int(postScale)) % q)
+		out[i] = uint16((sum * uint64(postScale)) % q)
 
-		omega_to_the_i = (omega_to_the_i * int(omega)) % q
-		postScale = uint16((int(postScale) * int(postScaleBase)) % q)
+		omega_to_the_i = (omega_to_the_i * uint64(omega)) % q
+		postScale = uint16((uint64(postScale) * uint64(postScaleBase)) % q)
 	}
 
 	return out
@@ -255,7 +255,7 @@
 
 	bFreq := new(Poly)
 	for i := range bFreq {
-		bFreq[i] = uint16((int(sFreq[i])*int(aFreq[i]) + int(eFreq[i])) % q)
+		bFreq[i] = uint16((uint64(sFreq[i])*uint64(aFreq[i]) + uint64(eFreq[i])) % q)
 	}
 
 	offerMsg = encodePoly(bFreq)
@@ -279,18 +279,18 @@
 
 	uFreq := new(Poly)
 	for i := range uFreq {
-		uFreq[i] = uint16((int(sPrimeFreq[i])*int(aFreq[i]) + int(ePrimeFreq[i])) % q)
+		uFreq[i] = uint16((uint64(sPrimeFreq[i])*uint64(aFreq[i]) + uint64(ePrimeFreq[i])) % q)
 	}
 
 	vFreq := new(Poly)
 	for i := range vFreq {
-		vFreq[i] = uint16((int(sPrimeFreq[i]) * int(bFreq[i])) % q)
+		vFreq[i] = uint16((uint64(sPrimeFreq[i]) * uint64(bFreq[i])) % q)
 	}
 
 	v := inverseNTT(vFreq)
 	ePrimePrime := sampleNoise(rand)
 	for i := range v {
-		v[i] = uint16((int(v[i]) + int(ePrimePrime[i])) % q)
+		v[i] = uint16((uint64(v[i]) + uint64(ePrimePrime[i])) % q)
 	}
 
 	rec := helprec(rand, v)
@@ -311,7 +311,7 @@
 	rec := decodeRec(acceptMsg[encodedPolyLen:])
 
 	for i, u := range uFreq {
-		uFreq[i] = uint16((int(u) * int(sk[i])) % q)
+		uFreq[i] = uint16((uint64(u) * uint64(sk[i])) % q)
 	}
 	u := inverseNTT(uFreq)
 
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index c10987e..962ac01 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -53,7 +53,8 @@
 	resourceDir     = flag.String("resource-dir", ".", "The directory in which to find certificate and key files.")
 	fuzzer          = flag.Bool("fuzzer", false, "If true, tests against a BoringSSL built in fuzzer mode.")
 	transcriptDir   = flag.String("transcript-dir", "", "The directory in which to write transcripts.")
-	timeout         = flag.Int("timeout", 15, "The number of seconds to wait for a read or write to bssl_shim.")
+	idleTimeout     = flag.Duration("idle-timeout", 15*time.Second, "The number of seconds to wait for a read or write to bssl_shim.")
+	deterministic   = flag.Bool("deterministic", false, "If true, uses a deterministic PRNG in the runner.")
 )
 
 const (
@@ -315,7 +316,7 @@
 }
 
 func doExchange(test *testCase, config *Config, conn net.Conn, isResume bool) error {
-	conn = &timeoutConn{conn, time.Duration(*timeout) * time.Second}
+	conn = &timeoutConn{conn, *idleTimeout}
 
 	if test.protocol == dtls {
 		config.Bugs.PacketAdaptor = newPacketAdaptor(conn)
@@ -765,6 +766,9 @@
 	if *fuzzer {
 		config.Bugs.NullAllCiphers = true
 	}
+	if *deterministic {
+		config.Rand = &deterministicRand{}
+	}
 
 	conn, err := acceptOrWait(listener, waitChan)
 	if err == nil {
@@ -795,6 +799,7 @@
 			if *fuzzer {
 				resumeConfig.Bugs.NullAllCiphers = true
 			}
+			resumeConfig.Rand = config.Rand
 		} else {
 			resumeConfig = config
 		}
@@ -924,6 +929,8 @@
 	{"ECDHE-PSK-AES128-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
 	{"ECDHE-PSK-AES256-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA},
 	{"ECDHE-PSK-CHACHA20-POLY1305", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256},
+	{"ECDHE-PSK-AES128-GCM-SHA256", TLS_ECDHE_PSK_WITH_AES_128_GCM_SHA256},
+	{"ECDHE-PSK-AES256-GCM-SHA384", TLS_ECDHE_PSK_WITH_AES_256_GCM_SHA384},
 	{"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},
@@ -1770,7 +1777,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":UNEXPECTED_MESSAGE:",
+			expectedError: ":BAD_HANDSHAKE_RECORD:",
 		},
 		{
 			protocol: dtls,
@@ -1781,7 +1788,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":EXCESSIVE_MESSAGE_SIZE:",
+			expectedError: ":BAD_HANDSHAKE_RECORD:",
 		},
 		{
 			protocol: dtls,
@@ -1792,7 +1799,7 @@
 				},
 			},
 			shouldFail:    true,
-			expectedError: ":EXCESSIVE_MESSAGE_SIZE:",
+			expectedError: ":BAD_HANDSHAKE_RECORD:",
 		},
 		{
 			protocol: dtls,
@@ -4685,106 +4692,137 @@
 }
 
 func addDTLSRetransmitTests() {
-	// Test that this is indeed the timeout schedule. Stress all
-	// four patterns of handshake.
-	for i := 1; i < len(timeouts); i++ {
-		number := strconv.Itoa(i)
-		testCases = append(testCases, testCase{
+	// These tests work by coordinating some behavior on both the shim and
+	// the runner.
+	//
+	// TimeoutSchedule configures the runner to send a series of timeout
+	// opcodes to the shim (see packetAdaptor) immediately before reading
+	// each peer handshake flight N. The timeout opcode both simulates a
+	// timeout in the shim and acts as a synchronization point to help the
+	// runner bracket each handshake flight.
+	//
+	// We assume the shim does not read from the channel eagerly. It must
+	// first wait until it has sent flight N and is ready to receive
+	// handshake flight N+1. At this point, it will process the timeout
+	// opcode. It must then immediately respond with a timeout ACK and act
+	// as if the shim was idle for the specified amount of time.
+	//
+	// The runner then drops all packets received before the ACK and
+	// continues waiting for flight N. This ordering results in one attempt
+	// at sending flight N to be dropped. For the test to complete, the
+	// shim must send flight N again, testing that the shim implements DTLS
+	// retransmit on a timeout.
+
+	for _, async := range []bool{true, false} {
+		var tests []testCase
+
+		// Test that this is indeed the timeout schedule. Stress all
+		// four patterns of handshake.
+		for i := 1; i < len(timeouts); i++ {
+			number := strconv.Itoa(i)
+			tests = append(tests, testCase{
+				protocol: dtls,
+				name:     "DTLS-Retransmit-Client-" + number,
+				config: Config{
+					Bugs: ProtocolBugs{
+						TimeoutSchedule: timeouts[:i],
+					},
+				},
+				resumeSession: true,
+			})
+			tests = append(tests, testCase{
+				protocol: dtls,
+				testType: serverTest,
+				name:     "DTLS-Retransmit-Server-" + number,
+				config: Config{
+					Bugs: ProtocolBugs{
+						TimeoutSchedule: timeouts[:i],
+					},
+				},
+				resumeSession: true,
+			})
+		}
+
+		// Test that exceeding the timeout schedule hits a read
+		// timeout.
+		tests = append(tests, testCase{
 			protocol: dtls,
-			name:     "DTLS-Retransmit-Client-" + number,
+			name:     "DTLS-Retransmit-Timeout",
 			config: Config{
 				Bugs: ProtocolBugs{
-					TimeoutSchedule: timeouts[:i],
+					TimeoutSchedule: timeouts,
 				},
 			},
 			resumeSession: true,
-			flags:         []string{"-async"},
+			shouldFail:    true,
+			expectedError: ":READ_TIMEOUT_EXPIRED:",
 		})
-		testCases = append(testCases, testCase{
+
+		if async {
+			// Test that timeout handling has a fudge factor, due to API
+			// problems.
+			tests = append(tests, testCase{
+				protocol: dtls,
+				name:     "DTLS-Retransmit-Fudge",
+				config: Config{
+					Bugs: ProtocolBugs{
+						TimeoutSchedule: []time.Duration{
+							timeouts[0] - 10*time.Millisecond,
+						},
+					},
+				},
+				resumeSession: true,
+			})
+		}
+
+		// Test that the final Finished retransmitting isn't
+		// duplicated if the peer badly fragments everything.
+		tests = append(tests, testCase{
+			testType: serverTest,
+			protocol: dtls,
+			name:     "DTLS-Retransmit-Fragmented",
+			config: Config{
+				Bugs: ProtocolBugs{
+					TimeoutSchedule:          []time.Duration{timeouts[0]},
+					MaxHandshakeRecordLength: 2,
+				},
+			},
+		})
+
+		// Test the timeout schedule when a shorter initial timeout duration is set.
+		tests = append(tests, testCase{
+			protocol: dtls,
+			name:     "DTLS-Retransmit-Short-Client",
+			config: Config{
+				Bugs: ProtocolBugs{
+					TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
+				},
+			},
+			resumeSession: true,
+			flags:         []string{"-initial-timeout-duration-ms", "250"},
+		})
+		tests = append(tests, testCase{
 			protocol: dtls,
 			testType: serverTest,
-			name:     "DTLS-Retransmit-Server-" + number,
+			name:     "DTLS-Retransmit-Short-Server",
 			config: Config{
 				Bugs: ProtocolBugs{
-					TimeoutSchedule: timeouts[:i],
+					TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
 				},
 			},
 			resumeSession: true,
-			flags:         []string{"-async"},
+			flags:         []string{"-initial-timeout-duration-ms", "250"},
 		})
+
+		for _, test := range tests {
+			if async {
+				test.name += "-Async"
+				test.flags = append(test.flags, "-async")
+			}
+
+			testCases = append(testCases, test)
+		}
 	}
-
-	// Test that exceeding the timeout schedule hits a read
-	// timeout.
-	testCases = append(testCases, testCase{
-		protocol: dtls,
-		name:     "DTLS-Retransmit-Timeout",
-		config: Config{
-			Bugs: ProtocolBugs{
-				TimeoutSchedule: timeouts,
-			},
-		},
-		resumeSession: true,
-		flags:         []string{"-async"},
-		shouldFail:    true,
-		expectedError: ":READ_TIMEOUT_EXPIRED:",
-	})
-
-	// Test that timeout handling has a fudge factor, due to API
-	// problems.
-	testCases = append(testCases, testCase{
-		protocol: dtls,
-		name:     "DTLS-Retransmit-Fudge",
-		config: Config{
-			Bugs: ProtocolBugs{
-				TimeoutSchedule: []time.Duration{
-					timeouts[0] - 10*time.Millisecond,
-				},
-			},
-		},
-		resumeSession: true,
-		flags:         []string{"-async"},
-	})
-
-	// Test that the final Finished retransmitting isn't
-	// duplicated if the peer badly fragments everything.
-	testCases = append(testCases, testCase{
-		testType: serverTest,
-		protocol: dtls,
-		name:     "DTLS-Retransmit-Fragmented",
-		config: Config{
-			Bugs: ProtocolBugs{
-				TimeoutSchedule:          []time.Duration{timeouts[0]},
-				MaxHandshakeRecordLength: 2,
-			},
-		},
-		flags: []string{"-async"},
-	})
-
-	// Test the timeout schedule when a shorter initial timeout duration is set.
-	testCases = append(testCases, testCase{
-		protocol: dtls,
-		name:     "DTLS-Retransmit-Short-Client",
-		config: Config{
-			Bugs: ProtocolBugs{
-				TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
-			},
-		},
-		resumeSession: true,
-		flags:         []string{"-async", "-initial-timeout-duration-ms", "250"},
-	})
-	testCases = append(testCases, testCase{
-		protocol: dtls,
-		testType: serverTest,
-		name:     "DTLS-Retransmit-Short-Server",
-		config: Config{
-			Bugs: ProtocolBugs{
-				TimeoutSchedule: shortTimeouts[:len(shortTimeouts)-1],
-			},
-		},
-		resumeSession: true,
-		flags:         []string{"-async", "-initial-timeout-duration-ms", "250"},
-	})
 }
 
 func addExportKeyingMaterialTests() {
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index b71da17..e1553e3 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -113,8 +113,10 @@
 
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
+#include <openssl/mem.h>
 
 #include "internal.h"
+#include "../crypto/internal.h"
 
 
 /* kMaxEmptyRecords is the number of consecutive, empty records that will be
@@ -123,6 +125,10 @@
  * forever. */
 static const uint8_t kMaxEmptyRecords = 32;
 
+/* kMaxWarningAlerts is the number of consecutive warning alerts that will be
+ * processed. */
+static const uint8_t kMaxWarningAlerts = 4;
+
 /* ssl_needs_record_splitting returns one if |ssl|'s current outgoing cipher
  * state needs record-splitting and zero otherwise. */
 static int ssl_needs_record_splitting(const SSL *ssl) {
@@ -154,7 +160,7 @@
   }
 }
 
-size_t ssl_seal_prefix_len(const SSL *ssl) {
+size_t ssl_seal_align_prefix_len(const SSL *ssl) {
   if (SSL_IS_DTLS(ssl)) {
     return DTLS1_RT_HEADER_LENGTH +
            SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx);
@@ -170,23 +176,28 @@
 }
 
 size_t ssl_max_seal_overhead(const SSL *ssl) {
+  size_t ret = SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   if (SSL_IS_DTLS(ssl)) {
-    return DTLS1_RT_HEADER_LENGTH +
-           SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
+    ret += DTLS1_RT_HEADER_LENGTH;
   } else {
-    size_t ret = SSL3_RT_HEADER_LENGTH +
-                 SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
-    if (ssl_needs_record_splitting(ssl)) {
-      ret *= 2;
-    }
-    return ret;
+    ret += SSL3_RT_HEADER_LENGTH;
   }
+  /* TLS 1.3 needs an extra byte for the encrypted record type. */
+  if (ssl->s3->have_version &&
+      ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+    ret += 1;
+  }
+  if (!SSL_IS_DTLS(ssl) && ssl_needs_record_splitting(ssl)) {
+    ret *= 2;
+  }
+  return ret;
 }
 
-enum ssl_open_record_t tls_open_record(
-    SSL *ssl, uint8_t *out_type, uint8_t *out, size_t *out_len,
-    size_t *out_consumed, uint8_t *out_alert, size_t max_out, const uint8_t *in,
-    size_t in_len) {
+enum ssl_open_record_t tls_open_record(SSL *ssl, uint8_t *out_type, CBS *out,
+                                       size_t *out_consumed, uint8_t *out_alert,
+                                       uint8_t *in, size_t in_len) {
+  *out_consumed = 0;
+
   CBS cbs;
   CBS_init(&cbs, in, in_len);
 
@@ -200,9 +211,9 @@
     return ssl_open_record_partial;
   }
 
-  /* Check the version. */
-  if ((ssl->s3->have_version && version != ssl->version) ||
-      (version >> 8) != SSL3_VERSION_MAJOR) {
+  /* Check that the major version in the record matches. As of TLS 1.3, the
+   * minor version is no longer checked. */
+  if ((version >> 8) != SSL3_VERSION_MAJOR) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_VERSION_NUMBER);
     *out_alert = SSL_AD_PROTOCOL_VERSION;
     return ssl_open_record_error;
@@ -222,34 +233,46 @@
     return ssl_open_record_partial;
   }
 
-  if (ssl->msg_callback != NULL) {
-    ssl->msg_callback(0 /* read */, 0, SSL3_RT_HEADER, in,
-                      SSL3_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg);
-  }
+  ssl_do_msg_callback(ssl, 0 /* read */, 0, SSL3_RT_HEADER, in,
+                      SSL3_RT_HEADER_LENGTH);
 
-  /* Decrypt the body. */
-  size_t plaintext_len;
-  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, &plaintext_len, max_out,
-                         type, version, ssl->s3->read_sequence, CBS_data(&body),
+  /* Decrypt the body in-place. */
+  if (!SSL_AEAD_CTX_open(ssl->s3->aead_read_ctx, out, type, version,
+                         ssl->s3->read_sequence, (uint8_t *)CBS_data(&body),
                          CBS_len(&body))) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
     *out_alert = SSL_AD_BAD_RECORD_MAC;
     return ssl_open_record_error;
   }
+  *out_consumed = in_len - CBS_len(&cbs);
+
   if (!ssl_record_sequence_update(ssl->s3->read_sequence, 8)) {
     *out_alert = SSL_AD_INTERNAL_ERROR;
     return ssl_open_record_error;
   }
 
+  /* TLS 1.3 hides the record type inside the encrypted data. */
+  if (ssl->s3->have_version &&
+      ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
+      ssl->s3->aead_read_ctx != NULL) {
+    do {
+      if (!CBS_get_last_u8(out, &type)) {
+        OPENSSL_PUT_ERROR(SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
+        *out_alert = SSL_AD_DECRYPT_ERROR;
+        return ssl_open_record_error;
+      }
+    } while (type == 0);
+  }
+
   /* Check the plaintext length. */
-  if (plaintext_len > SSL3_RT_MAX_PLAIN_LENGTH) {
+  if (CBS_len(out) > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_DATA_LENGTH_TOO_LONG);
     *out_alert = SSL_AD_RECORD_OVERFLOW;
     return ssl_open_record_error;
   }
 
   /* Limit the number of consecutive empty records. */
-  if (plaintext_len == 0) {
+  if (CBS_len(out) == 0) {
     ssl->s3->empty_record_count++;
     if (ssl->s3->empty_record_count > kMaxEmptyRecords) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_EMPTY_FRAGMENTS);
@@ -262,9 +285,13 @@
     ssl->s3->empty_record_count = 0;
   }
 
+  if (type == SSL3_RT_ALERT) {
+    return ssl_process_alert(ssl, out_alert, CBS_data(out), CBS_len(out));
+  }
+
+  ssl->s3->warning_alert_count = 0;
+
   *out_type = type;
-  *out_len = plaintext_len;
-  *out_consumed = in_len - CBS_len(&cbs);
   return ssl_open_record_success;
 }
 
@@ -275,20 +302,25 @@
     OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
     return 0;
   }
-  /* Check the record header does not alias any part of the input.
-   * |SSL_AEAD_CTX_seal| will internally enforce other aliasing requirements. */
-  if (in < out + SSL3_RT_HEADER_LENGTH && out < in + in_len) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
-    return 0;
-  }
+
+  /* Either |in| and |out| don't alias or |in| aligns with the
+   * ciphertext. |tls_seal_record| forbids aliasing, but TLS 1.3 aliases them
+   * internally. */
+  assert(!buffers_alias(in, in_len, out, max_out) ||
+         in ==
+             out + SSL3_RT_HEADER_LENGTH +
+                 SSL_AEAD_CTX_explicit_nonce_len(ssl->s3->aead_write_ctx));
 
   out[0] = type;
 
-  /* Some servers hang if initial ClientHello is larger than 256 bytes and
-   * record version number > TLS 1.0. */
-  uint16_t wire_version = ssl->version;
-  if (!ssl->s3->have_version && ssl->version > SSL3_VERSION) {
-    wire_version = TLS1_VERSION;
+  /* The TLS record-layer version number is meaningless and, starting in
+   * TLS 1.3, is frozen at TLS 1.0. But for historical reasons, SSL 3.0
+   * ClientHellos should use SSL 3.0 and pre-TLS-1.3 expects the version
+   * to change after version negotiation. */
+  uint16_t wire_version = TLS1_VERSION;
+  if (ssl->version == SSL3_VERSION ||
+      (ssl->s3->have_version && ssl3_protocol_version(ssl) < TLS1_3_VERSION)) {
+    wire_version = ssl->version;
   }
   out[1] = wire_version >> 8;
   out[2] = wire_version & 0xff;
@@ -311,31 +343,41 @@
 
   *out_len = SSL3_RT_HEADER_LENGTH + ciphertext_len;
 
-  if (ssl->msg_callback) {
-    ssl->msg_callback(1 /* write */, 0, SSL3_RT_HEADER, out,
-                      SSL3_RT_HEADER_LENGTH, ssl, ssl->msg_callback_arg);
-  }
-
+  ssl_do_msg_callback(ssl, 1 /* write */, 0, SSL3_RT_HEADER, out,
+                      SSL3_RT_HEADER_LENGTH);
   return 1;
 }
 
 int tls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
                     uint8_t type, const uint8_t *in, size_t in_len) {
+  if (buffers_alias(in, in_len, out, max_out)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
+    return 0;
+  }
+
   size_t frag_len = 0;
-  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
-      ssl_needs_record_splitting(ssl)) {
-    /* |do_seal_record| will notice if it clobbers |in[0]|, but not if it
-     * aliases the rest of |in|. */
-    if (in + 1 <= out && out < in + in_len) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_OUTPUT_ALIASES_INPUT);
+
+  /* TLS 1.3 hides the actual record type inside the encrypted data. */
+  if (ssl->s3->have_version &&
+      ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
+      ssl->s3->aead_read_ctx != NULL) {
+    size_t padding = SSL3_RT_HEADER_LENGTH + 1;
+
+    if (in_len > in_len + padding || max_out < in_len + padding) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
       return 0;
     }
-    /* Ensure |do_seal_record| does not write beyond |in[0]|. */
-    size_t frag_max_out = max_out;
-    if (out <= in + 1 && in + 1 < out + frag_max_out) {
-      frag_max_out = (size_t)(in + 1 - out);
-    }
-    if (!do_seal_record(ssl, out, &frag_len, frag_max_out, type, in, 1)) {
+
+    memmove(out + SSL3_RT_HEADER_LENGTH, in, in_len);
+    out[SSL3_RT_HEADER_LENGTH + in_len] = type;
+    in = out + SSL3_RT_HEADER_LENGTH;
+    type = SSL3_RT_APPLICATION_DATA;
+    in_len++;
+  }
+
+  if (type == SSL3_RT_APPLICATION_DATA && in_len > 1 &&
+      ssl_needs_record_splitting(ssl)) {
+    if (!do_seal_record(ssl, out, &frag_len, max_out, type, in, 1)) {
       return 0;
     }
     in++;
@@ -379,3 +421,51 @@
   SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
   ssl->s3->aead_write_ctx = aead_ctx;
 }
+
+enum ssl_open_record_t ssl_process_alert(SSL *ssl, uint8_t *out_alert,
+                                         const uint8_t *in, size_t in_len) {
+  /* Alerts records may not contain fragmented or multiple alerts. */
+  if (in_len != 2) {
+    *out_alert = SSL_AD_DECODE_ERROR;
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ALERT);
+    return ssl_open_record_error;
+  }
+
+  ssl_do_msg_callback(ssl, 0 /* read */, ssl->version, SSL3_RT_ALERT, in, in_len);
+
+  const uint8_t alert_level = in[0];
+  const uint8_t alert_descr = in[1];
+
+  uint16_t alert = (alert_level << 8) | alert_descr;
+  ssl_do_info_callback(ssl, SSL_CB_READ_ALERT, alert);
+
+  if (alert_level == SSL3_AL_WARNING) {
+    if (alert_descr == SSL_AD_CLOSE_NOTIFY) {
+      ssl->s3->recv_shutdown = ssl_shutdown_close_notify;
+      return ssl_open_record_close_notify;
+    }
+
+    ssl->s3->warning_alert_count++;
+    if (ssl->s3->warning_alert_count > kMaxWarningAlerts) {
+      *out_alert = SSL_AD_UNEXPECTED_MESSAGE;
+      OPENSSL_PUT_ERROR(SSL, SSL_R_TOO_MANY_WARNING_ALERTS);
+      return ssl_open_record_error;
+    }
+    return ssl_open_record_discard;
+  }
+
+  if (alert_level == SSL3_AL_FATAL) {
+    ssl->s3->recv_shutdown = ssl_shutdown_fatal_alert;
+    SSL_CTX_remove_session(ssl->ctx, ssl->session);
+
+    char tmp[16];
+    OPENSSL_PUT_ERROR(SSL, SSL_AD_REASON_OFFSET + alert_descr);
+    BIO_snprintf(tmp, sizeof(tmp), "%d", alert_descr);
+    ERR_add_error_data(2, "SSL alert number ", tmp);
+    return ssl_open_record_fatal_alert;
+  }
+
+  *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+  OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_ALERT_TYPE);
+  return ssl_open_record_error;
+}
diff --git a/src/tool/digest.cc b/src/tool/digest.cc
index c2cee7f..2e3e608 100644
--- a/src/tool/digest.cc
+++ b/src/tool/digest.cc
@@ -31,9 +31,9 @@
 #define O_BINARY 0
 #endif
 #else
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #include <io.h>
 #define PATH_MAX MAX_PATH
 typedef int ssize_t;
diff --git a/src/tool/internal.h b/src/tool/internal.h
index 7737f4c..fd66e00 100644
--- a/src/tool/internal.h
+++ b/src/tool/internal.h
@@ -20,19 +20,15 @@
 #include <string>
 #include <vector>
 
-#if defined(_MSC_VER)
-#pragma warning(push)
+OPENSSL_MSVC_PRAGMA(warning(push))
 // MSVC issues warning C4702 for unreachable code in its xtree header when
 // compiling with -D_HAS_EXCEPTIONS=0. See
 // https://connect.microsoft.com/VisualStudio/feedback/details/809962
-#pragma warning(disable: 4702)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(disable: 4702))
 
 #include <map>
 
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 #if defined(OPENSSL_WINDOWS)
   #define BORINGSSL_OPEN _open
diff --git a/src/tool/speed.cc b/src/tool/speed.cc
index 58c7382..a8eb8bf 100644
--- a/src/tool/speed.cc
+++ b/src/tool/speed.cc
@@ -30,9 +30,9 @@
 #include <openssl/rsa.h>
 
 #if defined(OPENSSL_WINDOWS)
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <windows.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #elif defined(OPENSSL_APPLE)
 #include <sys/time.h>
 #endif
diff --git a/src/tool/transport_common.cc b/src/tool/transport_common.cc
index be47787..01fcde4 100644
--- a/src/tool/transport_common.cc
+++ b/src/tool/transport_common.cc
@@ -33,10 +33,10 @@
 #include <unistd.h>
 #else
 #include <io.h>
-#pragma warning(push, 3)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#pragma warning(pop)
+OPENSSL_MSVC_PRAGMA(warning(pop))
 
 typedef int ssize_t;
 #pragma comment(lib, "Ws2_32.lib")
diff --git a/src/util/all_tests.json b/src/util/all_tests.json
index 9e3445c..48b540e 100644
--- a/src/util/all_tests.json
+++ b/src/util/all_tests.json
@@ -30,7 +30,7 @@
 	["crypto/cipher/aead_test", "des-ede3-cbc-sha1-ssl3", "crypto/cipher/test/des_ede3_cbc_sha1_ssl3_tests.txt"],
 	["crypto/cipher/aead_test", "aes-128-ctr-hmac-sha256", "crypto/cipher/test/aes_128_ctr_hmac_sha256.txt"],
 	["crypto/cipher/aead_test", "aes-256-ctr-hmac-sha256", "crypto/cipher/test/aes_256_ctr_hmac_sha256.txt"],
-	["crypto/cipher/cipher_test", "crypto/cipher/test/cipher_test.txt"],
+	["crypto/cipher/cipher_test", "crypto/cipher/test/cipher_tests.txt"],
 	["crypto/cmac/cmac_test"],
 	["crypto/constant_time_test"],
 	["crypto/curve25519/ed25519_test", "crypto/curve25519/ed25519_tests.txt"],
@@ -51,11 +51,12 @@
 	["crypto/lhash/lhash_test"],
 	["crypto/modes/gcm_test"],
 	["crypto/newhope/newhope_test"],
-	["crypto/newhope/newhope_vectors_test", "crypto/newhope/newhope_test.txt"],
+	["crypto/newhope/newhope_statistical_test"],
+	["crypto/newhope/newhope_vectors_test", "crypto/newhope/newhope_tests.txt"],
 	["crypto/obj/obj_test"],
 	["crypto/pkcs8/pkcs12_test"],
 	["crypto/pkcs8/pkcs8_test"],
-	["crypto/poly1305/poly1305_test", "crypto/poly1305/poly1305_test.txt"],
+	["crypto/poly1305/poly1305_test", "crypto/poly1305/poly1305_tests.txt"],
 	["crypto/refcount_test"],
 	["crypto/rsa/rsa_test"],
 	["crypto/thread_test"],
diff --git a/src/util/generate_build_files.py b/src/util/generate_build_files.py
index 663d6c7..76c390b 100644
--- a/src/util/generate_build_files.py
+++ b/src/util/generate_build_files.py
@@ -12,10 +12,9 @@
 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-"""Enumerates the BoringSSL source in src/ and either generates two gypi files
-  (boringssl.gypi and boringssl_tests.gypi) for Chromium, or generates
-  source-list files for Android."""
+"""Enumerates source files for consumption by various build systems."""
 
+import optparse
 import os
 import subprocess
 import sys
@@ -50,6 +49,12 @@
     ],
 }
 
+PREFIX = None
+
+
+def PathOf(x):
+  return x if not PREFIX else os.path.join(PREFIX, x)
+
 
 class Android(object):
 
@@ -121,7 +126,7 @@
 
     out.write('%s = [\n' % name)
     for f in sorted(files):
-      out.write('    "%s",\n' % f)
+      out.write('    "%s",\n' % PathOf(f))
     out.write(']\n')
 
   def WriteFiles(self, files, asm_outputs):
@@ -149,13 +154,13 @@
       for filename in files['test_support']:
         if os.path.basename(filename) == 'malloc.cc':
           continue
-        out.write('    "%s",\n' % filename)
+        out.write('    "%s",\n' % PathOf(filename))
 
       out.write(']\n\n')
 
       out.write('def create_tests(copts):\n')
       out.write('  test_support_sources_complete = test_support_sources + \\\n')
-      out.write('      native.glob(["src/crypto/test/*.h"])\n')
+      out.write('      native.glob(["%s"])\n' % PathOf("src/crypto/test/*.h"))
       name_counts = {}
       for test in files['tests']:
         name = os.path.basename(test[0])
@@ -185,7 +190,8 @@
         out.write('  native.cc_test(\n')
         out.write('      name = "%s",\n' % name)
         out.write('      size = "small",\n')
-        out.write('      srcs = ["%s"] + test_support_sources_complete,\n' % src)
+        out.write('      srcs = ["%s"] + test_support_sources_complete,\n' %
+            PathOf(src))
 
         data_files = []
         if len(test) > 1:
@@ -193,7 +199,8 @@
           out.write('      args = [\n')
           for arg in test[1:]:
             if '/' in arg:
-              out.write('          "$(location src/%s)",\n' % arg)
+              out.write('          "$(location %s)",\n' %
+                  PathOf(os.path.join('src', arg)))
               data_files.append('src/%s' % arg)
             else:
               out.write('          "%s",\n' % arg)
@@ -204,7 +211,7 @@
         if len(data_files) > 0:
           out.write('      data = [\n')
           for filename in data_files:
-            out.write('          "%s",\n' % filename)
+            out.write('          "%s",\n' % PathOf(filename))
           out.write('      ],\n')
 
         if 'ssl/' in test[0]:
@@ -609,17 +616,20 @@
   return 0
 
 
-def Usage():
-  print 'Usage: python %s [android|android-standalone|bazel|gn|gyp]' % sys.argv[0]
-  sys.exit(1)
-
-
 if __name__ == '__main__':
-  if len(sys.argv) < 2:
-    Usage()
+  parser = optparse.OptionParser(usage='Usage: %prog [--prefix=<path>]'
+      ' [android|android-standalone|bazel|gn|gyp]')
+  parser.add_option('--prefix', dest='prefix',
+      help='For Bazel, prepend argument to all source files')
+  options, args = parser.parse_args(sys.argv[1:])
+  PREFIX = options.prefix
+
+  if not args:
+    parser.print_help()
+    sys.exit(1)
 
   platforms = []
-  for s in sys.argv[1:]:
+  for s in args:
     if s == 'android':
       platforms.append(Android())
     elif s == 'android-standalone':
@@ -631,6 +641,7 @@
     elif s == 'gyp':
       platforms.append(GYP())
     else:
-      Usage()
+      parser.print_help()
+      sys.exit(1)
 
   sys.exit(main(platforms))
diff --git a/src/util/run_android_tests.go b/src/util/run_android_tests.go
index fc94d33..6502a16 100644
--- a/src/util/run_android_tests.go
+++ b/src/util/run_android_tests.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bytes"
 	"encoding/json"
 	"flag"
 	"fmt"
@@ -23,6 +24,7 @@
 	"os"
 	"os/exec"
 	"path/filepath"
+	"strconv"
 	"strings"
 )
 
@@ -56,6 +58,55 @@
 	return cmd.Run()
 }
 
+func adbShell(shellCmd string) (int, error) {
+	var args []string
+	if len(*device) > 0 {
+		args = append([]string{"-s", *device}, args...)
+	}
+	args = append(args, "shell")
+
+	const delimiter = "___EXIT_CODE___"
+
+	// Older versions of adb and Android do not preserve the exit
+	// code, so work around this.
+	// https://code.google.com/p/android/issues/detail?id=3254
+	shellCmd += "; echo " + delimiter + " $?"
+	args = append(args, shellCmd)
+
+	cmd := exec.Command(*adbPath, args...)
+	stdout, err := cmd.StdoutPipe()
+	if err != nil {
+		return 0, err
+	}
+	cmd.Stderr = os.Stderr
+	if err := cmd.Start(); err != nil {
+		return 0, err
+	}
+
+	var stdoutBytes bytes.Buffer
+	for {
+		var buf [1024]byte
+		n, err := stdout.Read(buf[:])
+		stdoutBytes.Write(buf[:n])
+		os.Stdout.Write(buf[:n])
+		if err != nil {
+			break
+		}
+	}
+
+	if err := cmd.Wait(); err != nil {
+		return 0, err
+	}
+
+	stdoutStr := stdoutBytes.String()
+	idx := strings.LastIndex(stdoutStr, delimiter)
+	if idx < 0 {
+		return 0, fmt.Errorf("Could not find delimiter in output.")
+	}
+
+	return strconv.Atoi(strings.TrimSpace(stdoutStr[idx+len(delimiter):]))
+}
+
 func goTool(args ...string) error {
 	cmd := exec.Command("go", args...)
 	cmd.Stdout = os.Stdout
@@ -227,17 +278,21 @@
 		os.Exit(1)
 	}
 
+	var unitTestExit int
 	if enableUnitTests() {
 		fmt.Printf("Running unit tests...\n")
-		if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp && ./util/all_tests -json-output results.json %s", *allTestsArgs)); err != nil {
+		unitTestExit, err = adbShell(fmt.Sprintf("cd /data/local/tmp/boringssl-tmp && ./util/all_tests -json-output results.json %s", *allTestsArgs))
+		if err != nil {
 			fmt.Printf("Failed to run unit tests: %s\n", err)
 			os.Exit(1)
 		}
 	}
 
+	var sslTestExit int
 	if enableSSLTests() {
 		fmt.Printf("Running SSL tests...\n")
-		if err := adb("shell", fmt.Sprintf("cd /data/local/tmp/boringssl-tmp/ssl/test/runner && ./runner -json-output ../../../results.json %s", *runnerArgs)); err != nil {
+		sslTestExit, err = adbShell(fmt.Sprintf("cd /data/local/tmp/boringssl-tmp/ssl/test/runner && ./runner -json-output ../../../results.json %s", *runnerArgs))
+		if err != nil {
 			fmt.Printf("Failed to run SSL tests: %s\n", err)
 			os.Exit(1)
 		}
@@ -249,4 +304,12 @@
 			os.Exit(1)
 		}
 	}
+
+	if unitTestExit != 0 {
+		os.Exit(unitTestExit)
+	}
+
+	if sslTestExit != 0 {
+		os.Exit(sslTestExit)
+	}
 }
diff --git a/win-x86/crypto/chacha/chacha-x86.asm b/win-x86/crypto/chacha/chacha-x86.asm
index 283c8e4..3ba31a2 100644
--- a/win-x86/crypto/chacha/chacha-x86.asm
+++ b/win-x86/crypto/chacha/chacha-x86.asm
@@ -272,13 +272,11 @@
 	xor	esi,DWORD [36+ebx]
 	xor	edx,DWORD [48+ebx]
 	xor	edi,DWORD [56+ebx]
-	mov	DWORD [16+esp],ebp
-	mov	ebp,DWORD [esp]
-	mov	DWORD [32+esp],ecx
-	mov	DWORD [36+esp],esi
-	mov	DWORD [48+esp],edx
-	mov	DWORD [56+esp],edi
-	mov	DWORD [eax],ebp
+	mov	DWORD [16+eax],ebp
+	mov	DWORD [32+eax],ecx
+	mov	DWORD [36+eax],esi
+	mov	DWORD [48+eax],edx
+	mov	DWORD [56+eax],edi
 	mov	ebp,DWORD [4+esp]
 	mov	ecx,DWORD [8+esp]
 	mov	esi,DWORD [12+esp]
@@ -295,42 +293,34 @@
 	xor	edx,DWORD [20+ebx]
 	xor	edi,DWORD [24+ebx]
 	mov	DWORD [4+eax],ebp
-	mov	ebp,DWORD [16+esp]
 	mov	DWORD [8+eax],ecx
 	mov	DWORD [12+eax],esi
-	mov	DWORD [16+eax],ebp
 	mov	DWORD [20+eax],edx
 	mov	DWORD [24+eax],edi
-	mov	ecx,DWORD [28+esp]
-	mov	edx,DWORD [32+esp]
-	mov	edi,DWORD [36+esp]
-	add	ecx,DWORD [92+esp]
-	mov	ebp,DWORD [40+esp]
-	xor	ecx,DWORD [28+ebx]
+	mov	ebp,DWORD [28+esp]
+	mov	ecx,DWORD [40+esp]
 	mov	esi,DWORD [44+esp]
-	mov	DWORD [28+eax],ecx
-	mov	DWORD [32+eax],edx
-	mov	DWORD [36+eax],edi
-	add	ebp,DWORD [104+esp]
-	add	esi,DWORD [108+esp]
-	xor	ebp,DWORD [40+ebx]
-	xor	esi,DWORD [44+ebx]
-	mov	DWORD [40+eax],ebp
-	mov	DWORD [44+eax],esi
-	mov	ecx,DWORD [48+esp]
-	mov	esi,DWORD [56+esp]
 	mov	edx,DWORD [52+esp]
 	mov	edi,DWORD [60+esp]
+	add	ebp,DWORD [92+esp]
+	add	ecx,DWORD [104+esp]
+	add	esi,DWORD [108+esp]
 	add	edx,DWORD [116+esp]
 	add	edi,DWORD [124+esp]
+	xor	ebp,DWORD [28+ebx]
+	xor	ecx,DWORD [40+ebx]
+	xor	esi,DWORD [44+ebx]
 	xor	edx,DWORD [52+ebx]
 	xor	edi,DWORD [60+ebx]
 	lea	ebx,[64+ebx]
-	mov	DWORD [48+eax],ecx
+	mov	DWORD [28+eax],ebp
+	mov	ebp,DWORD [esp]
+	mov	DWORD [40+eax],ecx
 	mov	ecx,DWORD [160+esp]
+	mov	DWORD [44+eax],esi
 	mov	DWORD [52+eax],edx
-	mov	DWORD [56+eax],esi
 	mov	DWORD [60+eax],edi
+	mov	DWORD [eax],ebp
 	lea	eax,[64+eax]
 	sub	ecx,64
 	jnz	NEAR L$003outer_loop
@@ -723,14 +713,24 @@
 	punpcklqdq	xmm6,xmm7
 	punpckhqdq	xmm1,xmm2
 	punpckhqdq	xmm3,xmm7
-	movdqa	[ebx-128],xmm0
+	movdqu	xmm4,[esi-128]
+	movdqu	xmm5,[esi-64]
+	movdqu	xmm2,[esi]
+	movdqu	xmm7,[64+esi]
+	lea	esi,[16+esi]
+	pxor	xmm4,xmm0
 	movdqa	xmm0,[ebx-64]
-	movdqa	[ebx-112],xmm1
-	movdqa	[ebx-96],xmm6
-	movdqa	[ebx-80],xmm3
+	pxor	xmm5,xmm1
 	movdqa	xmm1,[ebx-48]
+	pxor	xmm6,xmm2
 	movdqa	xmm2,[ebx-32]
+	pxor	xmm7,xmm3
 	movdqa	xmm3,[ebx-16]
+	movdqu	[edi-128],xmm4
+	movdqu	[edi-64],xmm5
+	movdqu	[edi],xmm6
+	movdqu	[64+edi],xmm7
+	lea	edi,[16+edi]
 	paddd	xmm0,[ebp-64]
 	paddd	xmm1,[ebp-48]
 	paddd	xmm2,[ebp-32]
@@ -747,14 +747,24 @@
 	punpcklqdq	xmm6,xmm7
 	punpckhqdq	xmm1,xmm2
 	punpckhqdq	xmm3,xmm7
-	movdqa	[ebx-64],xmm0
+	movdqu	xmm4,[esi-128]
+	movdqu	xmm5,[esi-64]
+	movdqu	xmm2,[esi]
+	movdqu	xmm7,[64+esi]
+	lea	esi,[16+esi]
+	pxor	xmm4,xmm0
 	movdqa	xmm0,[ebx]
-	movdqa	[ebx-48],xmm1
-	movdqa	[ebx-32],xmm6
-	movdqa	[ebx-16],xmm3
+	pxor	xmm5,xmm1
 	movdqa	xmm1,[16+ebx]
+	pxor	xmm6,xmm2
 	movdqa	xmm2,[32+ebx]
+	pxor	xmm7,xmm3
 	movdqa	xmm3,[48+ebx]
+	movdqu	[edi-128],xmm4
+	movdqu	[edi-64],xmm5
+	movdqu	[edi],xmm6
+	movdqu	[64+edi],xmm7
+	lea	edi,[16+edi]
 	paddd	xmm0,[ebp]
 	paddd	xmm1,[16+ebp]
 	paddd	xmm2,[32+ebp]
@@ -771,14 +781,24 @@
 	punpcklqdq	xmm6,xmm7
 	punpckhqdq	xmm1,xmm2
 	punpckhqdq	xmm3,xmm7
-	movdqa	[ebx],xmm0
+	movdqu	xmm4,[esi-128]
+	movdqu	xmm5,[esi-64]
+	movdqu	xmm2,[esi]
+	movdqu	xmm7,[64+esi]
+	lea	esi,[16+esi]
+	pxor	xmm4,xmm0
 	movdqa	xmm0,[64+ebx]
-	movdqa	[16+ebx],xmm1
-	movdqa	[32+ebx],xmm6
-	movdqa	[48+ebx],xmm3
+	pxor	xmm5,xmm1
 	movdqa	xmm1,[80+ebx]
+	pxor	xmm6,xmm2
 	movdqa	xmm2,[96+ebx]
+	pxor	xmm7,xmm3
 	movdqa	xmm3,[112+ebx]
+	movdqu	[edi-128],xmm4
+	movdqu	[edi-64],xmm5
+	movdqu	[edi],xmm6
+	movdqu	[64+edi],xmm7
+	lea	edi,[16+edi]
 	paddd	xmm0,[64+ebp]
 	paddd	xmm1,[80+ebp]
 	paddd	xmm2,[96+ebp]
@@ -795,60 +815,20 @@
 	punpcklqdq	xmm6,xmm7
 	punpckhqdq	xmm1,xmm2
 	punpckhqdq	xmm3,xmm7
-	movdqa	[64+ebx],xmm0
-	movdqa	[80+ebx],xmm1
-	movdqa	[96+ebx],xmm6
-	movdqa	[112+ebx],xmm3
-	movdqu	xmm0,[esi-128]
-	movdqu	xmm1,[esi-112]
-	movdqu	xmm2,[esi-96]
-	movdqu	xmm3,[esi-80]
-	pxor	xmm0,[ebx-128]
-	pxor	xmm1,[ebx-64]
-	pxor	xmm2,[ebx]
-	pxor	xmm3,[64+ebx]
-	movdqu	[edi-128],xmm0
-	movdqu	[edi-112],xmm1
-	movdqu	[edi-96],xmm2
-	movdqu	[edi-80],xmm3
-	movdqu	xmm0,[esi-64]
-	movdqu	xmm1,[esi-48]
-	movdqu	xmm2,[esi-32]
-	movdqu	xmm3,[esi-16]
-	pxor	xmm0,[ebx-112]
-	pxor	xmm1,[ebx-48]
-	pxor	xmm2,[16+ebx]
-	pxor	xmm3,[80+ebx]
-	movdqu	[edi-64],xmm0
-	movdqu	[edi-48],xmm1
-	movdqu	[edi-32],xmm2
-	movdqu	[edi-16],xmm3
-	movdqu	xmm0,[esi]
-	movdqu	xmm1,[16+esi]
-	movdqu	xmm2,[32+esi]
-	movdqu	xmm3,[48+esi]
-	pxor	xmm0,[ebx-96]
-	pxor	xmm1,[ebx-32]
-	pxor	xmm2,[32+ebx]
-	pxor	xmm3,[96+ebx]
-	movdqu	[edi],xmm0
-	movdqu	[16+edi],xmm1
-	movdqu	[32+edi],xmm2
-	movdqu	[48+edi],xmm3
-	movdqu	xmm0,[64+esi]
-	movdqu	xmm1,[80+esi]
-	movdqu	xmm2,[96+esi]
-	movdqu	xmm3,[112+esi]
-	pxor	xmm0,[ebx-80]
-	pxor	xmm1,[ebx-16]
-	pxor	xmm2,[48+ebx]
-	pxor	xmm3,[112+ebx]
-	movdqu	[64+edi],xmm0
-	movdqu	[80+edi],xmm1
-	movdqu	[96+edi],xmm2
-	movdqu	[112+edi],xmm3
-	lea	esi,[256+esi]
-	lea	edi,[256+edi]
+	movdqu	xmm4,[esi-128]
+	movdqu	xmm5,[esi-64]
+	movdqu	xmm2,[esi]
+	movdqu	xmm7,[64+esi]
+	lea	esi,[208+esi]
+	pxor	xmm4,xmm0
+	pxor	xmm5,xmm1
+	pxor	xmm6,xmm2
+	pxor	xmm7,xmm3
+	movdqu	[edi-128],xmm4
+	movdqu	[edi-64],xmm5
+	movdqu	[edi],xmm6
+	movdqu	[64+edi],xmm7
+	lea	edi,[208+edi]
 	sub	ecx,256
 	jnc	NEAR L$009outer_loop
 	add	ecx,256