external/boringssl: Sync to b2ff2623a88a65fd4db42d3820f3d8c64e8ab180.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/6d50f475e319de153a43e1dba5a1beca95948c63..b2ff2623a88a65fd4db42d3820f3d8c64e8ab180

Change-Id: I649281e093369d99e863b4882a2ff6a5ad8a64d1
Test: ATP's cts/libcore/gce-net (go/gce-net)
diff --git a/BORINGSSL_REVISION b/BORINGSSL_REVISION
index d7d248e..398c360 100644
--- a/BORINGSSL_REVISION
+++ b/BORINGSSL_REVISION
@@ -1 +1 @@
-6d50f475e319de153a43e1dba5a1beca95948c63
+b2ff2623a88a65fd4db42d3820f3d8c64e8ab180
diff --git a/linux-x86_64/crypto/bn/x86_64-mont5.S b/linux-x86_64/crypto/bn/x86_64-mont5.S
index 554df1f..5d7502c 100644
--- a/linux-x86_64/crypto/bn/x86_64-mont5.S
+++ b/linux-x86_64/crypto/bn/x86_64-mont5.S
@@ -1826,6 +1826,7 @@
 
 .align	32
 .L8x_tail_done:
+	xorq	%rax,%rax
 	addq	(%rdx),%r8
 	adcq	$0,%r9
 	adcq	$0,%r10
@@ -1834,9 +1835,7 @@
 	adcq	$0,%r13
 	adcq	$0,%r14
 	adcq	$0,%r15
-
-
-	xorq	%rax,%rax
+	adcq	$0,%rax
 
 	negq	%rsi
 .L8x_no_tail:
diff --git a/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S b/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
index 889c535..241d7d0 100644
--- a/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
+++ b/linux-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
@@ -61,7 +61,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -74,7 +74,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -108,7 +108,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -121,7 +121,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -140,7 +140,7 @@
 	adcq	%r9,%r11
 	adcq	$0,%r12
 
-	leaq	(1*16)(%rcx),%rcx
+	leaq	16(%rcx),%rcx
 	subq	$16,%r8
 	jmp	hash_ad_loop
 hash_ad_tail:
@@ -170,7 +170,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -183,7 +183,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -238,7 +238,6 @@
 .cfi_offset	r13, -40
 .cfi_offset	r14, -48
 .cfi_offset	r15, -56
-.cfi_offset	%r9, -64
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
 	movq	%rdx,8+32(%rbp)
@@ -406,7 +405,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	.rol8(%rip),%xmm8
@@ -459,7 +458,7 @@
 	pslld	$32-25,%xmm4
 	pxor	%xmm8,%xmm4
 	movdqa	80(%rbp),%xmm8
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 .byte	102,15,58,15,255,4
@@ -594,7 +593,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -607,7 +606,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -727,7 +726,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -740,7 +739,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -845,7 +844,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -858,7 +857,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1032,7 +1031,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1045,7 +1044,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1209,7 +1208,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1222,7 +1221,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1253,7 +1252,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1266,7 +1265,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1431,7 +1430,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	%xmm9,80(%rbp)
@@ -1510,7 +1509,7 @@
 .byte	102,15,58,15,237,12
 .byte	102,69,15,58,15,201,8
 .byte	102,69,15,58,15,237,4
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	paddd	%xmm6,%xmm2
@@ -1590,7 +1589,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1603,7 +1602,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1742,7 +1741,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1755,7 +1754,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1786,7 +1785,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1799,7 +1798,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2042,7 +2041,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2055,7 +2054,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2120,7 +2119,6 @@
 .cfi_offset	r13, -40
 .cfi_offset	r14, -48
 .cfi_offset	r15, -56
-.cfi_offset	%r9, -64
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
 	movq	%rdx,8+32(%rbp)
@@ -2508,7 +2506,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	.rol8(%rip),%xmm8
@@ -2561,7 +2559,7 @@
 	pslld	$32-25,%xmm4
 	pxor	%xmm8,%xmm4
 	movdqa	80(%rbp),%xmm8
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 .byte	102,15,58,15,255,4
@@ -2697,7 +2695,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2710,7 +2708,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2843,7 +2841,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2856,7 +2854,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2929,7 +2927,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2942,7 +2940,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3001,7 +2999,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3014,7 +3012,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3087,7 +3085,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3100,7 +3098,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3224,7 +3222,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3237,7 +3235,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3331,7 +3329,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3344,7 +3342,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3485,7 +3483,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3498,7 +3496,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3542,7 +3540,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3555,7 +3553,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3627,7 +3625,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3640,7 +3638,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3670,7 +3668,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3683,7 +3681,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3997,7 +3995,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4010,7 +4008,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4085,7 +4083,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -4103,7 +4101,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -4167,7 +4165,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vmovdqa	%ymm8,128(%rbp)
@@ -4194,7 +4192,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$12,%ymm14,%ymm14,%ymm14
 	vpalignr	$4,%ymm5,%ymm5,%ymm5
@@ -4274,7 +4272,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -4291,7 +4289,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpaddd	%ymm12,%ymm8,%ymm8
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
@@ -4385,7 +4383,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4398,7 +4396,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4450,7 +4448,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4463,7 +4461,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4528,7 +4526,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4541,7 +4539,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4648,7 +4646,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4657,7 +4655,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4795,7 +4793,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4804,7 +4802,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4894,7 +4892,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4903,7 +4901,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4991,7 +4989,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5004,7 +5002,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5102,7 +5100,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5111,7 +5109,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5215,7 +5213,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5228,7 +5226,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5293,7 +5291,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5302,7 +5300,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5375,7 +5373,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5384,7 +5382,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5506,7 +5504,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5515,7 +5513,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5758,7 +5756,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5771,7 +5769,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5799,7 +5797,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5812,7 +5810,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5862,7 +5860,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5875,7 +5873,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -6632,7 +6630,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -6650,7 +6648,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -6716,7 +6714,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vmovdqa	%ymm8,128(%rbp)
@@ -6743,7 +6741,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$12,%ymm14,%ymm14,%ymm14
 	vpalignr	$4,%ymm5,%ymm5,%ymm5
@@ -6823,7 +6821,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -6840,7 +6838,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpaddd	%ymm12,%ymm8,%ymm8
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
@@ -6935,7 +6933,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -6948,7 +6946,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7000,7 +6998,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7013,7 +7011,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7058,7 +7056,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7071,7 +7069,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7099,7 +7097,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7112,7 +7110,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7156,7 +7154,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7169,7 +7167,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7219,7 +7217,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7232,7 +7230,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7279,7 +7277,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7292,7 +7290,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7355,7 +7353,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7368,7 +7366,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7437,7 +7435,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7450,7 +7448,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7516,7 +7514,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7529,7 +7527,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7616,7 +7614,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7629,7 +7627,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7698,7 +7696,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7711,7 +7709,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7777,7 +7775,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7790,7 +7788,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7930,7 +7928,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -7939,7 +7937,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8007,7 +8005,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpaddd	%ymm4,%ymm0,%ymm0
@@ -8036,7 +8034,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpslld	$32-25,%ymm7,%ymm7
 	vpxor	%ymm8,%ymm7,%ymm7
 	vpsrld	$25,%ymm6,%ymm8
@@ -8141,7 +8139,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -8170,7 +8168,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$4,%ymm14,%ymm14,%ymm14
 	vpalignr	$12,%ymm5,%ymm5,%ymm5
@@ -8590,7 +8588,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8603,7 +8601,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8644,7 +8642,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8657,7 +8655,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8685,7 +8683,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8698,7 +8696,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8746,7 +8744,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8759,7 +8757,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
diff --git a/mac-x86_64/crypto/bn/x86_64-mont5.S b/mac-x86_64/crypto/bn/x86_64-mont5.S
index f3ad8d7..a154cc8 100644
--- a/mac-x86_64/crypto/bn/x86_64-mont5.S
+++ b/mac-x86_64/crypto/bn/x86_64-mont5.S
@@ -1825,6 +1825,7 @@
 
 .p2align	5
 L$8x_tail_done:
+	xorq	%rax,%rax
 	addq	(%rdx),%r8
 	adcq	$0,%r9
 	adcq	$0,%r10
@@ -1833,9 +1834,7 @@
 	adcq	$0,%r13
 	adcq	$0,%r14
 	adcq	$0,%r15
-
-
-	xorq	%rax,%rax
+	adcq	$0,%rax
 
 	negq	%rsi
 L$8x_no_tail:
diff --git a/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S b/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
index 20a7838..4e5c0f3 100644
--- a/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
+++ b/mac-x86_64/crypto/cipher/chacha20_poly1305_x86_64.S
@@ -60,7 +60,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -73,7 +73,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -107,7 +107,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -120,7 +120,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -139,7 +139,7 @@
 	adcq	%r9,%r11
 	adcq	$0,%r12
 
-	leaq	(1*16)(%rcx),%rcx
+	leaq	16(%rcx),%rcx
 	subq	$16,%r8
 	jmp	hash_ad_loop
 hash_ad_tail:
@@ -169,7 +169,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -182,7 +182,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -237,7 +237,6 @@
 .cfi_offset	r13, -40
 .cfi_offset	r14, -48
 .cfi_offset	r15, -56
-.cfi_offset	%r9, -64
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
 	movq	%rdx,8+32(%rbp)
@@ -405,7 +404,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	.rol8(%rip),%xmm8
@@ -458,7 +457,7 @@
 	pslld	$32-25,%xmm4
 	pxor	%xmm8,%xmm4
 	movdqa	80(%rbp),%xmm8
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 .byte	102,15,58,15,255,4
@@ -593,7 +592,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -606,7 +605,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -726,7 +725,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -739,7 +738,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -844,7 +843,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -857,7 +856,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1031,7 +1030,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1044,7 +1043,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1208,7 +1207,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1221,7 +1220,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1252,7 +1251,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1265,7 +1264,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1430,7 +1429,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	%xmm9,80(%rbp)
@@ -1509,7 +1508,7 @@
 .byte	102,15,58,15,237,12
 .byte	102,69,15,58,15,201,8
 .byte	102,69,15,58,15,237,4
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	paddd	%xmm6,%xmm2
@@ -1589,7 +1588,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1602,7 +1601,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1741,7 +1740,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1754,7 +1753,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -1785,7 +1784,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -1798,7 +1797,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2041,7 +2040,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2054,7 +2053,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2119,7 +2118,6 @@
 .cfi_offset	r13, -40
 .cfi_offset	r14, -48
 .cfi_offset	r15, -56
-.cfi_offset	%r9, -64
 	leaq	32(%rsp),%rbp
 	andq	$-32,%rbp
 	movq	%rdx,8+32(%rbp)
@@ -2507,7 +2505,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movdqa	.rol8(%rip),%xmm8
@@ -2560,7 +2558,7 @@
 	pslld	$32-25,%xmm4
 	pxor	%xmm8,%xmm4
 	movdqa	80(%rbp),%xmm8
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 .byte	102,15,58,15,255,4
@@ -2696,7 +2694,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2709,7 +2707,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2842,7 +2840,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2855,7 +2853,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -2928,7 +2926,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -2941,7 +2939,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3000,7 +2998,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3013,7 +3011,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3086,7 +3084,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3099,7 +3097,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3223,7 +3221,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3236,7 +3234,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3330,7 +3328,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3343,7 +3341,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3484,7 +3482,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3497,7 +3495,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3541,7 +3539,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3554,7 +3552,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3626,7 +3624,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3639,7 +3637,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3669,7 +3667,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -3682,7 +3680,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -3996,7 +3994,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4009,7 +4007,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4084,7 +4082,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -4102,7 +4100,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -4166,7 +4164,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vmovdqa	%ymm8,128(%rbp)
@@ -4193,7 +4191,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$12,%ymm14,%ymm14,%ymm14
 	vpalignr	$4,%ymm5,%ymm5,%ymm5
@@ -4273,7 +4271,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -4290,7 +4288,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpaddd	%ymm12,%ymm8,%ymm8
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
@@ -4384,7 +4382,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4397,7 +4395,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4449,7 +4447,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4462,7 +4460,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4527,7 +4525,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -4540,7 +4538,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4647,7 +4645,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4656,7 +4654,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4794,7 +4792,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4803,7 +4801,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4893,7 +4891,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -4902,7 +4900,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -4990,7 +4988,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5003,7 +5001,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5101,7 +5099,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5110,7 +5108,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5214,7 +5212,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5227,7 +5225,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5292,7 +5290,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5301,7 +5299,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5374,7 +5372,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5383,7 +5381,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5505,7 +5503,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -5514,7 +5512,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5757,7 +5755,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5770,7 +5768,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5798,7 +5796,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5811,7 +5809,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -5861,7 +5859,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -5874,7 +5872,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -6631,7 +6629,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -6649,7 +6647,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -6715,7 +6713,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vmovdqa	%ymm8,128(%rbp)
@@ -6742,7 +6740,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$12,%ymm14,%ymm14,%ymm14
 	vpalignr	$4,%ymm5,%ymm5,%ymm5
@@ -6822,7 +6820,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpshufb	%ymm8,%ymm15,%ymm15
@@ -6839,7 +6837,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpaddd	%ymm12,%ymm8,%ymm8
 	vpxor	%ymm11,%ymm7,%ymm7
 	vpxor	%ymm10,%ymm6,%ymm6
@@ -6934,7 +6932,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -6947,7 +6945,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -6999,7 +6997,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7012,7 +7010,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7057,7 +7055,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7070,7 +7068,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7098,7 +7096,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7111,7 +7109,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7155,7 +7153,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7168,7 +7166,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7218,7 +7216,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7231,7 +7229,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7278,7 +7276,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7291,7 +7289,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7354,7 +7352,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7367,7 +7365,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7436,7 +7434,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7449,7 +7447,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7515,7 +7513,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7528,7 +7526,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7615,7 +7613,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7628,7 +7626,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7697,7 +7695,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7710,7 +7708,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7776,7 +7774,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -7789,7 +7787,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -7929,7 +7927,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rdx
@@ -7938,7 +7936,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	addq	%rax,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8006,7 +8004,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpaddd	%ymm4,%ymm0,%ymm0
@@ -8035,7 +8033,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpslld	$32-25,%ymm7,%ymm7
 	vpxor	%ymm8,%ymm7,%ymm7
 	vpsrld	$25,%ymm6,%ymm8
@@ -8140,7 +8138,7 @@
 	movq	%rdx,%r15
 	mulxq	%r10,%r13,%r14
 	mulxq	%r11,%rax,%rdx
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	vpxor	%ymm9,%ymm5,%ymm5
@@ -8169,7 +8167,7 @@
 	mulxq	%r11,%r11,%r9
 	adcq	%r11,%r15
 	adcq	$0,%r9
-	imul	%r12,%rdx
+	imulq	%r12,%rdx
 	vpalignr	$8,%ymm10,%ymm10,%ymm10
 	vpalignr	$4,%ymm14,%ymm14,%ymm14
 	vpalignr	$12,%ymm5,%ymm5,%ymm5
@@ -8589,7 +8587,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8602,7 +8600,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8643,7 +8641,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8656,7 +8654,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8684,7 +8682,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8697,7 +8695,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
@@ -8745,7 +8743,7 @@
 	movq	%rdx,%r14
 	movq	0+0(%rbp),%rax
 	mulq	%r11
-	imul	%r12,%r15
+	imulq	%r12,%r15
 	addq	%rax,%r14
 	adcq	%rdx,%r15
 	movq	8+0(%rbp),%rax
@@ -8758,7 +8756,7 @@
 	mulq	%r11
 	addq	%rax,%r15
 	adcq	$0,%rdx
-	imul	%r12,%r9
+	imulq	%r12,%r9
 	addq	%r10,%r15
 	adcq	%rdx,%r9
 	movq	%r13,%r10
diff --git a/sources.bp b/sources.bp
index 31fd493..99720a5 100644
--- a/sources.bp
+++ b/sources.bp
@@ -56,7 +56,6 @@
         "src/crypto/base64/base64.c",
         "src/crypto/bio/bio.c",
         "src/crypto/bio/bio_mem.c",
-        "src/crypto/bio/buffer.c",
         "src/crypto/bio/connect.c",
         "src/crypto/bio/fd.c",
         "src/crypto/bio/file.c",
@@ -398,6 +397,7 @@
 cc_defaults {
     name: "libssl_sources",
     srcs: [
+        "src/ssl/bio_ssl.c",
         "src/ssl/custom_extensions.c",
         "src/ssl/d1_both.c",
         "src/ssl/d1_lib.c",
diff --git a/sources.mk b/sources.mk
index e370f6e..ebea6b9 100644
--- a/sources.mk
+++ b/sources.mk
@@ -54,7 +54,6 @@
   src/crypto/base64/base64.c\
   src/crypto/bio/bio.c\
   src/crypto/bio/bio_mem.c\
-  src/crypto/bio/buffer.c\
   src/crypto/bio/connect.c\
   src/crypto/bio/fd.c\
   src/crypto/bio/file.c\
diff --git a/src/crypto/bio/CMakeLists.txt b/src/crypto/bio/CMakeLists.txt
index 7859b58..49b9d76 100644
--- a/src/crypto/bio/CMakeLists.txt
+++ b/src/crypto/bio/CMakeLists.txt
@@ -7,7 +7,6 @@
 
   bio.c
   bio_mem.c
-  buffer.c
   connect.c
   fd.c
   file.c
diff --git a/src/crypto/bio/bio.c b/src/crypto/bio/bio.c
index 8aad9fb..5cab843 100644
--- a/src/crypto/bio/bio.c
+++ b/src/crypto/bio/bio.c
@@ -608,3 +608,5 @@
 void BIO_set_retry_special(BIO *bio) {
   bio->flags |= BIO_FLAGS_READ | BIO_FLAGS_IO_SPECIAL;
 }
+
+int BIO_set_write_buffer_size(BIO *bio, int buffer_size) { return 0; }
diff --git a/src/crypto/bio/bio_mem.c b/src/crypto/bio/bio_mem.c
index 24ed5be..1cba8a8 100644
--- a/src/crypto/bio/bio_mem.c
+++ b/src/crypto/bio/bio_mem.c
@@ -189,10 +189,6 @@
   return ret;
 }
 
-static int mem_puts(BIO *bp, const char *str) {
-  return mem_write(bp, str, strlen(str));
-}
-
 static int mem_gets(BIO *bio, char *buf, int size) {
   int i, j;
   char *p;
@@ -295,8 +291,12 @@
 }
 
 static const BIO_METHOD mem_method = {
-    BIO_TYPE_MEM, "memory buffer", mem_write, mem_read, mem_puts,
-    mem_gets,     mem_ctrl,        mem_new,   mem_free, NULL, };
+    BIO_TYPE_MEM,    "memory buffer",
+    mem_write,       mem_read,
+    NULL /* puts */, mem_gets,
+    mem_ctrl,        mem_new,
+    mem_free,        NULL /* callback_ctrl */,
+};
 
 const BIO_METHOD *BIO_s_mem(void) { return &mem_method; }
 
diff --git a/src/crypto/bio/buffer.c b/src/crypto/bio/buffer.c
deleted file mode 100644
index 6190f29..0000000
--- a/src/crypto/bio/buffer.c
+++ /dev/null
@@ -1,486 +0,0 @@
-/* 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/bio.h>
-
-#include <string.h>
-
-#include <openssl/buf.h>
-#include <openssl/err.h>
-#include <openssl/mem.h>
-
-#include "../internal.h"
-
-
-#define DEFAULT_BUFFER_SIZE 4096
-
-typedef struct bio_f_buffer_ctx_struct {
-  /* Buffers are setup like this:
-   *
-   * <---------------------- size ----------------------->
-   * +---------------------------------------------------+
-   * | consumed | remaining          | free space        |
-   * +---------------------------------------------------+
-   * <-- off --><------- len ------->
-   */
-
-  int ibuf_size;  /* how big is the input buffer */
-  int obuf_size;  /* how big is the output buffer */
-
-  char *ibuf;   /* the char array */
-  int ibuf_len; /* how many bytes are in it */
-  int ibuf_off; /* write/read offset */
-
-  char *obuf;   /* the char array */
-  int obuf_len; /* how many bytes are in it */
-  int obuf_off; /* write/read offset */
-} BIO_F_BUFFER_CTX;
-
-static int buffer_new(BIO *bio) {
-  BIO_F_BUFFER_CTX *ctx;
-
-  ctx = OPENSSL_malloc(sizeof(BIO_F_BUFFER_CTX));
-  if (ctx == NULL) {
-    return 0;
-  }
-  OPENSSL_memset(ctx, 0, sizeof(BIO_F_BUFFER_CTX));
-
-  ctx->ibuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
-  if (ctx->ibuf == NULL) {
-    goto err1;
-  }
-  ctx->obuf = OPENSSL_malloc(DEFAULT_BUFFER_SIZE);
-  if (ctx->obuf == NULL) {
-    goto err2;
-  }
-  ctx->ibuf_size = DEFAULT_BUFFER_SIZE;
-  ctx->obuf_size = DEFAULT_BUFFER_SIZE;
-
-  bio->init = 1;
-  bio->ptr = (char *)ctx;
-  return 1;
-
-err2:
-  OPENSSL_free(ctx->ibuf);
-
-err1:
-  OPENSSL_free(ctx);
-  return 0;
-}
-
-static int buffer_free(BIO *bio) {
-  BIO_F_BUFFER_CTX *ctx;
-
-  if (bio == NULL || bio->ptr == NULL) {
-    return 0;
-  }
-
-  ctx = (BIO_F_BUFFER_CTX *)bio->ptr;
-  OPENSSL_free(ctx->ibuf);
-  OPENSSL_free(ctx->obuf);
-  OPENSSL_free(bio->ptr);
-
-  bio->ptr = NULL;
-  bio->init = 0;
-  bio->flags = 0;
-
-  return 1;
-}
-
-static int buffer_read(BIO *bio, char *out, int outl) {
-  int i, num = 0;
-  BIO_F_BUFFER_CTX *ctx;
-
-  ctx = (BIO_F_BUFFER_CTX *)bio->ptr;
-
-  if (ctx == NULL || bio->next_bio == NULL) {
-    return 0;
-  }
-
-  num = 0;
-  BIO_clear_retry_flags(bio);
-
-  for (;;) {
-    i = ctx->ibuf_len;
-    /* If there is stuff left over, grab it */
-    if (i != 0) {
-      if (i > outl) {
-        i = outl;
-      }
-      OPENSSL_memcpy(out, &ctx->ibuf[ctx->ibuf_off], i);
-      ctx->ibuf_off += i;
-      ctx->ibuf_len -= i;
-      num += i;
-      if (outl == i) {
-        return num;
-      }
-      outl -= i;
-      out += i;
-    }
-
-    /* We may have done a partial read. Try to do more. We have nothing in the
-     * buffer. If we get an error and have read some data, just return it and
-     * let them retry to get the error again. Copy direct to parent address
-     * space */
-    if (outl > ctx->ibuf_size) {
-      for (;;) {
-        i = BIO_read(bio->next_bio, out, outl);
-        if (i <= 0) {
-          BIO_copy_next_retry(bio);
-          if (i < 0) {
-            return (num > 0) ? num : i;
-          }
-          return num;
-        }
-        num += i;
-        if (outl == i) {
-          return num;
-        }
-        out += i;
-        outl -= i;
-      }
-    }
-    /* else */
-
-    /* we are going to be doing some buffering */
-    i = BIO_read(bio->next_bio, ctx->ibuf, ctx->ibuf_size);
-    if (i <= 0) {
-      BIO_copy_next_retry(bio);
-      if (i < 0) {
-        return (num > 0) ? num : i;
-      }
-      return num;
-    }
-    ctx->ibuf_off = 0;
-    ctx->ibuf_len = i;
-  }
-}
-
-static int buffer_write(BIO *b, const char *in, int inl) {
-  int i, num = 0;
-  BIO_F_BUFFER_CTX *ctx;
-
-  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
-  if (ctx == NULL || b->next_bio == NULL) {
-    return 0;
-  }
-
-  BIO_clear_retry_flags(b);
-
-  for (;;) {
-    i = ctx->obuf_size - (ctx->obuf_off + ctx->obuf_len);
-    /* add to buffer and return */
-    if (i >= inl) {
-      OPENSSL_memcpy(&ctx->obuf[ctx->obuf_off + ctx->obuf_len], in, inl);
-      ctx->obuf_len += inl;
-      return num + inl;
-    }
-    /* else */
-    /* stuff already in buffer, so add to it first, then flush */
-    if (ctx->obuf_len != 0) {
-      if (i > 0) {
-        OPENSSL_memcpy(&ctx->obuf[ctx->obuf_off + ctx->obuf_len], in, i);
-        in += i;
-        inl -= i;
-        num += i;
-        ctx->obuf_len += i;
-      }
-
-      /* we now have a full buffer needing flushing */
-      for (;;) {
-        i = BIO_write(b->next_bio, &ctx->obuf[ctx->obuf_off], ctx->obuf_len);
-        if (i <= 0) {
-          BIO_copy_next_retry(b);
-
-          if (i < 0) {
-            return (num > 0) ? num : i;
-          }
-          return num;
-        }
-        ctx->obuf_off += i;
-        ctx->obuf_len -= i;
-        if (ctx->obuf_len == 0) {
-          break;
-        }
-      }
-    }
-
-    /* we only get here if the buffer has been flushed and we
-     * still have stuff to write */
-    ctx->obuf_off = 0;
-
-    /* we now have inl bytes to write */
-    while (inl >= ctx->obuf_size) {
-      i = BIO_write(b->next_bio, in, inl);
-      if (i <= 0) {
-        BIO_copy_next_retry(b);
-        if (i < 0) {
-          return (num > 0) ? num : i;
-        }
-        return num;
-      }
-      num += i;
-      in += i;
-      inl -= i;
-      if (inl == 0) {
-        return num;
-      }
-    }
-
-    /* copy the rest into the buffer since we have only a small
-     * amount left */
-  }
-}
-
-static long buffer_ctrl(BIO *b, int cmd, long num, void *ptr) {
-  BIO_F_BUFFER_CTX *ctx;
-  long ret = 1;
-  char *p1, *p2;
-  int r, *ip;
-  int ibs, obs;
-
-  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
-
-  switch (cmd) {
-    case BIO_CTRL_RESET:
-      ctx->ibuf_off = 0;
-      ctx->ibuf_len = 0;
-      ctx->obuf_off = 0;
-      ctx->obuf_len = 0;
-      if (b->next_bio == NULL) {
-        return 0;
-      }
-      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
-      break;
-
-    case BIO_CTRL_INFO:
-      ret = ctx->obuf_len;
-      break;
-
-    case BIO_CTRL_WPENDING:
-      ret = (long)ctx->obuf_len;
-      break;
-
-    case BIO_CTRL_PENDING:
-      ret = (long)ctx->ibuf_len;
-      break;
-
-    case BIO_C_SET_BUFF_SIZE:
-      ip = (int *)ptr;
-      if (*ip == 0) {
-        ibs = (int)num;
-        obs = ctx->obuf_size;
-      } else /* if (*ip == 1) */ {
-        ibs = ctx->ibuf_size;
-        obs = (int)num;
-      }
-      p1 = ctx->ibuf;
-      p2 = ctx->obuf;
-      if (ibs > DEFAULT_BUFFER_SIZE && ibs != ctx->ibuf_size) {
-        p1 = OPENSSL_malloc(ibs);
-        if (p1 == NULL) {
-          goto malloc_error;
-        }
-      }
-      if (obs > DEFAULT_BUFFER_SIZE && obs != ctx->obuf_size) {
-        p2 = OPENSSL_malloc(obs);
-        if (p2 == NULL) {
-          if (p1 != ctx->ibuf) {
-            OPENSSL_free(p1);
-          }
-          goto malloc_error;
-        }
-      }
-
-      if (ctx->ibuf != p1) {
-        OPENSSL_free(ctx->ibuf);
-        ctx->ibuf = p1;
-        ctx->ibuf_size = ibs;
-      }
-      ctx->ibuf_off = 0;
-      ctx->ibuf_len = 0;
-
-      if (ctx->obuf != p2) {
-        OPENSSL_free(ctx->obuf);
-        ctx->obuf = p2;
-        ctx->obuf_size = obs;
-      }
-      ctx->obuf_off = 0;
-      ctx->obuf_len = 0;
-      break;
-
-    case BIO_CTRL_FLUSH:
-      if (b->next_bio == NULL) {
-        return 0;
-      }
-
-      while (ctx->obuf_len > 0) {
-        BIO_clear_retry_flags(b);
-        r = BIO_write(b->next_bio, &(ctx->obuf[ctx->obuf_off]),
-                      ctx->obuf_len);
-        BIO_copy_next_retry(b);
-        if (r <= 0) {
-          return r;
-        }
-        ctx->obuf_off += r;
-        ctx->obuf_len -= r;
-      }
-
-      ctx->obuf_len = 0;
-      ctx->obuf_off = 0;
-      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
-      break;
-
-    default:
-      if (b->next_bio == NULL) {
-        return 0;
-      }
-      BIO_clear_retry_flags(b);
-      ret = BIO_ctrl(b->next_bio, cmd, num, ptr);
-      BIO_copy_next_retry(b);
-      break;
-  }
-  return ret;
-
-malloc_error:
-  OPENSSL_PUT_ERROR(BIO, ERR_R_MALLOC_FAILURE);
-  return 0;
-}
-
-static long buffer_callback_ctrl(BIO *b, int cmd, bio_info_cb fp) {
-  long ret = 1;
-
-  if (b->next_bio == NULL) {
-    return 0;
-  }
-
-  switch (cmd) {
-    default:
-      ret = BIO_callback_ctrl(b->next_bio, cmd, fp);
-      break;
-  }
-  return ret;
-}
-
-static int buffer_gets(BIO *b, char *buf, int size) {
-  BIO_F_BUFFER_CTX *ctx;
-  int num = 0, i, flag;
-  char *p;
-
-  ctx = (BIO_F_BUFFER_CTX *)b->ptr;
-  if (buf == NULL || size <= 0) {
-    return 0;
-  }
-
-  size--; /* reserve space for a '\0' */
-  BIO_clear_retry_flags(b);
-
-  for (;;) {
-    if (ctx->ibuf_len > 0) {
-      p = &ctx->ibuf[ctx->ibuf_off];
-      flag = 0;
-      for (i = 0; (i < ctx->ibuf_len) && (i < size); i++) {
-        *(buf++) = p[i];
-        if (p[i] == '\n') {
-          flag = 1;
-          i++;
-          break;
-        }
-      }
-      num += i;
-      size -= i;
-      ctx->ibuf_len -= i;
-      ctx->ibuf_off += i;
-      if (flag || size == 0) {
-        *buf = '\0';
-        return num;
-      }
-    } else /* read another chunk */
-    {
-      i = BIO_read(b->next_bio, ctx->ibuf, ctx->ibuf_size);
-      if (i <= 0) {
-        BIO_copy_next_retry(b);
-        *buf = '\0';
-        if (i < 0) {
-          return (num > 0) ? num : i;
-        }
-        return num;
-      }
-      ctx->ibuf_len = i;
-      ctx->ibuf_off = 0;
-    }
-  }
-}
-
-static int buffer_puts(BIO *b, const char *str) {
-  return buffer_write(b, str, strlen(str));
-}
-
-static const BIO_METHOD methods_buffer = {
-    BIO_TYPE_BUFFER, "buffer",             buffer_write, buffer_read,
-    buffer_puts,     buffer_gets,          buffer_ctrl,  buffer_new,
-    buffer_free,     buffer_callback_ctrl,
-};
-
-const BIO_METHOD *BIO_f_buffer(void) { return &methods_buffer; }
-
-int BIO_set_read_buffer_size(BIO *bio, int buffer_size) {
-  return BIO_int_ctrl(bio, BIO_C_SET_BUFF_SIZE, buffer_size, 0);
-}
-
-int BIO_set_write_buffer_size(BIO *bio, int buffer_size) {
-  return BIO_int_ctrl(bio, BIO_C_SET_BUFF_SIZE, buffer_size, 1);
-}
diff --git a/src/crypto/bio/connect.c b/src/crypto/bio/connect.c
index f6cc837..d40dd53 100644
--- a/src/crypto/bio/connect.c
+++ b/src/crypto/bio/connect.c
@@ -468,14 +468,6 @@
       break;
     case BIO_CTRL_FLUSH:
       break;
-    case BIO_CTRL_SET_CALLBACK: {
-#if 0 /* FIXME: Should this be used?  -- Richard Levitte */
-		OPENSSL_PUT_ERROR(BIO, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-		ret = -1;
-#else
-      ret = 0;
-#endif
-    } break;
     case BIO_CTRL_GET_CALLBACK: {
       int (**fptr)(const BIO *bio, int state, int xret);
       fptr = (int (**)(const BIO *bio, int state, int xret))ptr;
@@ -485,7 +477,7 @@
       ret = 0;
       break;
   }
-  return (ret);
+  return ret;
 }
 
 static long conn_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp) {
@@ -495,9 +487,9 @@
   data = (BIO_CONNECT *)bio->ptr;
 
   switch (cmd) {
-    case BIO_CTRL_SET_CALLBACK: {
+    case BIO_CTRL_SET_CALLBACK:
       data->info_callback = (int (*)(const struct bio_st *, int, int))fp;
-    } break;
+      break;
     default:
       ret = 0;
       break;
@@ -505,10 +497,6 @@
   return ret;
 }
 
-static int conn_puts(BIO *bp, const char *str) {
-  return conn_write(bp, str, strlen(str));
-}
-
 BIO *BIO_new_connect(const char *hostname) {
   BIO *ret;
 
@@ -524,8 +512,8 @@
 }
 
 static const BIO_METHOD methods_connectp = {
-    BIO_TYPE_CONNECT, "socket connect",         conn_write, conn_read,
-    conn_puts,        NULL /* connect_gets, */, conn_ctrl,  conn_new,
+    BIO_TYPE_CONNECT, "socket connect",   conn_write, conn_read,
+    NULL /* puts */,  NULL /* gets */,    conn_ctrl,  conn_new,
     conn_free,        conn_callback_ctrl,
 };
 
diff --git a/src/crypto/bio/fd.c b/src/crypto/bio/fd.c
index 13833df..4e9eeac 100644
--- a/src/crypto/bio/fd.c
+++ b/src/crypto/bio/fd.c
@@ -241,10 +241,6 @@
   return ret;
 }
 
-static int fd_puts(BIO *bp, const char *str) {
-  return fd_write(bp, str, strlen(str));
-}
-
 static int fd_gets(BIO *bp, char *buf, int size) {
   char *ptr = buf;
   char *end = buf + size - 1;
@@ -263,8 +259,9 @@
 }
 
 static const BIO_METHOD methods_fdp = {
-    BIO_TYPE_FD, "file descriptor", fd_write, fd_read, fd_puts,
-    fd_gets,     fd_ctrl,           fd_new,   fd_free, NULL, };
+    BIO_TYPE_FD, "file descriptor", fd_write, fd_read, NULL /* puts */,
+    fd_gets,     fd_ctrl,           fd_new,   fd_free, NULL /* callback_ctrl */,
+};
 
 const BIO_METHOD *BIO_s_fd(void) { return &methods_fdp; }
 
diff --git a/src/crypto/bio/file.c b/src/crypto/bio/file.c
index b903bc2..3580cd1 100644
--- a/src/crypto/bio/file.c
+++ b/src/crypto/bio/file.c
@@ -273,13 +273,13 @@
   return ret;
 }
 
-static int file_puts(BIO *bp, const char *str) {
-  return file_write(bp, str, strlen(str));
-}
-
 static const BIO_METHOD methods_filep = {
-    BIO_TYPE_FILE, "FILE pointer", file_write, file_read, file_puts,
-    file_gets,     file_ctrl,      file_new,   file_free, NULL, };
+    BIO_TYPE_FILE,   "FILE pointer",
+    file_write,      file_read,
+    NULL /* puts */, file_gets,
+    file_ctrl,       file_new,
+    file_free,       NULL /* callback_ctrl */,
+};
 
 const BIO_METHOD *BIO_s_file(void) { return &methods_filep; }
 
diff --git a/src/crypto/bio/pair.c b/src/crypto/bio/pair.c
index e933a1d..8ba382d 100644
--- a/src/crypto/bio/pair.c
+++ b/src/crypto/bio/pair.c
@@ -450,14 +450,10 @@
   return ret;
 }
 
-static int bio_puts(BIO *bio, const char *str) {
-  return bio_write(bio, str, strlen(str));
-}
 
 static const BIO_METHOD methods_biop = {
-    BIO_TYPE_BIO, "BIO pair",             bio_write, bio_read,
-    bio_puts,     NULL /* no bio_gets */, bio_ctrl,  bio_new,
-    bio_free,     NULL /* no bio_callback_ctrl */
+    BIO_TYPE_BIO,    "BIO pair", bio_write, bio_read, NULL /* puts */,
+    NULL /* gets */, bio_ctrl,   bio_new,   bio_free, NULL /* callback_ctrl */
 };
 
 static const BIO_METHOD *bio_s_bio(void) { return &methods_biop; }
diff --git a/src/crypto/bio/socket.c b/src/crypto/bio/socket.c
index f70ea92..111761f 100644
--- a/src/crypto/bio/socket.c
+++ b/src/crypto/bio/socket.c
@@ -142,10 +142,6 @@
   return ret;
 }
 
-static int sock_puts(BIO *bp, const char *str) {
-  return sock_write(bp, str, strlen(str));
-}
-
 static long sock_ctrl(BIO *b, int cmd, long num, void *ptr) {
   long ret = 1;
   int *ip;
@@ -185,8 +181,11 @@
 }
 
 static const BIO_METHOD methods_sockp = {
-    BIO_TYPE_SOCKET,  "socket",  sock_write, sock_read, sock_puts,
-    NULL /* gets, */, sock_ctrl, sock_new,   sock_free, NULL,
+    BIO_TYPE_SOCKET, "socket",
+    sock_write,      sock_read,
+    NULL /* puts */, NULL /* gets, */,
+    sock_ctrl,       sock_new,
+    sock_free,       NULL /* callback_ctrl */,
 };
 
 const BIO_METHOD *BIO_s_socket(void) { return &methods_sockp; }
diff --git a/src/crypto/bn/asm/x86_64-mont5.pl b/src/crypto/bn/asm/x86_64-mont5.pl
index 61dd902..61fde2d 100755
--- a/src/crypto/bn/asm/x86_64-mont5.pl
+++ b/src/crypto/bn/asm/x86_64-mont5.pl
@@ -1852,6 +1852,7 @@
 
 .align	32
 .L8x_tail_done:
+	xor	%rax,%rax
 	add	(%rdx),%r8		# can this overflow?
 	adc	\$0,%r9
 	adc	\$0,%r10
@@ -1859,10 +1860,8 @@
 	adc	\$0,%r12
 	adc	\$0,%r13
 	adc	\$0,%r14
-	adc	\$0,%r15		# can't overflow, because we
-					# started with "overhung" part
-					# of multiplication
-	xor	%rax,%rax
+	adc	\$0,%r15
+	adc	\$0,%rax
 
 	neg	$carry
 .L8x_no_tail:
@@ -3248,6 +3247,7 @@
 
 .align	32
 .Lsqrx8x_tail_done:
+	xor	%rax,%rax
 	add	24+8(%rsp),%r8		# can this overflow?
 	adc	\$0,%r9
 	adc	\$0,%r10
@@ -3255,10 +3255,8 @@
 	adc	\$0,%r12
 	adc	\$0,%r13
 	adc	\$0,%r14
-	adc	\$0,%r15		# can't overflow, because we
-					# started with "overhung" part
-					# of multiplication
-	mov	$carry,%rax		# xor	%rax,%rax
+	adc	\$0,%r15
+	adc	\$0,%rax
 
 	sub	16+8(%rsp),$carry	# mov 16(%rsp),%cf
 .Lsqrx8x_no_tail:			# %cf is 0 if jumped here
@@ -3273,7 +3271,7 @@
 	adc	8*5($tptr),%r13
 	adc	8*6($tptr),%r14
 	adc	8*7($tptr),%r15
-	adc	%rax,%rax		# top-most carry
+	adc	\$0,%rax		# top-most carry
 
 	mov	32+8(%rsp),%rbx		# n0
 	mov	8*8($tptr,%rcx),%rdx	# modulo-scheduled "%r8"
diff --git a/src/crypto/bn/bn_test.cc b/src/crypto/bn/bn_test.cc
index 8f93ad0..a152cdf 100644
--- a/src/crypto/bn/bn_test.cc
+++ b/src/crypto/bn/bn_test.cc
@@ -515,6 +515,54 @@
   return true;
 }
 
+static bool TestModSquare(FileTest *t, BN_CTX *ctx) {
+  bssl::UniquePtr<BIGNUM> a = GetBIGNUM(t, "A");
+  bssl::UniquePtr<BIGNUM> m = GetBIGNUM(t, "M");
+  bssl::UniquePtr<BIGNUM> mod_square = GetBIGNUM(t, "ModSquare");
+  if (!a || !m || !mod_square) {
+    return false;
+  }
+
+  bssl::UniquePtr<BIGNUM> a_copy(BN_new());
+  bssl::UniquePtr<BIGNUM> ret(BN_new());
+  if (!ret || !a_copy ||
+      !BN_mod_mul(ret.get(), a.get(), a.get(), m.get(), ctx) ||
+      !ExpectBIGNUMsEqual(t, "A * A (mod M)", mod_square.get(), ret.get()) ||
+      // Repeat the operation with |a_copy|.
+      !BN_copy(a_copy.get(), a.get()) ||
+      !BN_mod_mul(ret.get(), a.get(), a_copy.get(), m.get(), ctx) ||
+      !ExpectBIGNUMsEqual(t, "A * A_copy (mod M)", mod_square.get(),
+                          ret.get())) {
+    return false;
+  }
+
+  if (BN_is_odd(m.get())) {
+    // Reduce |a| and test the Montgomery version.
+    bssl::UniquePtr<BN_MONT_CTX> mont(BN_MONT_CTX_new());
+    bssl::UniquePtr<BIGNUM> a_tmp(BN_new());
+    if (!mont || !a_tmp ||
+        !BN_MONT_CTX_set(mont.get(), m.get(), ctx) ||
+        !BN_nnmod(a_tmp.get(), a.get(), m.get(), ctx) ||
+        !BN_to_montgomery(a_tmp.get(), a_tmp.get(), mont.get(), ctx) ||
+        !BN_mod_mul_montgomery(ret.get(), a_tmp.get(), a_tmp.get(), mont.get(),
+                               ctx) ||
+        !BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx) ||
+        !ExpectBIGNUMsEqual(t, "A * A (mod M) (Montgomery)",
+                            mod_square.get(), ret.get()) ||
+        // Repeat the operation with |a_copy|.
+        !BN_copy(a_copy.get(), a_tmp.get()) ||
+        !BN_mod_mul_montgomery(ret.get(), a_tmp.get(), a_copy.get(), mont.get(),
+                               ctx) ||
+        !BN_from_montgomery(ret.get(), ret.get(), mont.get(), ctx) ||
+        !ExpectBIGNUMsEqual(t, "A * A_copy (mod M) (Montgomery)",
+                            mod_square.get(), ret.get())) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 static bool TestModExp(FileTest *t, BN_CTX *ctx) {
   bssl::UniquePtr<BIGNUM> a = GetBIGNUM(t, "A");
   bssl::UniquePtr<BIGNUM> e = GetBIGNUM(t, "E");
@@ -649,6 +697,7 @@
     {"Product", TestProduct},
     {"Quotient", TestQuotient},
     {"ModMul", TestModMul},
+    {"ModSquare", TestModSquare},
     {"ModExp", TestModExp},
     {"Exp", TestExp},
     {"ModSqrt", TestModSqrt},
diff --git a/src/crypto/bn/bn_tests.txt b/src/crypto/bn/bn_tests.txt
index 46c788f..c53eb23 100644
--- a/src/crypto/bn/bn_tests.txt
+++ b/src/crypto/bn/bn_tests.txt
@@ -9888,6 +9888,16 @@
 M = d78af684e71db0c39cff4e64fb9db567132cb9c50cc98009feb820b26f2ded9b91b9b5e2b83ae0ae4eb4e0523ca726bfbe969b89fd754f674ce99118c3f2d1c5d81fdc7c54e02b60262b241d53c040e99e45826eca37a804668e690e1afc1ca42c9a15d84d4954425f0b7642fc0bd9d7b24e2618d2dcc9b729d944badacfddaf
 
 
+# ModSquare tests.
+#
+# These test vectors satisfy A * A = ModSquare (mod M) and 0 <= ModSquare < M.
+
+# Regression test for CVE-2017-3732.
+ModSquare = fffffffdfffffd01000009000002f6fffdf403000312000402f3fff5f602fe080a0005fdfafffa00010001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000002000002fefffff7fffffd07000109fdfffef3fffdfd06000405ff00fdfbfffe00010001
+A = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff00000000
+M = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff
+
+
 # ModExp tests.
 #
 # These test vectors satisfy A ^ E = ModExp (mod M) and 0 <= ModExp < M.
diff --git a/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl b/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
index c3f3e0b..44590c2 100644
--- a/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
+++ b/src/crypto/cipher/asm/chacha20_poly1305_x86_64.pl
@@ -31,7 +31,7 @@
 ( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
 die "can't locate x86_64-xlate.pl";
 
-open OUT,"| \"$^X\" $xlate $flavour $output";
+open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
 *STDOUT=*OUT;
 
 $avx = 2;
@@ -138,7 +138,7 @@
         mov %rdx, $t1
         mov 0+$r_store, %rax
         mul $acc1
-        imul $acc2, $t2
+        imulq $acc2, $t2
         add %rax, $t1
         adc %rdx, $t2\n";
 }
@@ -157,7 +157,7 @@
 }
 
 sub poly_stage3 {
-$code.="imul $acc2, $t3
+$code.="imulq $acc2, $t3
         add $acc0, $t2
         adc %rdx, $t3\n";
 }
@@ -389,7 +389,7 @@
         jb hash_ad_tail\n";
         &poly_add("0($adp)");
         &poly_mul(); $code.="
-        lea (1*16)($adp), $adp
+        lea 1*16($adp), $adp
         sub \$16, $itr2
     jmp hash_ad_loop
 hash_ad_tail:
@@ -453,7 +453,6 @@
 .cfi_offset r13, -40
 .cfi_offset r14, -48
 .cfi_offset r15, -56
-.cfi_offset $keyp, -64
     lea 32(%rsp), %rbp
     and \$-32, %rbp
     mov %rdx, 8+$len_store
@@ -852,7 +851,6 @@
 .cfi_offset r13, -40
 .cfi_offset r14, -48
 .cfi_offset r15, -56
-.cfi_offset $keyp, -64
     lea 32(%rsp), %rbp
     and \$-32, %rbp
     mov %rdx, 8+$len_store
@@ -1378,7 +1376,7 @@
     mov %rdx, $t2
     mulx $acc0, $t0, $t1
     mulx $acc1, %rax, %rdx
-    imul $acc2, $t2
+    imulq $acc2, $t2
     add %rax, $t1
     adc %rdx, $t2
 ___
@@ -1392,7 +1390,7 @@
     mulx $acc1, $acc1, $t3
     adc $acc1, $t2
     adc \$0, $t3
-    imul $acc2, %rdx
+    imulq $acc2, %rdx
 ___
 }
 
diff --git a/src/crypto/cipher/e_chacha20poly1305.c b/src/crypto/cipher/e_chacha20poly1305.c
index 34d094b..c6e81ab 100644
--- a/src/crypto/cipher/e_chacha20poly1305.c
+++ b/src/crypto/cipher/e_chacha20poly1305.c
@@ -18,6 +18,7 @@
 
 #include <openssl/chacha.h>
 #include <openssl/cipher.h>
+#include <openssl/cpu.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 #include <openssl/poly1305.h>
@@ -35,16 +36,21 @@
 
 #if defined(OPENSSL_X86_64) && !defined(OPENSSL_NO_ASM) && \
     !defined(OPENSSL_WINDOWS)
-static const int kHaveAsm = 1;
+static int asm_capable(void) {
+  const int sse41_capable = (OPENSSL_ia32cap_P[1] & (1 << 19)) != 0;
+  return sse41_capable;
+}
+
 // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
 // decrypts |plaintext_len| bytes from |ciphertext| and writes them to
 // |out_plaintext|. On entry, |aead_data| must contain the final 48 bytes of
 // the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
 // by the nonce. On exit, it will contain the calculated tag value, which the
 // caller must check.
-void chacha20_poly1305_open(uint8_t *out_plaintext, const uint8_t *ciphertext,
-                            size_t plaintext_len, const uint8_t *ad,
-                            size_t ad_len, uint8_t *aead_data);
+extern void chacha20_poly1305_open(uint8_t *out_plaintext,
+                                   const uint8_t *ciphertext,
+                                   size_t plaintext_len, const uint8_t *ad,
+                                   size_t ad_len, uint8_t *aead_data);
 
 // chacha20_poly1305_open is defined in chacha20_poly1305_x86_64.pl. It
 // encrypts |plaintext_len| bytes from |plaintext| and writes them to
@@ -52,11 +58,15 @@
 // the initial ChaCha20 block, i.e. the key, followed by four zeros, followed
 // by the nonce. On exit, it will contain the calculated tag value, which the
 // caller must append to the ciphertext.
-void chacha20_poly1305_seal(uint8_t *out_ciphertext, const uint8_t *plaintext,
-                            size_t plaintext_len, const uint8_t *ad,
-                            size_t ad_len, uint8_t *aead_data);
+extern void chacha20_poly1305_seal(uint8_t *out_ciphertext,
+                                   const uint8_t *plaintext,
+                                   size_t plaintext_len, const uint8_t *ad,
+                                   size_t ad_len, uint8_t *aead_data);
 #else
-static const int kHaveAsm = 0;
+static int asm_capable(void) {
+  return 0;
+}
+
 
 static void chacha20_poly1305_open(uint8_t *out_plaintext,
                                    const uint8_t *ciphertext,
@@ -181,7 +191,7 @@
 
   alignas(16) uint8_t tag[48];
 
-  if (kHaveAsm) {
+  if (asm_capable()) {
     OPENSSL_memcpy(tag, c20_ctx->key, 32);
     OPENSSL_memset(tag + 32, 0, 4);
     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
@@ -229,7 +239,7 @@
   plaintext_len = in_len - c20_ctx->tag_len;
   alignas(16) uint8_t tag[48];
 
-  if (kHaveAsm) {
+  if (asm_capable()) {
     OPENSSL_memcpy(tag, c20_ctx->key, 32);
     OPENSSL_memset(tag + 32, 0, 4);
     OPENSSL_memcpy(tag + 32 + 4, nonce, 12);
diff --git a/src/crypto/curve25519/asm/x25519-asm-x86_64.S b/src/crypto/curve25519/asm/x25519-asm-x86_64.S
index 531ac16..18041d0 100644
--- a/src/crypto/curve25519/asm/x25519-asm-x86_64.S
+++ b/src/crypto/curve25519/asm/x25519-asm-x86_64.S
@@ -60,17 +60,10 @@
 .globl C_ABI(x25519_x86_64_freeze)
 HIDDEN C_ABI(x25519_x86_64_freeze)
 C_ABI(x25519_x86_64_freeze):
-mov %rsp,%r11
-and $31,%r11
-add $64,%r11
-sub %r11,%rsp
-movq %r11,0(%rsp)
-movq %r12,8(%rsp)
-movq %r13,16(%rsp)
-movq %r14,24(%rsp)
-movq %r15,32(%rsp)
-movq %rbx,40(%rsp)
-movq %rbp,48(%rsp)
+.cfi_startproc
+/* This is a leaf function and uses the redzone for saving registers. */
+movq %r12,-8(%rsp)
+.cfi_rel_offset r12, -8
 movq   0(%rdi),%rsi
 movq   8(%rdi),%rdx
 movq   16(%rdi),%rcx
@@ -128,44 +121,38 @@
 movq   %rcx,16(%rdi)
 movq   %r8,24(%rdi)
 movq   %r9,32(%rdi)
-movq 0(%rsp),%r11
-movq 8(%rsp),%r12
-movq 16(%rsp),%r13
-movq 24(%rsp),%r14
-movq 32(%rsp),%r15
-movq 40(%rsp),%rbx
-movq 48(%rsp),%rbp
-add %r11,%rsp
-mov %rdi,%rax
-mov %rsi,%rdx
+movq -8(%rsp),%r12
 ret
+.cfi_endproc
 
 .p2align 5
 .globl C_ABI(x25519_x86_64_mul)
 HIDDEN C_ABI(x25519_x86_64_mul)
 C_ABI(x25519_x86_64_mul):
-mov %rsp,%r11
-and $31,%r11
-add $96,%r11
-sub %r11,%rsp
-movq %r11,0(%rsp)
-movq %r12,8(%rsp)
-movq %r13,16(%rsp)
-movq %r14,24(%rsp)
-movq %r15,32(%rsp)
-movq %rbx,40(%rsp)
-movq %rbp,48(%rsp)
-movq %rdi,56(%rsp)
+.cfi_startproc
+/* This is a leaf function and uses the redzone for saving registers. */
+movq %r12,-8(%rsp)
+.cfi_rel_offset r12, -8
+movq %r13,-16(%rsp)
+.cfi_rel_offset r13, -16
+movq %r14,-24(%rsp)
+.cfi_rel_offset r14, -24
+movq %r15,-32(%rsp)
+.cfi_rel_offset r15, -32
+movq %rbx,-40(%rsp)
+.cfi_rel_offset rbx, -40
+movq %rbp,-48(%rsp)
+.cfi_rel_offset rbp, -48
 mov  %rdx,%rcx
 movq   24(%rsi),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,64(%rsp)
+movq %rax,-64(%rsp)
 mulq  16(%rcx)
 mov  %rax,%r8
 mov  %rdx,%r9
 movq   32(%rsi),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,72(%rsp)
+movq %rax,-72(%rsp)
 mulq  8(%rcx)
 add  %rax,%r8
 adc %rdx,%r9
@@ -240,11 +227,11 @@
 mulq  8(%rcx)
 add  %rax,%rbx
 adc %rdx,%rbp
-movq 64(%rsp),%rax
+movq -64(%rsp),%rax
 mulq  24(%rcx)
 add  %rax,%r10
 adc %rdx,%r11
-movq 64(%rsp),%rax
+movq -64(%rsp),%rax
 mulq  32(%rcx)
 add  %rax,%r12
 adc %rdx,%r13
@@ -252,15 +239,15 @@
 mulq  0(%rcx)
 add  %rax,%rbx
 adc %rdx,%rbp
-movq 72(%rsp),%rax
+movq -72(%rsp),%rax
 mulq  16(%rcx)
 add  %rax,%r10
 adc %rdx,%r11
-movq 72(%rsp),%rax
+movq -72(%rsp),%rax
 mulq  24(%rcx)
 add  %rax,%r12
 adc %rdx,%r13
-movq 72(%rsp),%rax
+movq -72(%rsp),%rax
 mulq  32(%rcx)
 add  %rax,%r14
 adc %rdx,%r15
@@ -307,33 +294,31 @@
 movq   %r9,16(%rdi)
 movq   %rax,24(%rdi)
 movq   %r10,32(%rdi)
-movq 0(%rsp),%r11
-movq 8(%rsp),%r12
-movq 16(%rsp),%r13
-movq 24(%rsp),%r14
-movq 32(%rsp),%r15
-movq 40(%rsp),%rbx
-movq 48(%rsp),%rbp
-add %r11,%rsp
-mov %rdi,%rax
-mov %rsi,%rdx
+movq -8(%rsp),%r12
+movq -16(%rsp),%r13
+movq -24(%rsp),%r14
+movq -32(%rsp),%r15
+movq -40(%rsp),%rbx
+movq -48(%rsp),%rbp
 ret
+.cfi_endproc
 
 .p2align 5
 .globl C_ABI(x25519_x86_64_square)
 HIDDEN C_ABI(x25519_x86_64_square)
 C_ABI(x25519_x86_64_square):
-mov %rsp,%r11
-and $31,%r11
-add $64,%r11
-sub %r11,%rsp
-movq %r11,0(%rsp)
-movq %r12,8(%rsp)
-movq %r13,16(%rsp)
-movq %r14,24(%rsp)
-movq %r15,32(%rsp)
-movq %rbx,40(%rsp)
-movq %rbp,48(%rsp)
+.cfi_startproc
+/* This is a leaf function and uses the redzone for saving registers. */
+movq %r12,-8(%rsp)
+.cfi_rel_offset r12, -8
+movq %r13,-16(%rsp)
+.cfi_rel_offset r13, -16
+movq %r14,-24(%rsp)
+.cfi_rel_offset r14, -24
+movq %r15,-32(%rsp)
+.cfi_rel_offset r15, -32
+movq %rbx,-40(%rsp)
+.cfi_rel_offset rbx, -40
 movq   0(%rsi),%rax
 mulq  0(%rsi)
 mov  %rax,%rcx
@@ -449,33 +434,33 @@
 movq   %r9,16(%rdi)
 movq   %rax,24(%rdi)
 movq   %r10,32(%rdi)
-movq 0(%rsp),%r11
-movq 8(%rsp),%r12
-movq 16(%rsp),%r13
-movq 24(%rsp),%r14
-movq 32(%rsp),%r15
-movq 40(%rsp),%rbx
-movq 48(%rsp),%rbp
-add %r11,%rsp
-mov %rdi,%rax
-mov %rsi,%rdx
+movq -8(%rsp),%r12
+movq -16(%rsp),%r13
+movq -24(%rsp),%r14
+movq -32(%rsp),%r15
+movq -40(%rsp),%rbx
 ret
+.cfi_endproc
 
 .p2align 5
 .globl C_ABI(x25519_x86_64_ladderstep)
 HIDDEN C_ABI(x25519_x86_64_ladderstep)
 C_ABI(x25519_x86_64_ladderstep):
-mov %rsp,%r11
-and $31,%r11
-add $352,%r11
-sub %r11,%rsp
-movq %r11,0(%rsp)
-movq %r12,8(%rsp)
-movq %r13,16(%rsp)
-movq %r14,24(%rsp)
-movq %r15,32(%rsp)
-movq %rbx,40(%rsp)
-movq %rbp,48(%rsp)
+.cfi_startproc
+sub $344,%rsp
+.cfi_adjust_cfa_offset 344
+movq %r12,296(%rsp)
+.cfi_rel_offset r12, 296
+movq %r13,304(%rsp)
+.cfi_rel_offset r13, 304
+movq %r14,312(%rsp)
+.cfi_rel_offset r14, 312
+movq %r15,320(%rsp)
+.cfi_rel_offset r15, 320
+movq %rbx,328(%rsp)
+.cfi_rel_offset rbx, 328
+movq %rbp,336(%rsp)
+.cfi_rel_offset rbp, 336
 movq   40(%rdi),%rsi
 movq   48(%rdi),%rdx
 movq   56(%rdi),%rcx
@@ -501,201 +486,86 @@
 subq 96(%rdi),%r11
 subq 104(%rdi),%r12
 subq 112(%rdi),%r13
-movq %rsi,56(%rsp)
-movq %rdx,64(%rsp)
-movq %rcx,72(%rsp)
-movq %r8,80(%rsp)
-movq %r9,88(%rsp)
-movq %rax,96(%rsp)
-movq %r10,104(%rsp)
-movq %r11,112(%rsp)
-movq %r12,120(%rsp)
-movq %r13,128(%rsp)
-movq 96(%rsp),%rax
-mulq  96(%rsp)
+movq %rsi,0(%rsp)
+movq %rdx,8(%rsp)
+movq %rcx,16(%rsp)
+movq %r8,24(%rsp)
+movq %r9,32(%rsp)
+movq %rax,40(%rsp)
+movq %r10,48(%rsp)
+movq %r11,56(%rsp)
+movq %r12,64(%rsp)
+movq %r13,72(%rsp)
+movq 40(%rsp),%rax
+mulq  40(%rsp)
 mov  %rax,%rsi
 mov  %rdx,%rcx
-movq 96(%rsp),%rax
+movq 40(%rsp),%rax
 shl  $1,%rax
-mulq  104(%rsp)
+mulq  48(%rsp)
 mov  %rax,%r8
 mov  %rdx,%r9
-movq 96(%rsp),%rax
+movq 40(%rsp),%rax
 shl  $1,%rax
-mulq  112(%rsp)
+mulq  56(%rsp)
 mov  %rax,%r10
 mov  %rdx,%r11
-movq 96(%rsp),%rax
+movq 40(%rsp),%rax
 shl  $1,%rax
-mulq  120(%rsp)
+mulq  64(%rsp)
 mov  %rax,%r12
 mov  %rdx,%r13
-movq 96(%rsp),%rax
+movq 40(%rsp),%rax
 shl  $1,%rax
-mulq  128(%rsp)
+mulq  72(%rsp)
 mov  %rax,%r14
 mov  %rdx,%r15
-movq 104(%rsp),%rax
-mulq  104(%rsp)
+movq 48(%rsp),%rax
+mulq  48(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 104(%rsp),%rax
+movq 48(%rsp),%rax
 shl  $1,%rax
-mulq  112(%rsp)
+mulq  56(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 104(%rsp),%rax
+movq 48(%rsp),%rax
 shl  $1,%rax
-mulq  120(%rsp)
+mulq  64(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 104(%rsp),%rdx
+movq 48(%rsp),%rdx
 imulq  $38,%rdx,%rax
-mulq  128(%rsp)
+mulq  72(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 112(%rsp),%rax
-mulq  112(%rsp)
-add  %rax,%r14
-adc %rdx,%r15
-movq 112(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  120(%rsp)
-add  %rax,%rsi
-adc %rdx,%rcx
-movq 112(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  128(%rsp)
-add  %rax,%r8
-adc %rdx,%r9
-movq 120(%rsp),%rdx
-imulq  $19,%rdx,%rax
-mulq  120(%rsp)
-add  %rax,%r8
-adc %rdx,%r9
-movq 120(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  128(%rsp)
-add  %rax,%r10
-adc %rdx,%r11
-movq 128(%rsp),%rdx
-imulq  $19,%rdx,%rax
-mulq  128(%rsp)
-add  %rax,%r12
-adc %rdx,%r13
-movq x25519_x86_64_REDMASK51(%rip),%rdx
-shld $13,%rsi,%rcx
-and  %rdx,%rsi
-shld $13,%r8,%r9
-and  %rdx,%r8
-add  %rcx,%r8
-shld $13,%r10,%r11
-and  %rdx,%r10
-add  %r9,%r10
-shld $13,%r12,%r13
-and  %rdx,%r12
-add  %r11,%r12
-shld $13,%r14,%r15
-and  %rdx,%r14
-add  %r13,%r14
-imulq  $19,%r15,%rcx
-add  %rcx,%rsi
-mov  %rsi,%rcx
-shr  $51,%rcx
-add  %r8,%rcx
-and  %rdx,%rsi
-mov  %rcx,%r8
-shr  $51,%rcx
-add  %r10,%rcx
-and  %rdx,%r8
-mov  %rcx,%r9
-shr  $51,%rcx
-add  %r12,%rcx
-and  %rdx,%r9
-mov  %rcx,%rax
-shr  $51,%rcx
-add  %r14,%rcx
-and  %rdx,%rax
-mov  %rcx,%r10
-shr  $51,%rcx
-imulq  $19,%rcx,%rcx
-add  %rcx,%rsi
-and  %rdx,%r10
-movq %rsi,136(%rsp)
-movq %r8,144(%rsp)
-movq %r9,152(%rsp)
-movq %rax,160(%rsp)
-movq %r10,168(%rsp)
 movq 56(%rsp),%rax
 mulq  56(%rsp)
-mov  %rax,%rsi
-mov  %rdx,%rcx
-movq 56(%rsp),%rax
-shl  $1,%rax
-mulq  64(%rsp)
-mov  %rax,%r8
-mov  %rdx,%r9
-movq 56(%rsp),%rax
-shl  $1,%rax
-mulq  72(%rsp)
-mov  %rax,%r10
-mov  %rdx,%r11
-movq 56(%rsp),%rax
-shl  $1,%rax
-mulq  80(%rsp)
-mov  %rax,%r12
-mov  %rdx,%r13
-movq 56(%rsp),%rax
-shl  $1,%rax
-mulq  88(%rsp)
-mov  %rax,%r14
-mov  %rdx,%r15
-movq 64(%rsp),%rax
-mulq  64(%rsp)
-add  %rax,%r10
-adc %rdx,%r11
-movq 64(%rsp),%rax
-shl  $1,%rax
-mulq  72(%rsp)
-add  %rax,%r12
-adc %rdx,%r13
-movq 64(%rsp),%rax
-shl  $1,%rax
-mulq  80(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
+movq 56(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  64(%rsp)
+add  %rax,%rsi
+adc %rdx,%rcx
+movq 56(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  72(%rsp)
+add  %rax,%r8
+adc %rdx,%r9
+movq 64(%rsp),%rdx
+imulq  $19,%rdx,%rax
+mulq  64(%rsp)
+add  %rax,%r8
+adc %rdx,%r9
 movq 64(%rsp),%rdx
 imulq  $38,%rdx,%rax
-mulq  88(%rsp)
-add  %rax,%rsi
-adc %rdx,%rcx
-movq 72(%rsp),%rax
 mulq  72(%rsp)
-add  %rax,%r14
-adc %rdx,%r15
-movq 72(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  80(%rsp)
-add  %rax,%rsi
-adc %rdx,%rcx
-movq 72(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  88(%rsp)
-add  %rax,%r8
-adc %rdx,%r9
-movq 80(%rsp),%rdx
-imulq  $19,%rdx,%rax
-mulq  80(%rsp)
-add  %rax,%r8
-adc %rdx,%r9
-movq 80(%rsp),%rdx
-imulq  $38,%rdx,%rax
-mulq  88(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 88(%rsp),%rdx
+movq 72(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  88(%rsp)
+mulq  72(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq x25519_x86_64_REDMASK51(%rip),%rdx
@@ -736,11 +606,126 @@
 imulq  $19,%rcx,%rcx
 add  %rcx,%rsi
 and  %rdx,%r10
-movq %rsi,176(%rsp)
-movq %r8,184(%rsp)
-movq %r9,192(%rsp)
-movq %rax,200(%rsp)
-movq %r10,208(%rsp)
+movq %rsi,80(%rsp)
+movq %r8,88(%rsp)
+movq %r9,96(%rsp)
+movq %rax,104(%rsp)
+movq %r10,112(%rsp)
+movq 0(%rsp),%rax
+mulq  0(%rsp)
+mov  %rax,%rsi
+mov  %rdx,%rcx
+movq 0(%rsp),%rax
+shl  $1,%rax
+mulq  8(%rsp)
+mov  %rax,%r8
+mov  %rdx,%r9
+movq 0(%rsp),%rax
+shl  $1,%rax
+mulq  16(%rsp)
+mov  %rax,%r10
+mov  %rdx,%r11
+movq 0(%rsp),%rax
+shl  $1,%rax
+mulq  24(%rsp)
+mov  %rax,%r12
+mov  %rdx,%r13
+movq 0(%rsp),%rax
+shl  $1,%rax
+mulq  32(%rsp)
+mov  %rax,%r14
+mov  %rdx,%r15
+movq 8(%rsp),%rax
+mulq  8(%rsp)
+add  %rax,%r10
+adc %rdx,%r11
+movq 8(%rsp),%rax
+shl  $1,%rax
+mulq  16(%rsp)
+add  %rax,%r12
+adc %rdx,%r13
+movq 8(%rsp),%rax
+shl  $1,%rax
+mulq  24(%rsp)
+add  %rax,%r14
+adc %rdx,%r15
+movq 8(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  32(%rsp)
+add  %rax,%rsi
+adc %rdx,%rcx
+movq 16(%rsp),%rax
+mulq  16(%rsp)
+add  %rax,%r14
+adc %rdx,%r15
+movq 16(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  24(%rsp)
+add  %rax,%rsi
+adc %rdx,%rcx
+movq 16(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  32(%rsp)
+add  %rax,%r8
+adc %rdx,%r9
+movq 24(%rsp),%rdx
+imulq  $19,%rdx,%rax
+mulq  24(%rsp)
+add  %rax,%r8
+adc %rdx,%r9
+movq 24(%rsp),%rdx
+imulq  $38,%rdx,%rax
+mulq  32(%rsp)
+add  %rax,%r10
+adc %rdx,%r11
+movq 32(%rsp),%rdx
+imulq  $19,%rdx,%rax
+mulq  32(%rsp)
+add  %rax,%r12
+adc %rdx,%r13
+movq x25519_x86_64_REDMASK51(%rip),%rdx
+shld $13,%rsi,%rcx
+and  %rdx,%rsi
+shld $13,%r8,%r9
+and  %rdx,%r8
+add  %rcx,%r8
+shld $13,%r10,%r11
+and  %rdx,%r10
+add  %r9,%r10
+shld $13,%r12,%r13
+and  %rdx,%r12
+add  %r11,%r12
+shld $13,%r14,%r15
+and  %rdx,%r14
+add  %r13,%r14
+imulq  $19,%r15,%rcx
+add  %rcx,%rsi
+mov  %rsi,%rcx
+shr  $51,%rcx
+add  %r8,%rcx
+and  %rdx,%rsi
+mov  %rcx,%r8
+shr  $51,%rcx
+add  %r10,%rcx
+and  %rdx,%r8
+mov  %rcx,%r9
+shr  $51,%rcx
+add  %r12,%rcx
+and  %rdx,%r9
+mov  %rcx,%rax
+shr  $51,%rcx
+add  %r14,%rcx
+and  %rdx,%rax
+mov  %rcx,%r10
+shr  $51,%rcx
+imulq  $19,%rcx,%rcx
+add  %rcx,%rsi
+and  %rdx,%r10
+movq %rsi,120(%rsp)
+movq %r8,128(%rsp)
+movq %r9,136(%rsp)
+movq %rax,144(%rsp)
+movq %r10,152(%rsp)
 mov  %rsi,%rsi
 mov  %r8,%rdx
 mov  %r9,%rcx
@@ -751,16 +736,16 @@
 add  x25519_x86_64_2P1234(%rip),%rcx
 add  x25519_x86_64_2P1234(%rip),%r8
 add  x25519_x86_64_2P1234(%rip),%r9
-subq 136(%rsp),%rsi
-subq 144(%rsp),%rdx
-subq 152(%rsp),%rcx
-subq 160(%rsp),%r8
-subq 168(%rsp),%r9
-movq %rsi,216(%rsp)
-movq %rdx,224(%rsp)
-movq %rcx,232(%rsp)
-movq %r8,240(%rsp)
-movq %r9,248(%rsp)
+subq 80(%rsp),%rsi
+subq 88(%rsp),%rdx
+subq 96(%rsp),%rcx
+subq 104(%rsp),%r8
+subq 112(%rsp),%r9
+movq %rsi,160(%rsp)
+movq %rdx,168(%rsp)
+movq %rcx,176(%rsp)
+movq %r8,184(%rsp)
+movq %r9,192(%rsp)
 movq   120(%rdi),%rsi
 movq   128(%rdi),%rdx
 movq   136(%rdi),%rcx
@@ -786,121 +771,121 @@
 subq 176(%rdi),%r11
 subq 184(%rdi),%r12
 subq 192(%rdi),%r13
-movq %rsi,256(%rsp)
-movq %rdx,264(%rsp)
-movq %rcx,272(%rsp)
-movq %r8,280(%rsp)
-movq %r9,288(%rsp)
-movq %rax,296(%rsp)
-movq %r10,304(%rsp)
-movq %r11,312(%rsp)
-movq %r12,320(%rsp)
-movq %r13,328(%rsp)
-movq 280(%rsp),%rsi
+movq %rsi,200(%rsp)
+movq %rdx,208(%rsp)
+movq %rcx,216(%rsp)
+movq %r8,224(%rsp)
+movq %r9,232(%rsp)
+movq %rax,240(%rsp)
+movq %r10,248(%rsp)
+movq %r11,256(%rsp)
+movq %r12,264(%rsp)
+movq %r13,272(%rsp)
+movq 224(%rsp),%rsi
 imulq  $19,%rsi,%rax
-movq %rax,336(%rsp)
-mulq  112(%rsp)
+movq %rax,280(%rsp)
+mulq  56(%rsp)
 mov  %rax,%rsi
 mov  %rdx,%rcx
-movq 288(%rsp),%rdx
+movq 232(%rsp),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,344(%rsp)
-mulq  104(%rsp)
+movq %rax,288(%rsp)
+mulq  48(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 256(%rsp),%rax
-mulq  96(%rsp)
+movq 200(%rsp),%rax
+mulq  40(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 256(%rsp),%rax
-mulq  104(%rsp)
+movq 200(%rsp),%rax
+mulq  48(%rsp)
 mov  %rax,%r8
 mov  %rdx,%r9
-movq 256(%rsp),%rax
-mulq  112(%rsp)
+movq 200(%rsp),%rax
+mulq  56(%rsp)
 mov  %rax,%r10
 mov  %rdx,%r11
-movq 256(%rsp),%rax
-mulq  120(%rsp)
+movq 200(%rsp),%rax
+mulq  64(%rsp)
 mov  %rax,%r12
 mov  %rdx,%r13
-movq 256(%rsp),%rax
-mulq  128(%rsp)
+movq 200(%rsp),%rax
+mulq  72(%rsp)
 mov  %rax,%r14
 mov  %rdx,%r15
-movq 264(%rsp),%rax
-mulq  96(%rsp)
+movq 208(%rsp),%rax
+mulq  40(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 264(%rsp),%rax
-mulq  104(%rsp)
+movq 208(%rsp),%rax
+mulq  48(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 264(%rsp),%rax
-mulq  112(%rsp)
+movq 208(%rsp),%rax
+mulq  56(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 264(%rsp),%rax
-mulq  120(%rsp)
+movq 208(%rsp),%rax
+mulq  64(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 264(%rsp),%rdx
+movq 208(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  128(%rsp)
+mulq  72(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 272(%rsp),%rax
-mulq  96(%rsp)
+movq 216(%rsp),%rax
+mulq  40(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 272(%rsp),%rax
-mulq  104(%rsp)
+movq 216(%rsp),%rax
+mulq  48(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 272(%rsp),%rax
-mulq  112(%rsp)
+movq 216(%rsp),%rax
+mulq  56(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 272(%rsp),%rdx
+movq 216(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  120(%rsp)
+mulq  64(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 272(%rsp),%rdx
+movq 216(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  128(%rsp)
+mulq  72(%rsp)
+add  %rax,%r8
+adc %rdx,%r9
+movq 224(%rsp),%rax
+mulq  40(%rsp)
+add  %rax,%r12
+adc %rdx,%r13
+movq 224(%rsp),%rax
+mulq  48(%rsp)
+add  %rax,%r14
+adc %rdx,%r15
+movq 280(%rsp),%rax
+mulq  64(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
 movq 280(%rsp),%rax
-mulq  96(%rsp)
-add  %rax,%r12
-adc %rdx,%r13
-movq 280(%rsp),%rax
-mulq  104(%rsp)
+mulq  72(%rsp)
+add  %rax,%r10
+adc %rdx,%r11
+movq 232(%rsp),%rax
+mulq  40(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 336(%rsp),%rax
-mulq  120(%rsp)
+movq 288(%rsp),%rax
+mulq  56(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 336(%rsp),%rax
-mulq  128(%rsp)
+movq 288(%rsp),%rax
+mulq  64(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
 movq 288(%rsp),%rax
-mulq  96(%rsp)
-add  %rax,%r14
-adc %rdx,%r15
-movq 344(%rsp),%rax
-mulq  112(%rsp)
-add  %rax,%r8
-adc %rdx,%r9
-movq 344(%rsp),%rax
-mulq  120(%rsp)
-add  %rax,%r10
-adc %rdx,%r11
-movq 344(%rsp),%rax
-mulq  128(%rsp)
+mulq  72(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq x25519_x86_64_REDMASK51(%rip),%rdx
@@ -941,116 +926,116 @@
 imulq  $19,%rcx,%rcx
 add  %rcx,%rsi
 and  %rdx,%r10
-movq %rsi,96(%rsp)
-movq %r8,104(%rsp)
-movq %r9,112(%rsp)
-movq %rax,120(%rsp)
-movq %r10,128(%rsp)
-movq 320(%rsp),%rsi
+movq %rsi,40(%rsp)
+movq %r8,48(%rsp)
+movq %r9,56(%rsp)
+movq %rax,64(%rsp)
+movq %r10,72(%rsp)
+movq 264(%rsp),%rsi
 imulq  $19,%rsi,%rax
-movq %rax,256(%rsp)
-mulq  72(%rsp)
+movq %rax,200(%rsp)
+mulq  16(%rsp)
 mov  %rax,%rsi
 mov  %rdx,%rcx
-movq 328(%rsp),%rdx
+movq 272(%rsp),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,264(%rsp)
-mulq  64(%rsp)
+movq %rax,208(%rsp)
+mulq  8(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 296(%rsp),%rax
-mulq  56(%rsp)
+movq 240(%rsp),%rax
+mulq  0(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 296(%rsp),%rax
-mulq  64(%rsp)
+movq 240(%rsp),%rax
+mulq  8(%rsp)
 mov  %rax,%r8
 mov  %rdx,%r9
-movq 296(%rsp),%rax
-mulq  72(%rsp)
+movq 240(%rsp),%rax
+mulq  16(%rsp)
 mov  %rax,%r10
 mov  %rdx,%r11
-movq 296(%rsp),%rax
-mulq  80(%rsp)
+movq 240(%rsp),%rax
+mulq  24(%rsp)
 mov  %rax,%r12
 mov  %rdx,%r13
-movq 296(%rsp),%rax
-mulq  88(%rsp)
+movq 240(%rsp),%rax
+mulq  32(%rsp)
 mov  %rax,%r14
 mov  %rdx,%r15
-movq 304(%rsp),%rax
-mulq  56(%rsp)
+movq 248(%rsp),%rax
+mulq  0(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 304(%rsp),%rax
-mulq  64(%rsp)
+movq 248(%rsp),%rax
+mulq  8(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 304(%rsp),%rax
-mulq  72(%rsp)
+movq 248(%rsp),%rax
+mulq  16(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 304(%rsp),%rax
-mulq  80(%rsp)
+movq 248(%rsp),%rax
+mulq  24(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 304(%rsp),%rdx
+movq 248(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  88(%rsp)
+mulq  32(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 312(%rsp),%rax
-mulq  56(%rsp)
+movq 256(%rsp),%rax
+mulq  0(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 312(%rsp),%rax
-mulq  64(%rsp)
+movq 256(%rsp),%rax
+mulq  8(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 312(%rsp),%rax
-mulq  72(%rsp)
+movq 256(%rsp),%rax
+mulq  16(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 312(%rsp),%rdx
+movq 256(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  80(%rsp)
+mulq  24(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 312(%rsp),%rdx
+movq 256(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  88(%rsp)
+mulq  32(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 320(%rsp),%rax
-mulq  56(%rsp)
+movq 264(%rsp),%rax
+mulq  0(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 320(%rsp),%rax
-mulq  64(%rsp)
+movq 264(%rsp),%rax
+mulq  8(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 256(%rsp),%rax
-mulq  80(%rsp)
+movq 200(%rsp),%rax
+mulq  24(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 256(%rsp),%rax
-mulq  88(%rsp)
+movq 200(%rsp),%rax
+mulq  32(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 328(%rsp),%rax
-mulq  56(%rsp)
+movq 272(%rsp),%rax
+mulq  0(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 264(%rsp),%rax
-mulq  72(%rsp)
+movq 208(%rsp),%rax
+mulq  16(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 264(%rsp),%rax
-mulq  80(%rsp)
+movq 208(%rsp),%rax
+mulq  24(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 264(%rsp),%rax
-mulq  88(%rsp)
+movq 208(%rsp),%rax
+mulq  32(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq x25519_x86_64_REDMASK51(%rip),%rdx
@@ -1101,16 +1086,16 @@
 add  x25519_x86_64_2P1234(%rip),%r11
 add  x25519_x86_64_2P1234(%rip),%r12
 add  x25519_x86_64_2P1234(%rip),%r13
-addq 96(%rsp),%rsi
-addq 104(%rsp),%r8
-addq 112(%rsp),%r9
-addq 120(%rsp),%rax
-addq 128(%rsp),%r10
-subq 96(%rsp),%rdx
-subq 104(%rsp),%rcx
-subq 112(%rsp),%r11
-subq 120(%rsp),%r12
-subq 128(%rsp),%r13
+addq 40(%rsp),%rsi
+addq 48(%rsp),%r8
+addq 56(%rsp),%r9
+addq 64(%rsp),%rax
+addq 72(%rsp),%r10
+subq 40(%rsp),%rdx
+subq 48(%rsp),%rcx
+subq 56(%rsp),%r11
+subq 64(%rsp),%r12
+subq 72(%rsp),%r13
 movq   %rsi,120(%rdi)
 movq   %r8,128(%rdi)
 movq   %r9,136(%rdi)
@@ -1353,13 +1338,13 @@
 movq   %r10,192(%rdi)
 movq   184(%rdi),%rsi
 imulq  $19,%rsi,%rax
-movq %rax,56(%rsp)
+movq %rax,0(%rsp)
 mulq  16(%rdi)
 mov  %rax,%rsi
 mov  %rdx,%rcx
 movq   192(%rdi),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,64(%rsp)
+movq %rax,8(%rsp)
 mulq  8(%rdi)
 add  %rax,%rsi
 adc %rdx,%rcx
@@ -1434,11 +1419,11 @@
 mulq  8(%rdi)
 add  %rax,%r14
 adc %rdx,%r15
-movq 56(%rsp),%rax
+movq 0(%rsp),%rax
 mulq  24(%rdi)
 add  %rax,%r8
 adc %rdx,%r9
-movq 56(%rsp),%rax
+movq 0(%rsp),%rax
 mulq  32(%rdi)
 add  %rax,%r10
 adc %rdx,%r11
@@ -1446,15 +1431,15 @@
 mulq  0(%rdi)
 add  %rax,%r14
 adc %rdx,%r15
-movq 64(%rsp),%rax
+movq 8(%rsp),%rax
 mulq  16(%rdi)
 add  %rax,%r8
 adc %rdx,%r9
-movq 64(%rsp),%rax
+movq 8(%rsp),%rax
 mulq  24(%rdi)
 add  %rax,%r10
 adc %rdx,%r11
-movq 64(%rsp),%rax
+movq 8(%rsp),%rax
 mulq  32(%rdi)
 add  %rax,%r12
 adc %rdx,%r13
@@ -1501,111 +1486,111 @@
 movq   %r9,176(%rdi)
 movq   %rax,184(%rdi)
 movq   %r10,192(%rdi)
-movq 200(%rsp),%rsi
+movq 144(%rsp),%rsi
 imulq  $19,%rsi,%rax
-movq %rax,56(%rsp)
-mulq  152(%rsp)
+movq %rax,0(%rsp)
+mulq  96(%rsp)
 mov  %rax,%rsi
 mov  %rdx,%rcx
-movq 208(%rsp),%rdx
+movq 152(%rsp),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,64(%rsp)
-mulq  144(%rsp)
+movq %rax,8(%rsp)
+mulq  88(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 176(%rsp),%rax
-mulq  136(%rsp)
+movq 120(%rsp),%rax
+mulq  80(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 176(%rsp),%rax
-mulq  144(%rsp)
+movq 120(%rsp),%rax
+mulq  88(%rsp)
 mov  %rax,%r8
 mov  %rdx,%r9
-movq 176(%rsp),%rax
-mulq  152(%rsp)
+movq 120(%rsp),%rax
+mulq  96(%rsp)
 mov  %rax,%r10
 mov  %rdx,%r11
-movq 176(%rsp),%rax
-mulq  160(%rsp)
+movq 120(%rsp),%rax
+mulq  104(%rsp)
 mov  %rax,%r12
 mov  %rdx,%r13
-movq 176(%rsp),%rax
-mulq  168(%rsp)
+movq 120(%rsp),%rax
+mulq  112(%rsp)
 mov  %rax,%r14
 mov  %rdx,%r15
-movq 184(%rsp),%rax
-mulq  136(%rsp)
+movq 128(%rsp),%rax
+mulq  80(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 184(%rsp),%rax
-mulq  144(%rsp)
+movq 128(%rsp),%rax
+mulq  88(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 184(%rsp),%rax
-mulq  152(%rsp)
+movq 128(%rsp),%rax
+mulq  96(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 184(%rsp),%rax
-mulq  160(%rsp)
+movq 128(%rsp),%rax
+mulq  104(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 184(%rsp),%rdx
+movq 128(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  168(%rsp)
+mulq  112(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 192(%rsp),%rax
-mulq  136(%rsp)
+movq 136(%rsp),%rax
+mulq  80(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 192(%rsp),%rax
-mulq  144(%rsp)
+movq 136(%rsp),%rax
+mulq  88(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 192(%rsp),%rax
-mulq  152(%rsp)
+movq 136(%rsp),%rax
+mulq  96(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 192(%rsp),%rdx
+movq 136(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  160(%rsp)
+mulq  104(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
-movq 192(%rsp),%rdx
+movq 136(%rsp),%rdx
 imulq  $19,%rdx,%rax
-mulq  168(%rsp)
+mulq  112(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 200(%rsp),%rax
-mulq  136(%rsp)
+movq 144(%rsp),%rax
+mulq  80(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
-movq 200(%rsp),%rax
-mulq  144(%rsp)
+movq 144(%rsp),%rax
+mulq  88(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 56(%rsp),%rax
-mulq  160(%rsp)
+movq 0(%rsp),%rax
+mulq  104(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 56(%rsp),%rax
-mulq  168(%rsp)
+movq 0(%rsp),%rax
+mulq  112(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 208(%rsp),%rax
-mulq  136(%rsp)
+movq 152(%rsp),%rax
+mulq  80(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 64(%rsp),%rax
-mulq  152(%rsp)
+movq 8(%rsp),%rax
+mulq  96(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 64(%rsp),%rax
-mulq  160(%rsp)
+movq 8(%rsp),%rax
+mulq  104(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 64(%rsp),%rax
-mulq  168(%rsp)
+movq 8(%rsp),%rax
+mulq  112(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq x25519_x86_64_REDMASK51(%rip),%rdx
@@ -1651,37 +1636,37 @@
 movq   %r9,56(%rdi)
 movq   %rax,64(%rdi)
 movq   %r10,72(%rdi)
-movq 216(%rsp),%rax
+movq 160(%rsp),%rax
 mulq  x25519_x86_64_121666_213(%rip)
 shr  $13,%rax
 mov  %rax,%rsi
 mov  %rdx,%rcx
-movq 224(%rsp),%rax
+movq 168(%rsp),%rax
 mulq  x25519_x86_64_121666_213(%rip)
 shr  $13,%rax
 add  %rax,%rcx
 mov  %rdx,%r8
-movq 232(%rsp),%rax
+movq 176(%rsp),%rax
 mulq  x25519_x86_64_121666_213(%rip)
 shr  $13,%rax
 add  %rax,%r8
 mov  %rdx,%r9
-movq 240(%rsp),%rax
+movq 184(%rsp),%rax
 mulq  x25519_x86_64_121666_213(%rip)
 shr  $13,%rax
 add  %rax,%r9
 mov  %rdx,%r10
-movq 248(%rsp),%rax
+movq 192(%rsp),%rax
 mulq  x25519_x86_64_121666_213(%rip)
 shr  $13,%rax
 add  %rax,%r10
 imulq  $19,%rdx,%rdx
 add  %rdx,%rsi
-addq 136(%rsp),%rsi
-addq 144(%rsp),%rcx
-addq 152(%rsp),%r8
-addq 160(%rsp),%r9
-addq 168(%rsp),%r10
+addq 80(%rsp),%rsi
+addq 88(%rsp),%rcx
+addq 96(%rsp),%r8
+addq 104(%rsp),%r9
+addq 112(%rsp),%r10
 movq   %rsi,80(%rdi)
 movq   %rcx,88(%rdi)
 movq   %r8,96(%rdi)
@@ -1689,109 +1674,109 @@
 movq   %r10,112(%rdi)
 movq   104(%rdi),%rsi
 imulq  $19,%rsi,%rax
-movq %rax,56(%rsp)
-mulq  232(%rsp)
+movq %rax,0(%rsp)
+mulq  176(%rsp)
 mov  %rax,%rsi
 mov  %rdx,%rcx
 movq   112(%rdi),%rdx
 imulq  $19,%rdx,%rax
-movq %rax,64(%rsp)
-mulq  224(%rsp)
+movq %rax,8(%rsp)
+mulq  168(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
 movq   80(%rdi),%rax
-mulq  216(%rsp)
+mulq  160(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
 movq   80(%rdi),%rax
-mulq  224(%rsp)
+mulq  168(%rsp)
 mov  %rax,%r8
 mov  %rdx,%r9
 movq   80(%rdi),%rax
-mulq  232(%rsp)
+mulq  176(%rsp)
 mov  %rax,%r10
 mov  %rdx,%r11
 movq   80(%rdi),%rax
-mulq  240(%rsp)
+mulq  184(%rsp)
 mov  %rax,%r12
 mov  %rdx,%r13
 movq   80(%rdi),%rax
-mulq  248(%rsp)
+mulq  192(%rsp)
 mov  %rax,%r14
 mov  %rdx,%r15
 movq   88(%rdi),%rax
-mulq  216(%rsp)
+mulq  160(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
 movq   88(%rdi),%rax
-mulq  224(%rsp)
+mulq  168(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
 movq   88(%rdi),%rax
-mulq  232(%rsp)
+mulq  176(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq   88(%rdi),%rax
-mulq  240(%rsp)
+mulq  184(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
 movq   88(%rdi),%rdx
 imulq  $19,%rdx,%rax
-mulq  248(%rsp)
+mulq  192(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
 movq   96(%rdi),%rax
-mulq  216(%rsp)
+mulq  160(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
 movq   96(%rdi),%rax
-mulq  224(%rsp)
+mulq  168(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq   96(%rdi),%rax
-mulq  232(%rsp)
+mulq  176(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
 movq   96(%rdi),%rdx
 imulq  $19,%rdx,%rax
-mulq  240(%rsp)
+mulq  184(%rsp)
 add  %rax,%rsi
 adc %rdx,%rcx
 movq   96(%rdi),%rdx
 imulq  $19,%rdx,%rax
-mulq  248(%rsp)
+mulq  192(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
 movq   104(%rdi),%rax
-mulq  216(%rsp)
+mulq  160(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq   104(%rdi),%rax
-mulq  224(%rsp)
+mulq  168(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 56(%rsp),%rax
-mulq  240(%rsp)
+movq 0(%rsp),%rax
+mulq  184(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 56(%rsp),%rax
-mulq  248(%rsp)
+movq 0(%rsp),%rax
+mulq  192(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
 movq   112(%rdi),%rax
-mulq  216(%rsp)
+mulq  160(%rsp)
 add  %rax,%r14
 adc %rdx,%r15
-movq 64(%rsp),%rax
-mulq  232(%rsp)
+movq 8(%rsp),%rax
+mulq  176(%rsp)
 add  %rax,%r8
 adc %rdx,%r9
-movq 64(%rsp),%rax
-mulq  240(%rsp)
+movq 8(%rsp),%rax
+mulq  184(%rsp)
 add  %rax,%r10
 adc %rdx,%r11
-movq 64(%rsp),%rax
-mulq  248(%rsp)
+movq 8(%rsp),%rax
+mulq  192(%rsp)
 add  %rax,%r12
 adc %rdx,%r13
 movq x25519_x86_64_REDMASK51(%rip),%rdx
@@ -1837,26 +1822,22 @@
 movq   %r9,96(%rdi)
 movq   %rax,104(%rdi)
 movq   %r10,112(%rdi)
-movq 0(%rsp),%r11
-movq 8(%rsp),%r12
-movq 16(%rsp),%r13
-movq 24(%rsp),%r14
-movq 32(%rsp),%r15
-movq 40(%rsp),%rbx
-movq 48(%rsp),%rbp
-add %r11,%rsp
-mov %rdi,%rax
-mov %rsi,%rdx
+movq 296(%rsp),%r12
+movq 304(%rsp),%r13
+movq 312(%rsp),%r14
+movq 320(%rsp),%r15
+movq 328(%rsp),%rbx
+movq 336(%rsp),%rbp
+add $344,%rsp
+.cfi_adjust_cfa_offset -344
 ret
+.cfi_endproc
 
 .p2align 5
 .globl C_ABI(x25519_x86_64_work_cswap)
 HIDDEN C_ABI(x25519_x86_64_work_cswap)
 C_ABI(x25519_x86_64_work_cswap):
-mov %rsp,%r11
-and $31,%r11
-add $0,%r11
-sub %r11,%rsp
+.cfi_startproc
 cmp  $1,%rsi
 movq   0(%rdi),%rsi
 movq   80(%rdi),%rdx
@@ -1928,10 +1909,10 @@
 movq   %rdx,144(%rdi)
 movq   %rcx,72(%rdi)
 movq   %r8,152(%rdi)
-add %r11,%rsp
 mov %rdi,%rax
 mov %rsi,%rdx
 ret
+.cfi_endproc
 
 #endif  /* __x86_64__ */
 #endif  /* !OPENSSL_NO_ASM */
diff --git a/src/crypto/evp/evp_ctx.c b/src/crypto/evp/evp_ctx.c
index 8cf23bb..905aae9 100644
--- a/src/crypto/evp/evp_ctx.c
+++ b/src/crypto/evp/evp_ctx.c
@@ -201,6 +201,7 @@
     return 0;
   }
   if (keytype != -1 && ctx->pmeth->pkey_id != keytype) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
     return 0;
   }
 
diff --git a/src/crypto/rand/urandom.c b/src/crypto/rand/urandom.c
index 3ccd940..23bdcf4 100644
--- a/src/crypto/rand/urandom.c
+++ b/src/crypto/rand/urandom.c
@@ -12,7 +12,9 @@
  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
 
+#if !defined(_GNU_SOURCE)
 #define _GNU_SOURCE  /* needed for syscall() on Linux. */
+#endif
 
 #include <openssl/rand.h>
 
diff --git a/src/crypto/x509/x_x509.c b/src/crypto/x509/x_x509.c
index d3cd5b0..15118d2 100644
--- a/src/crypto/x509/x_x509.c
+++ b/src/crypto/x509/x_x509.c
@@ -56,6 +56,7 @@
  * [including the GNU Public Licence.] */
 
 #include <assert.h>
+#include <limits.h>
 #include <stdio.h>
 
 #include <openssl/asn1t.h>
@@ -151,6 +152,11 @@
 IMPLEMENT_ASN1_DUP_FUNCTION(X509)
 
 X509 *X509_parse_from_buffer(CRYPTO_BUFFER *buf) {
+  if (CRYPTO_BUFFER_len(buf) > LONG_MAX) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+    return 0;
+  }
+
   X509 *x509 = X509_new();
   if (x509 == NULL) {
     return NULL;
diff --git a/src/decrepit/CMakeLists.txt b/src/decrepit/CMakeLists.txt
index ee49bc8..223320d 100644
--- a/src/decrepit/CMakeLists.txt
+++ b/src/decrepit/CMakeLists.txt
@@ -1,7 +1,6 @@
 include_directories(../include)
 
 add_subdirectory(bio)
-add_subdirectory(biossl)
 add_subdirectory(blowfish)
 add_subdirectory(cast)
 add_subdirectory(des)
@@ -20,7 +19,6 @@
   decrepit
 
   $<TARGET_OBJECTS:bio_decrepit>
-  $<TARGET_OBJECTS:biossl_decrepit>
   $<TARGET_OBJECTS:blowfish>
   $<TARGET_OBJECTS:cast>
   $<TARGET_OBJECTS:des_decrepit>
diff --git a/src/decrepit/bio/base64_bio.c b/src/decrepit/bio/base64_bio.c
index 85f30ff..eef4e1a 100644
--- a/src/decrepit/bio/base64_bio.c
+++ b/src/decrepit/bio/base64_bio.c
@@ -526,12 +526,8 @@
   return ret;
 }
 
-static int b64_puts(BIO *b, const char *str) {
-  return b64_write(b, str, strlen(str));
-}
-
 static const BIO_METHOD b64_method = {
-    BIO_TYPE_BASE64, "base64 encoding", b64_write, b64_read, b64_puts,
+    BIO_TYPE_BASE64, "base64 encoding", b64_write, b64_read, NULL /* puts */,
     NULL /* gets */, b64_ctrl,          b64_new,   b64_free, b64_callback_ctrl,
 };
 
diff --git a/src/decrepit/biossl/CMakeLists.txt b/src/decrepit/biossl/CMakeLists.txt
deleted file mode 100644
index 39fe139..0000000
--- a/src/decrepit/biossl/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-include_directories(../../include)
-
-add_library(
-  biossl_decrepit
-
-  OBJECT
-
-  bio_ssl.c
-)
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 110629e..6ba1421 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -493,23 +493,6 @@
 OPENSSL_EXPORT int BIO_rw_filename(BIO *bio, const char *filename);
 
 
-/* Buffer BIOs.
- *
- * Buffer BIOs are a filter-type BIO, i.e. they are designed to be used in a
- * chain of BIOs. They provide buffering to reduce the number of operations on
- * the underlying BIOs. */
-
-OPENSSL_EXPORT const BIO_METHOD *BIO_f_buffer(void);
-
-/* BIO_set_read_buffer_size sets the size, in bytes, of the read buffer and
- * clears it. It returns one on success and zero on failure. */
-OPENSSL_EXPORT int BIO_set_read_buffer_size(BIO *bio, int buffer_size);
-
-/* BIO_set_write_buffer_size sets the size, in bytes, of the write buffer and
- * clears it. It returns one on success and zero on failure. */
-OPENSSL_EXPORT int BIO_set_write_buffer_size(BIO *bio, int buffer_size);
-
-
 /* Socket BIOs.
  *
  * Socket BIOs behave like file descriptor BIOs but, on Windows systems, wrap
@@ -672,6 +655,9 @@
 
 OPENSSL_EXPORT void BIO_set_retry_special(BIO *bio);
 
+/* BIO_set_write_buffer_size returns zero. */
+OPENSSL_EXPORT int BIO_set_write_buffer_size(BIO *bio, int buffer_size);
+
 
 /* Private functions */
 
diff --git a/src/include/openssl/sha.h b/src/include/openssl/sha.h
index 2ddcb62..7c31097 100644
--- a/src/include/openssl/sha.h
+++ b/src/include/openssl/sha.h
@@ -73,10 +73,6 @@
 /* SHA_DIGEST_LENGTH is the length of a SHA-1 digest. */
 #define SHA_DIGEST_LENGTH 20
 
-/* TODO(fork): remove */
-#define SHA_LBLOCK 16
-#define SHA_LONG uint32_t
-
 /* SHA1_Init initialises |sha| and returns one. */
 OPENSSL_EXPORT int SHA1_Init(SHA_CTX *sha);
 
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index 1491a53..3bb5b78 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -1726,19 +1726,36 @@
 OPENSSL_EXPORT SSL_SESSION *SSL_get1_session(SSL *ssl);
 
 /* SSL_DEFAULT_SESSION_TIMEOUT is the default lifetime, in seconds, of a
- * session. */
+ * session in TLS 1.2 or earlier. This is how long we are willing to use the
+ * secret to encrypt traffic without fresh key material. */
 #define SSL_DEFAULT_SESSION_TIMEOUT (2 * 60 * 60)
 
-/* SSL_CTX_set_timeout sets the lifetime, in seconds, of sessions created in
- * |ctx| to |timeout|. */
+/* SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT is the default lifetime, in seconds, of a
+ * session for TLS 1.3 psk_dhe_ke. This is how long we are willing to use the
+ * secret as an authenticator. */
+#define SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT (2 * 24 * 60 * 60)
+
+/* SSL_DEFAULT_SESSION_AUTH_TIMEOUT is the default non-renewable lifetime, in
+ * seconds, of a TLS 1.3 session. This is how long we are willing to trust the
+ * signature in the initial handshake. */
+#define SSL_DEFAULT_SESSION_AUTH_TIMEOUT (7 * 24 * 60 * 60)
+
+/* SSL_CTX_set_timeout sets the lifetime, in seconds, of TLS 1.2 (or earlier)
+ * sessions created in |ctx| to |timeout|. */
 OPENSSL_EXPORT long SSL_CTX_set_timeout(SSL_CTX *ctx, long timeout);
 
-/* SSL_CTX_get_timeout returns the lifetime, in seconds, of sessions created in
- * |ctx|. */
+/* SSL_CTX_set_session_psk_dhe_timeout sets the lifetime, in seconds, of TLS 1.3
+ * sessions created in |ctx| to |timeout|. */
+OPENSSL_EXPORT void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx,
+                                                        long timeout);
+
+/* SSL_CTX_get_timeout returns the lifetime, in seconds, of TLS 1.2 (or earlier)
+ * sessions created in |ctx|. */
 OPENSSL_EXPORT long SSL_CTX_get_timeout(const SSL_CTX *ctx);
 
-/* SSL_set_session_timeout sets the default lifetime, in seconds, of the
- * session created in |ssl| to |timeout|, and returns the old value.
+/* SSL_set_session_timeout sets the default lifetime, in seconds, of a TLS 1.2
+ * (or earlier) session created in |ssl| to |timeout|, and returns the old
+ * value.
  *
  * By default the value |SSL_DEFAULT_SESSION_TIMEOUT| is used, which can be
  * overridden at the context level by calling |SSL_CTX_set_timeout|.
@@ -1746,6 +1763,10 @@
  * If |timeout| is zero the newly created session will not be resumable. */
 OPENSSL_EXPORT long SSL_set_session_timeout(SSL *ssl, long timeout);
 
+/* SSL_set_session_psk_dhe_timeout sets the lifetime, in seconds, of TLS 1.3
+ * sessions created in |ssl| to |timeout|. */
+OPENSSL_EXPORT void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout);
+
 /* SSL_CTX_set_session_id_context sets |ctx|'s session ID context to |sid_ctx|.
  * It returns one on success and zero on error. The session ID context is an
  * application-defined opaque byte string. A session will not be used in a
@@ -2968,6 +2989,10 @@
 OPENSSL_EXPORT int SSL_set_max_send_fragment(SSL *ssl,
                                              size_t max_send_fragment);
 
+/* SSL_get_v2clienthello_count returns the total number of V2ClientHellos that
+ * are accepted. */
+OPENSSL_EXPORT uint64_t SSL_get_v2clienthello_count(void);
+
 /* ssl_early_callback_ctx (aka |SSL_CLIENT_HELLO|) is passed to certain
  * callbacks that are called very early on during the server handshake. At this
  * point, much of the SSL* hasn't been filled out and only the ClientHello can
@@ -3646,7 +3671,9 @@
 
 /* BIO_f_ssl returns a |BIO_METHOD| that can wrap an |SSL*| in a |BIO*|. Note
  * that this has quite different behaviour from the version in OpenSSL (notably
- * that it doesn't try to auto renegotiate). */
+ * that it doesn't try to auto renegotiate).
+ *
+ * IMPORTANT: if you are not curl, don't use this. */
 OPENSSL_EXPORT const BIO_METHOD *BIO_f_ssl(void);
 
 /* BIO_set_ssl sets |ssl| as the underlying connection for |bio|, which must
@@ -3732,9 +3759,14 @@
    * non-fatal certificate errors. */
   long verify_result;
 
-  /* timeout is the lifetime of the session in seconds, measured from |time|. */
+  /* timeout is the lifetime of the session in seconds, measured from |time|.
+   * This is renewable up to |auth_timeout|. */
   long timeout;
 
+  /* auth_timeout is the non-renewable lifetime of the session in seconds,
+   * measured from |time|. */
+  long auth_timeout;
+
   /* time is the time the session was issued, measured in seconds from the UNIX
    * epoch. */
   long time;
@@ -3868,10 +3900,14 @@
    * SSL_accept which cache SSL_SESSIONS. */
   int session_cache_mode;
 
-  /* If timeout is not 0, it is the default timeout value set when SSL_new() is
-   * called.  This has been put in to make life easier to set things up */
+  /* session_timeout is the default lifetime for new sessions in TLS 1.2 and
+   * earlier, in seconds. */
   long session_timeout;
 
+  /* session_psk_dhe_timeout is the default lifetime for new sessions in TLS
+   * 1.3, in seconds. */
+  long session_psk_dhe_timeout;
+
   /* If this callback is not null, it will be called each time a session id is
    * added to the cache.  If this function returns 1, it means that the
    * callback will do a SSL_SESSION_free() when it has finished using it.
@@ -4106,17 +4142,6 @@
   BIO *rbio; /* used by SSL_read */
   BIO *wbio; /* used by SSL_write */
 
-  /* bbio, if non-NULL, is a buffer placed in front of |wbio| to pack handshake
-   * messages within one flight into a single |BIO_write|. In this case, |wbio|
-   * and |bbio| are equal and the true caller-configured BIO is
-   * |bbio->next_bio|.
-   *
-   * TODO(davidben): This does not work right for DTLS. It assumes the MTU is
-   * smaller than the buffer size so that the buffer's internal flushing never
-   * kicks in. It also doesn't kick in for DTLS retransmission. Replace this
-   * with a better mechanism. */
-  BIO *bbio;
-
   int (*handshake_func)(SSL_HANDSHAKE *hs);
 
   BUF_MEM *init_buf; /* buffer used during init */
@@ -4126,10 +4151,6 @@
   /* init_num is the length of the current handshake message body. */
   uint32_t init_num;
 
-  /* init_off, in DTLS, is the number of bytes of the current message that have
-   * been written. */
-  uint32_t init_off;
-
   struct ssl3_state_st *s3;  /* SSLv3 variables */
   struct dtls1_state_st *d1; /* DTLSv1 variables */
 
@@ -4249,9 +4270,13 @@
   unsigned retain_only_sha256_of_client_certs:1;
 
   /* session_timeout is the default lifetime in seconds of the session
-   * created in this connection. */
+   * created in this connection at TLS 1.2 and earlier. */
   long session_timeout;
 
+  /* session_psk_dhe_timeout is the default lifetime in seconds of sessions
+   * created in this connection at TLS 1.3. */
+  long session_psk_dhe_timeout;
+
   /* OCSP response to be sent to the client, if requested. */
   CRYPTO_BUFFER *ocsp_response;
 };
diff --git a/src/include/openssl/ssl3.h b/src/include/openssl/ssl3.h
index e75b70d..84051ff 100644
--- a/src/include/openssl/ssl3.h
+++ b/src/include/openssl/ssl3.h
@@ -309,7 +309,6 @@
 #define SSL3_ST_VERIFY_SERVER_CERT (0x102 | SSL_ST_CONNECT)
 /* write to server */
 #define SSL3_ST_CW_CLNT_HELLO_A (0x110 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_CLNT_HELLO_B (0x111 | SSL_ST_CONNECT)
 /* read from server */
 #define SSL3_ST_CR_SRVR_HELLO_A (0x120 | SSL_ST_CONNECT)
 #define DTLS1_ST_CR_HELLO_VERIFY_REQUEST_A (0x126 | SSL_ST_CONNECT)
@@ -320,19 +319,13 @@
 #define SSL3_ST_CR_SRVR_DONE_A (0x160 | SSL_ST_CONNECT)
 /* write to server */
 #define SSL3_ST_CW_CERT_A (0x170 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_CERT_B (0x171 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_KEY_EXCH_A (0x180 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_KEY_EXCH_B (0x181 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_CERT_VRFY_A (0x190 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_CERT_VRFY_B (0x191 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_CERT_VRFY_C (0x192 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_CHANGE (0x1A0 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_NEXT_PROTO_A (0x200 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_NEXT_PROTO_B (0x201 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_CHANNEL_ID_A (0x220 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_CHANNEL_ID_B (0x221 | SSL_ST_CONNECT)
 #define SSL3_ST_CW_FINISHED_A (0x1B0 | SSL_ST_CONNECT)
-#define SSL3_ST_CW_FINISHED_B (0x1B1 | SSL_ST_CONNECT)
 /* read from server */
 #define SSL3_ST_CR_CHANGE (0x1C0 | SSL_ST_CONNECT)
 #define SSL3_ST_CR_FINISHED_A (0x1D0 | SSL_ST_CONNECT)
@@ -351,22 +344,16 @@
 #define SSL3_ST_SR_CLNT_HELLO_B (0x111 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CLNT_HELLO_C (0x112 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_CLNT_HELLO_D (0x113 | SSL_ST_ACCEPT)
-#define SSL3_ST_SR_CLNT_HELLO_E (0x114 | SSL_ST_ACCEPT)
 /* write to client */
 #define SSL3_ST_SW_HELLO_REQ_A (0x120 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_HELLO_REQ_B (0x121 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_HELLO_REQ_C (0x122 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_SRVR_HELLO_A (0x130 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_SRVR_HELLO_B (0x131 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_A (0x140 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_CERT_B (0x141 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_KEY_EXCH_A (0x150 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_KEY_EXCH_B (0x151 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_KEY_EXCH_C (0x152 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_REQ_A (0x160 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_CERT_REQ_B (0x161 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_SRVR_DONE_A (0x170 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_SRVR_DONE_B (0x171 | SSL_ST_ACCEPT)
 /* read from client */
 #define SSL3_ST_SR_CERT_A (0x180 | SSL_ST_ACCEPT)
 #define SSL3_ST_SR_KEY_EXCH_A (0x190 | SSL_ST_ACCEPT)
@@ -380,13 +367,8 @@
 /* write to client */
 #define SSL3_ST_SW_CHANGE (0x1D0 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_FINISHED_A (0x1E0 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_FINISHED_B (0x1E1 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_SESSION_TICKET_A (0x1F0 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_SESSION_TICKET_B (0x1F1 | SSL_ST_ACCEPT)
 #define SSL3_ST_SW_CERT_STATUS_A (0x200 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_CERT_STATUS_B (0x201 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_SUPPLEMENTAL_DATA_A (0x220 | SSL_ST_ACCEPT)
-#define SSL3_ST_SW_SUPPLEMENTAL_DATA_B (0x221 | SSL_ST_ACCEPT)
 
 #define SSL3_MT_HELLO_REQUEST 0
 #define SSL3_MT_CLIENT_HELLO 1
diff --git a/src/ssl/CMakeLists.txt b/src/ssl/CMakeLists.txt
index 7102769..afc3a39 100644
--- a/src/ssl/CMakeLists.txt
+++ b/src/ssl/CMakeLists.txt
@@ -3,6 +3,7 @@
 add_library(
   ssl
 
+  bio_ssl.c
   custom_extensions.c
   handshake_server.c
   handshake_client.c
diff --git a/src/decrepit/biossl/bio_ssl.c b/src/ssl/bio_ssl.c
similarity index 93%
rename from src/decrepit/biossl/bio_ssl.c
rename to src/ssl/bio_ssl.c
index f8147c6..ad8f5d8 100644
--- a/src/decrepit/biossl/bio_ssl.c
+++ b/src/ssl/bio_ssl.c
@@ -107,14 +107,14 @@
       return 1;
 
     case BIO_CTRL_WPENDING:
-      return BIO_ctrl(ssl->wbio, cmd, num, ptr);
+      return BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr);
 
     case BIO_CTRL_PENDING:
       return SSL_pending(ssl);
 
     case BIO_CTRL_FLUSH: {
       BIO_clear_retry_flags(bio);
-      long ret = BIO_ctrl(ssl->wbio, cmd, num, ptr);
+      long ret = BIO_ctrl(SSL_get_wbio(ssl), cmd, num, ptr);
       BIO_copy_next_retry(bio);
       return ret;
     }
@@ -125,7 +125,7 @@
       return -1;
 
     default:
-      return BIO_ctrl(ssl->rbio, cmd, num, ptr);
+      return BIO_ctrl(SSL_get_rbio(ssl), cmd, num, ptr);
   }
 }
 
@@ -159,7 +159,7 @@
       return -1;
 
     default:
-      return BIO_callback_ctrl(ssl->rbio, cmd, fp);
+      return BIO_callback_ctrl(SSL_get_rbio(ssl), cmd, fp);
   }
 }
 
diff --git a/src/ssl/d1_both.c b/src/ssl/d1_both.c
index d3e4a92..48a5c54 100644
--- a/src/ssl/d1_both.c
+++ b/src/ssl/d1_both.c
@@ -122,6 +122,7 @@
 #include <openssl/evp.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/type_check.h>
 #include <openssl/x509.h>
 
 #include "../crypto/internal.h"
@@ -311,9 +312,9 @@
   }
 
   /* 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.
+   * 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
@@ -384,8 +385,8 @@
     assert(msg_len > 0);
 
     /* Copy the body into the fragment. */
-    OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off, CBS_data(&body),
-           CBS_len(&body));
+    OPENSSL_memcpy(frag->data + DTLS1_HM_HEADER_LENGTH + frag_off,
+                   CBS_data(&body), CBS_len(&body));
     dtls1_hm_fragment_mark(frag, frag_off, frag_off + frag_len);
   }
 
@@ -394,17 +395,11 @@
   return 1;
 }
 
-int dtls1_get_message(SSL *ssl, int msg_type,
-                      enum ssl_hash_message_t hash_message) {
+int dtls1_get_message(SSL *ssl) {
   if (ssl->s3->tmp.reuse_message) {
-    /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
-     * ssl_dont_hash_message would have to have been applied to the previous
-     * call. */
-    assert(hash_message == ssl_hash_message);
+    /* There must be a current message. */
     assert(ssl->init_msg != NULL);
-
     ssl->s3->tmp.reuse_message = 0;
-    hash_message = ssl_dont_hash_message;
   } else {
     dtls1_release_current_message(ssl, 0 /* don't free buffer */);
   }
@@ -429,15 +424,6 @@
   ssl->init_msg = frag->data + DTLS1_HM_HEADER_LENGTH;
   ssl->init_num = frag->msg_len;
 
-  if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    return -1;
-  }
-  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
-    return -1;
-  }
-
   ssl_do_msg_callback(ssl, 0 /* read */, SSL3_RT_HANDSHAKE, frag->data,
                       ssl->init_num + DTLS1_HM_HEADER_LENGTH);
   return 1;
@@ -507,219 +493,14 @@
 
 /* Sending handshake messages. */
 
-static void dtls1_update_mtu(SSL *ssl) {
-  /* TODO(davidben): What is this code doing and do we need it? */
-  if (ssl->d1->mtu < dtls1_min_mtu() &&
-      !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
-    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
-    if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
-      ssl->d1->mtu = (unsigned)mtu;
-    } else {
-      ssl->d1->mtu = kDefaultMTU;
-      BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
-    }
-  }
-
-  /* The MTU should be above the minimum now. */
-  assert(ssl->d1->mtu >= dtls1_min_mtu());
-}
-
-/* dtls1_max_record_size returns the maximum record body length that may be
- * written without exceeding the MTU. It accounts for any buffering installed on
- * the write BIO. If no record may be written, it returns zero. */
-static size_t dtls1_max_record_size(const SSL *ssl) {
-  size_t ret = ssl->d1->mtu;
-
-  size_t overhead = SSL_max_seal_overhead(ssl);
-  if (ret <= overhead) {
-    return 0;
-  }
-  ret -= overhead;
-
-  size_t pending = BIO_wpending(ssl->wbio);
-  if (ret <= pending) {
-    return 0;
-  }
-  ret -= pending;
-
-  return ret;
-}
-
-static int dtls1_write_change_cipher_spec(SSL *ssl,
-                                          enum dtls1_use_epoch_t use_epoch) {
-  dtls1_update_mtu(ssl);
-
-  /* During the handshake, wbio is buffered to pack messages together. Flush the
-   * buffer if the ChangeCipherSpec would not fit in a packet. */
-  if (dtls1_max_record_size(ssl) == 0) {
-    int ret = BIO_flush(ssl->wbio);
-    if (ret <= 0) {
-      ssl->rwstate = SSL_WRITING;
-      return ret;
-    }
-  }
-
-  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
-  int ret =
-      dtls1_write_record(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                         sizeof(kChangeCipherSpec), use_epoch);
-  if (ret <= 0) {
-    return ret;
-  }
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
-                      kChangeCipherSpec, sizeof(kChangeCipherSpec));
-  return 1;
-}
-
-/* dtls1_do_handshake_write writes handshake message |in| using the given epoch,
- * starting |offset| bytes into the message body. It returns one on success. On
- * error, it returns <= 0 and sets |*out_offset| to the number of bytes of body
- * that were successfully written. This may be used to retry the write
- * later. |in| must be a reassembled handshake message with the full DTLS
- * handshake header. */
-static int dtls1_do_handshake_write(SSL *ssl, size_t *out_offset,
-                                    const uint8_t *in, size_t offset,
-                                    size_t len,
-                                    enum dtls1_use_epoch_t use_epoch) {
-  dtls1_update_mtu(ssl);
-
-  int ret = -1;
-  CBB cbb;
-  CBB_zero(&cbb);
-  /* Allocate a temporary buffer to hold the message fragments to avoid
-   * clobbering the message. */
-  uint8_t *buf = OPENSSL_malloc(ssl->d1->mtu);
-  if (buf == NULL) {
-    goto err;
-  }
-
-  /* Although it may be sent as multiple fragments, a DTLS message must be sent
-   * serialized as a single fragment for purposes of |ssl_do_msg_callback| and
-   * the handshake hash. */
-  CBS cbs, body;
-  struct hm_header_st hdr;
-  CBS_init(&cbs, in, len);
-  if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
-      hdr.frag_off != 0 ||
-      hdr.frag_len != CBS_len(&body) ||
-      hdr.msg_len != CBS_len(&body) ||
-      !CBS_skip(&body, offset) ||
-      CBS_len(&cbs) != 0) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    goto err;
-  }
-
-  do {
-    /* During the handshake, wbio is buffered to pack messages together. Flush
-     * the buffer if there isn't enough room to make progress. */
-    if (dtls1_max_record_size(ssl) < DTLS1_HM_HEADER_LENGTH + 1) {
-      int flush_ret = BIO_flush(ssl->wbio);
-      if (flush_ret <= 0) {
-        ssl->rwstate = SSL_WRITING;
-        ret = flush_ret;
-        goto err;
-      }
-      assert(BIO_wpending(ssl->wbio) == 0);
-    }
-
-    size_t todo = dtls1_max_record_size(ssl);
-    if (todo < DTLS1_HM_HEADER_LENGTH + 1) {
-      /* To make forward progress, the MTU must, at minimum, fit the handshake
-       * header and one byte of handshake body. */
-      OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
-      goto err;
-    }
-    todo -= DTLS1_HM_HEADER_LENGTH;
-
-    if (todo > CBS_len(&body)) {
-      todo = CBS_len(&body);
-    }
-    if (todo >= (1u << 24)) {
-      todo = (1u << 24) - 1;
-    }
-
-    size_t buf_len;
-    if (!CBB_init_fixed(&cbb, buf, ssl->d1->mtu) ||
-        !CBB_add_u8(&cbb, hdr.type) ||
-        !CBB_add_u24(&cbb, hdr.msg_len) ||
-        !CBB_add_u16(&cbb, hdr.seq) ||
-        !CBB_add_u24(&cbb, offset) ||
-        !CBB_add_u24(&cbb, todo) ||
-        !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
-        !CBB_finish(&cbb, NULL, &buf_len)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-
-    int write_ret =
-        dtls1_write_record(ssl, SSL3_RT_HANDSHAKE, buf, buf_len, use_epoch);
-    if (write_ret <= 0) {
-      ret = write_ret;
-      goto err;
-    }
-
-    if (!CBS_skip(&body, todo)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      goto err;
-    }
-    offset += todo;
-  } while (CBS_len(&body) != 0);
-
-  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, in, len);
-
-  ret = 1;
-
-err:
-  *out_offset = offset;
-  CBB_cleanup(&cbb);
-  OPENSSL_free(buf);
-  return ret;
-}
-
 void dtls_clear_outgoing_messages(SSL *ssl) {
   for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
     OPENSSL_free(ssl->d1->outgoing_messages[i].data);
     ssl->d1->outgoing_messages[i].data = NULL;
   }
   ssl->d1->outgoing_messages_len = 0;
-}
-
-/* dtls1_add_change_cipher_spec adds a ChangeCipherSpec to the current
- * handshake flight. */
-static int dtls1_add_change_cipher_spec(SSL *ssl) {
-  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
-  DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
-  msg->data = NULL;
-  msg->len = 0;
-  msg->epoch = ssl->d1->w_epoch;
-  msg->is_ccs = 1;
-
-  ssl->d1->outgoing_messages_len++;
-  return 1;
-}
-
-static int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) {
-  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    OPENSSL_free(data);
-    return 0;
-  }
-
-  DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
-  msg->data = data;
-  msg->len = len;
-  msg->epoch = ssl->d1->w_epoch;
-  msg->is_ccs = 0;
-
-  ssl->d1->outgoing_messages_len++;
-  return 1;
+  ssl->d1->outgoing_written = 0;
+  ssl->d1->outgoing_offset = 0;
 }
 
 int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
@@ -752,36 +533,91 @@
   return 1;
 }
 
-int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
-  ssl3_update_handshake_hash(ssl, msg, len);
-
-  ssl->d1->handshake_write_seq++;
-  ssl->init_off = 0;
-  return dtls1_add_message(ssl, msg, len);
-}
-
-int dtls1_write_message(SSL *ssl) {
-  if (ssl->d1->outgoing_messages_len == 0) {
+/* add_outgoing adds a new handshake message or ChangeCipherSpec to the current
+ * outgoing flight. It returns one on success and zero on error. In both cases,
+ * it takes ownership of |data| and releases it with |OPENSSL_free| when
+ * done. */
+static int add_outgoing(SSL *ssl, int is_ccs, uint8_t *data, size_t len) {
+  OPENSSL_COMPILE_ASSERT(SSL_MAX_HANDSHAKE_FLIGHT <
+                             (1 << 8 * sizeof(ssl->d1->outgoing_messages_len)),
+                         outgoing_messages_len_is_too_small);
+  if (ssl->d1->outgoing_messages_len >= SSL_MAX_HANDSHAKE_FLIGHT) {
+    assert(0);
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
+    OPENSSL_free(data);
+    return 0;
   }
 
-  const DTLS_OUTGOING_MESSAGE *msg =
-      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len - 1];
-  if (msg->is_ccs) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return -1;
+  if (!is_ccs) {
+    ssl3_update_handshake_hash(ssl, data, len);
+    ssl->d1->handshake_write_seq++;
   }
 
-  size_t offset = ssl->init_off;
-  int ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
-                                     dtls1_use_current_epoch);
-  ssl->init_off = offset;
-  return ret;
+  DTLS_OUTGOING_MESSAGE *msg =
+      &ssl->d1->outgoing_messages[ssl->d1->outgoing_messages_len];
+  msg->data = data;
+  msg->len = len;
+  msg->epoch = ssl->d1->w_epoch;
+  msg->is_ccs = is_ccs;
+
+  ssl->d1->outgoing_messages_len++;
+  return 1;
 }
 
-static int dtls1_retransmit_message(SSL *ssl,
-                                    const DTLS_OUTGOING_MESSAGE *msg) {
+int dtls1_add_message(SSL *ssl, uint8_t *data, size_t len) {
+  return add_outgoing(ssl, 0 /* handshake */, data, len);
+}
+
+int dtls1_add_change_cipher_spec(SSL *ssl) {
+  return add_outgoing(ssl, 1 /* ChangeCipherSpec */, NULL, 0);
+}
+
+int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
+  /* The |add_alert| path is only used for warning alerts for now, which DTLS
+   * never sends. This will be implemented later once closure alerts are
+   * converted. */
+  assert(0);
+  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  return 0;
+}
+
+/* dtls1_update_mtu updates the current MTU from the BIO, ensuring it is above
+ * the minimum. */
+static void dtls1_update_mtu(SSL *ssl) {
+  /* TODO(davidben): No consumer implements |BIO_CTRL_DGRAM_SET_MTU| and the
+   * only |BIO_CTRL_DGRAM_QUERY_MTU| implementation could use
+   * |SSL_set_mtu|. Does this need to be so complex?  */
+  if (ssl->d1->mtu < dtls1_min_mtu() &&
+      !(SSL_get_options(ssl) & SSL_OP_NO_QUERY_MTU)) {
+    long mtu = BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_QUERY_MTU, 0, NULL);
+    if (mtu >= 0 && mtu <= (1 << 30) && (unsigned)mtu >= dtls1_min_mtu()) {
+      ssl->d1->mtu = (unsigned)mtu;
+    } else {
+      ssl->d1->mtu = kDefaultMTU;
+      BIO_ctrl(ssl->wbio, BIO_CTRL_DGRAM_SET_MTU, ssl->d1->mtu, NULL);
+    }
+  }
+
+  /* The MTU should be above the minimum now. */
+  assert(ssl->d1->mtu >= dtls1_min_mtu());
+}
+
+enum seal_result_t {
+  seal_error,
+  seal_no_progress,
+  seal_partial,
+  seal_success,
+};
+
+/* seal_next_message seals |msg|, which must be the next message, to |out|. If
+ * progress was made, it returns |seal_partial| or |seal_success| and sets
+ * |*out_len| to the number of bytes written. */
+static enum seal_result_t seal_next_message(SSL *ssl, uint8_t *out,
+                                            size_t *out_len, size_t max_out,
+                                            const DTLS_OUTGOING_MESSAGE *msg) {
+  assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+  assert(msg == &ssl->d1->outgoing_messages[ssl->d1->outgoing_written]);
+
   /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
    * (negotiated cipher) exist. */
   assert(ssl->d1->w_epoch == 0 || ssl->d1->w_epoch == 1);
@@ -790,56 +626,181 @@
   if (ssl->d1->w_epoch == 1 && msg->epoch == 0) {
     use_epoch = dtls1_use_previous_epoch;
   }
+  size_t overhead = dtls_max_seal_overhead(ssl, use_epoch);
+  size_t prefix = dtls_seal_prefix_len(ssl, use_epoch);
 
-  /* TODO(davidben): This cannot handle non-blocking writes. */
-  int ret;
   if (msg->is_ccs) {
-    ret = dtls1_write_change_cipher_spec(ssl, use_epoch);
-  } else {
-    size_t offset = 0;
-    ret = dtls1_do_handshake_write(ssl, &offset, msg->data, offset, msg->len,
-                                   use_epoch);
+    /* Check there is room for the ChangeCipherSpec. */
+    static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+    if (max_out < sizeof(kChangeCipherSpec) + overhead) {
+      return seal_no_progress;
+    }
+
+    if (!dtls_seal_record(ssl, out, out_len, max_out,
+                          SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                          sizeof(kChangeCipherSpec), use_epoch)) {
+      return seal_error;
+    }
+
+    ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
+                        kChangeCipherSpec, sizeof(kChangeCipherSpec));
+    return seal_success;
   }
 
-  return ret;
+  /* DTLS messages are serialized as a single fragment in |msg|. */
+  CBS cbs, body;
+  struct hm_header_st hdr;
+  CBS_init(&cbs, msg->data, msg->len);
+  if (!dtls1_parse_fragment(&cbs, &hdr, &body) ||
+      hdr.frag_off != 0 ||
+      hdr.frag_len != CBS_len(&body) ||
+      hdr.msg_len != CBS_len(&body) ||
+      !CBS_skip(&body, ssl->d1->outgoing_offset) ||
+      CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return seal_error;
+  }
+
+  /* Determine how much progress can be made. */
+  if (max_out < DTLS1_HM_HEADER_LENGTH + 1 + overhead || max_out < prefix) {
+    return seal_no_progress;
+  }
+  size_t todo = CBS_len(&body);
+  if (todo > max_out - DTLS1_HM_HEADER_LENGTH - overhead) {
+    todo = max_out - DTLS1_HM_HEADER_LENGTH - overhead;
+  }
+
+  /* Assemble a fragment, to be sealed in-place. */
+  CBB cbb;
+  uint8_t *frag = out + prefix;
+  size_t max_frag = max_out - prefix, frag_len;
+  if (!CBB_init_fixed(&cbb, frag, max_frag) ||
+      !CBB_add_u8(&cbb, hdr.type) ||
+      !CBB_add_u24(&cbb, hdr.msg_len) ||
+      !CBB_add_u16(&cbb, hdr.seq) ||
+      !CBB_add_u24(&cbb, ssl->d1->outgoing_offset) ||
+      !CBB_add_u24(&cbb, todo) ||
+      !CBB_add_bytes(&cbb, CBS_data(&body), todo) ||
+      !CBB_finish(&cbb, NULL, &frag_len)) {
+    CBB_cleanup(&cbb);
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return seal_error;
+  }
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, frag, frag_len);
+
+  if (!dtls_seal_record(ssl, out, out_len, max_out, SSL3_RT_HANDSHAKE,
+                        out + prefix, frag_len, use_epoch)) {
+    return seal_error;
+  }
+
+  if (todo == CBS_len(&body)) {
+    /* The next message is complete. */
+    ssl->d1->outgoing_offset = 0;
+    return seal_success;
+  }
+
+  ssl->d1->outgoing_offset += todo;
+  return seal_partial;
 }
 
-int dtls1_retransmit_outgoing_messages(SSL *ssl) {
-  /* Ensure we are packing handshake messages. */
-  const int was_buffered = ssl_is_wbio_buffered(ssl);
-  assert(was_buffered == SSL_in_init(ssl));
-  if (!was_buffered && !ssl_init_wbio_buffer(ssl)) {
-    return -1;
+/* seal_next_packet writes as much of the next flight as possible to |out| and
+ * advances |ssl->d1->outgoing_written| and |ssl->d1->outgoing_offset| as
+ * appropriate. */
+static int seal_next_packet(SSL *ssl, uint8_t *out, size_t *out_len,
+                            size_t max_out) {
+  int made_progress = 0;
+  size_t total = 0;
+  assert(ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len);
+  for (; ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len;
+       ssl->d1->outgoing_written++) {
+    const DTLS_OUTGOING_MESSAGE *msg =
+        &ssl->d1->outgoing_messages[ssl->d1->outgoing_written];
+    size_t len;
+    enum seal_result_t ret = seal_next_message(ssl, out, &len, max_out, msg);
+    switch (ret) {
+      case seal_error:
+        return 0;
+
+      case seal_no_progress:
+        goto packet_full;
+
+      case seal_partial:
+      case seal_success:
+        out += len;
+        max_out -= len;
+        total += len;
+        made_progress = 1;
+
+        if (ret == seal_partial) {
+          goto packet_full;
+        }
+        break;
+    }
   }
-  assert(ssl_is_wbio_buffered(ssl));
+
+packet_full:
+  /* The MTU was too small to make any progress. */
+  if (!made_progress) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_MTU_TOO_SMALL);
+    return 0;
+  }
+
+  *out_len = total;
+  return 1;
+}
+
+int dtls1_flush_flight(SSL *ssl) {
+  dtls1_update_mtu(ssl);
 
   int ret = -1;
-  for (size_t i = 0; i < ssl->d1->outgoing_messages_len; i++) {
-    if (dtls1_retransmit_message(ssl, &ssl->d1->outgoing_messages[i]) <= 0) {
+  uint8_t *packet = OPENSSL_malloc(ssl->d1->mtu);
+  if (packet == NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  while (ssl->d1->outgoing_written < ssl->d1->outgoing_messages_len) {
+    uint8_t old_written = ssl->d1->outgoing_written;
+    uint32_t old_offset = ssl->d1->outgoing_offset;
+
+    size_t packet_len;
+    if (!seal_next_packet(ssl, packet, &packet_len, ssl->d1->mtu)) {
+      goto err;
+    }
+
+    int bio_ret = BIO_write(ssl->wbio, packet, packet_len);
+    if (bio_ret <= 0) {
+      /* Retry this packet the next time around. */
+      ssl->d1->outgoing_written = old_written;
+      ssl->d1->outgoing_offset = old_offset;
+      ssl->rwstate = SSL_WRITING;
+      ret = bio_ret;
       goto err;
     }
   }
 
-  ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
+  if (BIO_flush(ssl->wbio) <= 0) {
     ssl->rwstate = SSL_WRITING;
     goto err;
   }
 
+  ret = 1;
+
 err:
-  if (!was_buffered) {
-    ssl_free_wbio_buffer(ssl);
-  }
+  OPENSSL_free(packet);
   return ret;
 }
 
-int dtls1_send_change_cipher_spec(SSL *ssl) {
-  int ret = dtls1_write_change_cipher_spec(ssl, dtls1_use_current_epoch);
-  if (ret <= 0) {
-    return ret;
-  }
-  dtls1_add_change_cipher_spec(ssl);
-  return 1;
+int dtls1_retransmit_outgoing_messages(SSL *ssl) {
+  /* Rewind to the start of the flight and write it again.
+   *
+   * TODO(davidben): This does not allow retransmits to be resumed on
+   * non-blocking write. */
+  ssl->d1->outgoing_written = 0;
+  ssl->d1->outgoing_offset = 0;
+
+  return dtls1_flush_flight(ssl);
 }
 
 unsigned int dtls1_min_mtu(void) {
diff --git a/src/ssl/d1_pkt.c b/src/ssl/d1_pkt.c
index c6950d5..27b2763 100644
--- a/src/ssl/d1_pkt.c
+++ b/src/ssl/d1_pkt.c
@@ -331,7 +331,7 @@
   }
 }
 
-int dtls1_write_app_data(SSL *ssl, const void *buf_, int len) {
+int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
   assert(!SSL_in_init(ssl));
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
@@ -348,7 +348,7 @@
     return 0;
   }
 
-  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf_, (size_t)len,
+  int ret = dtls1_write_record(ssl, SSL3_RT_APPLICATION_DATA, buf, (size_t)len,
                                dtls1_use_current_epoch);
   if (ret <= 0) {
     return ret;
@@ -364,15 +364,6 @@
    * |ssl_write_buffer_flush|. */
   assert(!ssl_write_buffer_is_pending(ssl));
 
-  /* If we have an alert to send, lets send it */
-  if (ssl->s3->alert_dispatch) {
-    int ret = ssl->method->dispatch_alert(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
-    /* if it went, fall through and send more stuff */
-  }
-
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     return -1;
@@ -397,13 +388,12 @@
 }
 
 int dtls1_dispatch_alert(SSL *ssl) {
-  ssl->s3->alert_dispatch = 0;
   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;
   }
+  ssl->s3->alert_dispatch = 0;
 
   /* If the alert is fatal, flush the BIO now. */
   if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/dtls_method.c b/src/ssl/dtls_method.c
index 702b3c0..7a35b39 100644
--- a/src/ssl/dtls_method.c
+++ b/src/ssl/dtls_method.c
@@ -99,14 +99,6 @@
   return cipher->algorithm_enc != SSL_eNULL;
 }
 
-static int dtls1_flush_flight(SSL *ssl) {
-  int ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
-  }
-  return ret;
-}
-
 static void dtls1_expect_flight(SSL *ssl) { dtls1_start_timer(ssl); }
 
 static void dtls1_received_flight(SSL *ssl) { dtls1_stop_timer(ssl); }
@@ -159,9 +151,9 @@
     dtls1_supports_cipher,
     dtls1_init_message,
     dtls1_finish_message,
-    dtls1_queue_message,
-    dtls1_write_message,
-    dtls1_send_change_cipher_spec,
+    dtls1_add_message,
+    dtls1_add_change_cipher_spec,
+    dtls1_add_alert,
     dtls1_flush_flight,
     dtls1_expect_flight,
     dtls1_received_flight,
diff --git a/src/ssl/dtls_record.c b/src/ssl/dtls_record.c
index 35c08b0..879706d 100644
--- a/src/ssl/dtls_record.c
+++ b/src/ssl/dtls_record.c
@@ -249,16 +249,27 @@
   return ssl_open_record_success;
 }
 
-size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
-  const SSL_AEAD_CTX *aead = ssl->s3->aead_write_ctx;
+static const SSL_AEAD_CTX *get_write_aead(const SSL *ssl,
+                                          enum dtls1_use_epoch_t use_epoch) {
   if (use_epoch == dtls1_use_previous_epoch) {
     /* DTLS renegotiation is unsupported, so only epochs 0 (NULL cipher) and 1
      * (negotiated cipher) exist. */
     assert(ssl->d1->w_epoch == 1);
-    aead = NULL;
+    return NULL;
   }
 
-  return DTLS1_RT_HEADER_LENGTH + SSL_AEAD_CTX_explicit_nonce_len(aead);
+  return ssl->s3->aead_write_ctx;
+}
+
+size_t dtls_max_seal_overhead(const SSL *ssl,
+                              enum dtls1_use_epoch_t use_epoch) {
+  return DTLS1_RT_HEADER_LENGTH +
+         SSL_AEAD_CTX_max_overhead(get_write_aead(ssl, use_epoch));
+}
+
+size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch) {
+  return DTLS1_RT_HEADER_LENGTH +
+         SSL_AEAD_CTX_explicit_nonce_len(get_write_aead(ssl, use_epoch));
 }
 
 int dtls_seal_record(SSL *ssl, uint8_t *out, size_t *out_len, size_t max_out,
diff --git a/src/ssl/handshake_client.c b/src/ssl/handshake_client.c
index 3389d6c..23a4cff 100644
--- a/src/ssl/handshake_client.c
+++ b/src/ssl/handshake_client.c
@@ -206,17 +206,10 @@
 
       case SSL_ST_CONNECT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
-
-        if (!ssl_init_wbio_buffer(ssl)) {
-          ret = -1;
-          goto end;
-        }
-
         hs->state = SSL3_ST_CW_CLNT_HELLO_A;
         break;
 
       case SSL3_ST_CW_CLNT_HELLO_A:
-      case SSL3_ST_CW_CLNT_HELLO_B:
         ret = ssl3_send_client_hello(hs);
         if (ret <= 0) {
           goto end;
@@ -326,7 +319,6 @@
         break;
 
       case SSL3_ST_CW_CERT_A:
-      case SSL3_ST_CW_CERT_B:
         if (hs->cert_request) {
           ret = ssl3_send_client_certificate(hs);
           if (ret <= 0) {
@@ -339,7 +331,6 @@
         break;
 
       case SSL3_ST_CW_KEY_EXCH_A:
-      case SSL3_ST_CW_KEY_EXCH_B:
         ret = ssl3_send_client_key_exchange(hs);
         if (ret <= 0) {
           goto end;
@@ -349,7 +340,6 @@
 
       case SSL3_ST_CW_CERT_VRFY_A:
       case SSL3_ST_CW_CERT_VRFY_B:
-      case SSL3_ST_CW_CERT_VRFY_C:
         if (hs->cert_request && ssl_has_certificate(ssl)) {
           ret = ssl3_send_cert_verify(hs);
           if (ret <= 0) {
@@ -362,22 +352,16 @@
         break;
 
       case SSL3_ST_CW_CHANGE:
-        ret = ssl->method->send_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-
-        hs->state = SSL3_ST_CW_NEXT_PROTO_A;
-
-        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
+        if (!ssl->method->add_change_cipher_spec(ssl) ||
+            !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_CLIENT_WRITE)) {
           ret = -1;
           goto end;
         }
 
+        hs->state = SSL3_ST_CW_NEXT_PROTO_A;
         break;
 
       case SSL3_ST_CW_NEXT_PROTO_A:
-      case SSL3_ST_CW_NEXT_PROTO_B:
         if (hs->next_proto_neg_seen) {
           ret = ssl3_send_next_proto(hs);
           if (ret <= 0) {
@@ -390,7 +374,6 @@
         break;
 
       case SSL3_ST_CW_CHANNEL_ID_A:
-      case SSL3_ST_CW_CHANNEL_ID_B:
         if (ssl->s3->tlsext_channel_id_valid) {
           ret = ssl3_send_channel_id(hs);
           if (ret <= 0) {
@@ -403,9 +386,7 @@
         break;
 
       case SSL3_ST_CW_FINISHED_A:
-      case SSL3_ST_CW_FINISHED_B:
-        ret = ssl3_send_finished(hs, SSL3_ST_CW_FINISHED_A,
-                                 SSL3_ST_CW_FINISHED_B);
+        ret = ssl3_send_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -437,8 +418,6 @@
       case SSL3_ST_FALSE_START:
         hs->state = SSL3_ST_CR_SESSION_TICKET_A;
         hs->in_false_start = 1;
-
-        ssl_free_wbio_buffer(ssl);
         ret = 1;
         goto end;
 
@@ -527,9 +506,6 @@
           ssl->s3->new_session = NULL;
         }
 
-        /* Remove write buffering now. */
-        ssl_free_wbio_buffer(ssl);
-
         const int is_initial_handshake = !ssl->s3->initial_handshake_complete;
         ssl->s3->initial_handshake_complete = 1;
         if (is_initial_handshake) {
@@ -578,11 +554,59 @@
   return ret;
 }
 
+/* ssl_get_client_disabled sets |*out_mask_a| and |*out_mask_k| to masks of
+ * disabled algorithms. */
+static void ssl_get_client_disabled(SSL *ssl, uint32_t *out_mask_a,
+                                    uint32_t *out_mask_k) {
+  int have_rsa = 0, have_ecdsa = 0;
+  *out_mask_a = 0;
+  *out_mask_k = 0;
+
+  /* Now go through all signature algorithms seeing if we support any for RSA or
+   * ECDSA. Do this for all versions not just TLS 1.2. */
+  const uint16_t *sigalgs;
+  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
+  for (size_t i = 0; i < num_sigalgs; i++) {
+    switch (sigalgs[i]) {
+      case SSL_SIGN_RSA_PSS_SHA512:
+      case SSL_SIGN_RSA_PSS_SHA384:
+      case SSL_SIGN_RSA_PSS_SHA256:
+      case SSL_SIGN_RSA_PKCS1_SHA512:
+      case SSL_SIGN_RSA_PKCS1_SHA384:
+      case SSL_SIGN_RSA_PKCS1_SHA256:
+      case SSL_SIGN_RSA_PKCS1_SHA1:
+        have_rsa = 1;
+        break;
+
+      case SSL_SIGN_ECDSA_SECP521R1_SHA512:
+      case SSL_SIGN_ECDSA_SECP384R1_SHA384:
+      case SSL_SIGN_ECDSA_SECP256R1_SHA256:
+      case SSL_SIGN_ECDSA_SHA1:
+        have_ecdsa = 1;
+        break;
+    }
+  }
+
+  /* Disable auth if we don't include any appropriate signature algorithms. */
+  if (!have_rsa) {
+    *out_mask_a |= SSL_aRSA;
+  }
+  if (!have_ecdsa) {
+    *out_mask_a |= SSL_aECDSA;
+  }
+
+  /* PSK requires a client callback. */
+  if (ssl->psk_client_callback == NULL) {
+    *out_mask_a |= SSL_aPSK;
+    *out_mask_k |= SSL_kPSK;
+  }
+}
+
 static int ssl_write_client_cipher_list(SSL *ssl, CBB *out,
                                         uint16_t min_version,
                                         uint16_t max_version) {
-  /* Prepare disabled cipher masks. */
-  ssl_set_client_disabled(ssl);
+  uint32_t mask_a, mask_k;
+  ssl_get_client_disabled(ssl, &mask_a, &mask_k);
 
   CBB child;
   if (!CBB_add_u16_length_prefixed(out, &child)) {
@@ -618,8 +642,8 @@
     for (size_t i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
       const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(ciphers, i);
       /* Skip disabled ciphers */
-      if ((cipher->algorithm_mkey & ssl->cert->mask_k) ||
-          (cipher->algorithm_auth & ssl->cert->mask_a)) {
+      if ((cipher->algorithm_mkey & mask_k) ||
+          (cipher->algorithm_auth & mask_a)) {
         continue;
       }
       if (SSL_CIPHER_get_min_version(cipher) > max_version ||
@@ -713,7 +737,7 @@
     goto err;
   }
 
-  return ssl->method->queue_message(ssl, msg, len);
+  return ssl->method->add_message(ssl, msg, len);
 
  err:
   CBB_cleanup(&cbb);
@@ -722,10 +746,6 @@
 
 static int ssl3_send_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CLNT_HELLO_B) {
-    return ssl->method->write_message(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)) {
@@ -779,8 +799,7 @@
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_CLNT_HELLO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int dtls1_get_hello_verify(SSL_HANDSHAKE *hs) {
@@ -789,7 +808,7 @@
   CBS hello_verify_request, cookie;
   uint16_t server_version;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -800,8 +819,10 @@
     return 1;
   }
 
-  CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
+  /* The handshake transcript is reset on HelloVerifyRequst, so do not bother
+   * hashing it. */
 
+  CBS_init(&hello_verify_request, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u16(&hello_verify_request, &server_version) ||
       !CBS_get_u8_length_prefixed(&hello_verify_request, &cookie) ||
       CBS_len(&hello_verify_request) != 0) {
@@ -828,13 +849,12 @@
 
 static int ssl3_get_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  CERT *ct = ssl->cert;
   int al = SSL_AD_INTERNAL_ERROR;
   CBS server_hello, server_random, session_id;
   uint16_t server_wire_version, cipher_suite;
   uint8_t compression_method;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     uint32_t err = ERR_peek_error();
     if (ERR_GET_LIB(err) == ERR_LIB_SSL &&
@@ -896,9 +916,7 @@
 
   ssl_clear_tls13_state(hs);
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_SERVER_HELLO) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
     return -1;
   }
 
@@ -945,7 +963,9 @@
   }
 
   /* The cipher must be allowed in the selected version and enabled. */
-  if ((c->algorithm_mkey & ct->mask_k) || (c->algorithm_auth & ct->mask_a) ||
+  uint32_t mask_a, mask_k;
+  ssl_get_client_disabled(ssl, &mask_a, &mask_k);
+  if ((c->algorithm_mkey & mask_k) || (c->algorithm_auth & mask_a) ||
       SSL_CIPHER_get_min_version(c) > ssl3_protocol_version(ssl) ||
       SSL_CIPHER_get_max_version(c) < ssl3_protocol_version(ssl) ||
       !sk_SSL_CIPHER_find(SSL_get_ciphers(ssl), NULL, c)) {
@@ -977,8 +997,10 @@
   }
   ssl->s3->tmp.new_cipher = c;
 
-  /* Now that the cipher is known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(ssl)) {
+  /* Now that the cipher is known, initialize the handshake hash and hash the
+   * ServerHello. */
+  if (!ssl3_init_handshake_hash(ssl) ||
+      !ssl_hash_current_message(ssl)) {
     goto f_err;
   }
 
@@ -1033,12 +1055,16 @@
 
 static int ssl3_get_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret =
-      ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS cbs;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
 
@@ -1077,7 +1103,7 @@
   CBS certificate_status, ocsp_response;
   uint8_t status_type;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -1089,6 +1115,10 @@
     return 1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS_init(&certificate_status, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8(&certificate_status, &status_type) ||
       status_type != TLSEXT_STATUSTYPE_ocsp ||
@@ -1130,7 +1160,7 @@
   EC_KEY *ecdh = NULL;
   EC_POINT *srvr_ecpoint = NULL;
 
-  int ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
@@ -1147,6 +1177,10 @@
     return 1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   /* Retain a copy of the original CBS to compute the signature over. */
   CBS server_key_exchange;
   CBS_init(&server_key_exchange, ssl->init_msg, ssl->init_num);
@@ -1360,7 +1394,7 @@
 
 static int ssl3_get_certificate_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int msg_ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
@@ -1373,9 +1407,8 @@
     return 1;
   }
 
-  if (ssl->s3->tmp.message_type != SSL3_MT_CERTIFICATE_REQUEST) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_REQUEST) ||
+      !ssl_hash_current_message(ssl)) {
     return -1;
   }
 
@@ -1428,12 +1461,16 @@
 
 static int ssl3_get_server_hello_done(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_SERVER_HELLO_DONE,
-                                         ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO_DONE) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   /* ServerHelloDone is empty. */
   if (ssl->init_num > 0) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
@@ -1446,11 +1483,6 @@
 
 static int ssl3_send_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CERT_B) {
-    return ssl->method->write_message(ssl);
-  }
-  assert(hs->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);
@@ -1469,9 +1501,12 @@
     /* Without a client certificate, the handshake buffer may be released. */
     ssl3_free_handshake_buffer(ssl);
 
+    /* In SSL 3.0, the Certificate message is replaced with a warning alert. */
     if (ssl->version == SSL3_VERSION) {
-      /* In SSL 3.0, send no certificate by skipping both messages. */
-      ssl3_send_alert(ssl, SSL3_AL_WARNING, SSL_AD_NO_CERTIFICATE);
+      if (!ssl->method->add_alert(ssl, SSL3_AL_WARNING,
+                                  SSL_AD_NO_CERTIFICATE)) {
+        return -1;
+      }
       return 1;
     }
   }
@@ -1480,8 +1515,7 @@
       !ssl3_output_cert_chain(ssl)) {
     return -1;
   }
-  hs->state = SSL3_ST_CW_CERT_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 OPENSSL_COMPILE_ASSERT(sizeof(size_t) >= sizeof(unsigned),
@@ -1489,11 +1523,6 @@
 
 static int ssl3_send_client_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_KEY_EXCH_B) {
-    return ssl->method->write_message(ssl);
-  }
-  assert(hs->state == SSL3_ST_CW_KEY_EXCH_A);
-
   uint8_t *pms = NULL;
   size_t pms_len = 0;
   CBB cbb, body;
@@ -1647,10 +1676,9 @@
 
   /* The message must be added to the finished hash before calculating the
    * master secret. */
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
-  hs->state = SSL3_ST_CW_KEY_EXCH_B;
 
   ssl->s3->new_session->master_key_length =
       tls1_generate_master_secret(ssl, ssl->s3->new_session->master_key, pms,
@@ -1663,7 +1691,7 @@
   OPENSSL_cleanse(pms, pms_len);
   OPENSSL_free(pms);
 
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1676,10 +1704,6 @@
 
 static int ssl3_send_cert_verify(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CERT_VRFY_C) {
-    return ssl->method->write_message(ssl);
-  }
-
   assert(ssl_has_private_key(ssl));
 
   CBB cbb, body, child;
@@ -1765,12 +1789,11 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->state = SSL3_ST_CW_CERT_VRFY_C;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1779,12 +1802,6 @@
 
 static int ssl3_send_next_proto(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_NEXT_PROTO_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_CW_NEXT_PROTO_A);
-
   static const uint8_t kZero[32] = {0};
   size_t padding_len = 32 - ((ssl->s3->next_proto_negotiated_len + 2) % 32);
 
@@ -1795,24 +1812,17 @@
                      ssl->s3->next_proto_negotiated_len) ||
       !CBB_add_u8_length_prefixed(&body, &child) ||
       !CBB_add_bytes(&child, kZero, padding_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_NEXT_PROTO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_CW_CHANNEL_ID_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_CW_CHANNEL_ID_A);
-
   if (!ssl_do_channel_id_callback(ssl)) {
     return -1;
   }
@@ -1825,24 +1835,27 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
       !tls1_write_channel_id(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_CW_CHANNEL_ID_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_get_new_session_ticket(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_NEW_SESSION_TICKET,
-                                         ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_NEW_SESSION_TICKET) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS new_session_ticket, ticket;
   uint32_t tlsext_tick_lifetime_hint;
   CBS_init(&new_session_ticket, ssl->init_msg, ssl->init_num);
@@ -1877,7 +1890,7 @@
   }
 
   /* |tlsext_tick_lifetime_hint| is measured from when the ticket was issued. */
-  ssl_session_refresh_time(ssl, session);
+  ssl_session_rebase_time(ssl, session);
 
   if (!CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
diff --git a/src/ssl/handshake_server.c b/src/ssl/handshake_server.c
index a4396f4..7b66cf2 100644
--- a/src/ssl/handshake_server.c
+++ b/src/ssl/handshake_server.c
@@ -185,6 +185,17 @@
 static int ssl3_get_channel_id(SSL_HANDSHAKE *hs);
 static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs);
 
+static struct CRYPTO_STATIC_MUTEX g_v2clienthello_lock =
+    CRYPTO_STATIC_MUTEX_INIT;
+static uint64_t g_v2clienthello_count = 0;
+
+uint64_t SSL_get_v2clienthello_count(void) {
+  CRYPTO_STATIC_MUTEX_lock_read(&g_v2clienthello_lock);
+  uint64_t ret = g_v2clienthello_count;
+  CRYPTO_STATIC_MUTEX_unlock_read(&g_v2clienthello_lock);
+  return ret;
+}
+
 int ssl3_accept(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint32_t alg_a;
@@ -206,13 +217,6 @@
       case SSL_ST_ACCEPT:
         ssl_do_info_callback(ssl, SSL_CB_HANDSHAKE_START, 1);
 
-        /* Enable a write buffer. This groups handshake messages within a flight
-         * into a single write. */
-        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;
@@ -226,7 +230,6 @@
       case SSL3_ST_SR_CLNT_HELLO_B:
       case SSL3_ST_SR_CLNT_HELLO_C:
       case SSL3_ST_SR_CLNT_HELLO_D:
-      case SSL3_ST_SR_CLNT_HELLO_E:
         ret = ssl3_get_client_hello(hs);
         if (hs->state == SSL_ST_TLS13) {
           break;
@@ -239,7 +242,6 @@
         break;
 
       case SSL3_ST_SW_SRVR_HELLO_A:
-      case SSL3_ST_SW_SRVR_HELLO_B:
         ret = ssl3_send_server_hello(hs);
         if (ret <= 0) {
           goto end;
@@ -252,7 +254,6 @@
         break;
 
       case SSL3_ST_SW_CERT_A:
-      case SSL3_ST_SW_CERT_B:
         if (ssl_cipher_uses_certificate_auth(ssl->s3->tmp.new_cipher)) {
           ret = ssl3_send_server_certificate(hs);
           if (ret <= 0) {
@@ -265,7 +266,6 @@
         break;
 
       case SSL3_ST_SW_CERT_STATUS_A:
-      case SSL3_ST_SW_CERT_STATUS_B:
         if (hs->certificate_status_expected) {
           ret = ssl3_send_certificate_status(hs);
           if (ret <= 0) {
@@ -279,7 +279,6 @@
 
       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;
 
         /* PSK ciphers send ServerKeyExchange if there is an identity hint. */
@@ -297,7 +296,6 @@
         break;
 
       case SSL3_ST_SW_CERT_REQ_A:
-      case SSL3_ST_SW_CERT_REQ_B:
         if (hs->cert_request) {
           ret = ssl3_send_certificate_request(hs);
           if (ret <= 0) {
@@ -310,7 +308,6 @@
         break;
 
       case SSL3_ST_SW_SRVR_DONE_A:
-      case SSL3_ST_SW_SRVR_DONE_B:
         ret = ssl3_send_server_hello_done(hs);
         if (ret <= 0) {
           goto end;
@@ -410,7 +407,6 @@
         break;
 
       case SSL3_ST_SW_SESSION_TICKET_A:
-      case SSL3_ST_SW_SESSION_TICKET_B:
         if (hs->ticket_expected) {
           ret = ssl3_send_new_session_ticket(hs);
           if (ret <= 0) {
@@ -423,22 +419,17 @@
         break;
 
       case SSL3_ST_SW_CHANGE:
-        ret = ssl->method->send_change_cipher_spec(ssl);
-        if (ret <= 0) {
-          goto end;
-        }
-        hs->state = SSL3_ST_SW_FINISHED_A;
-
-        if (!tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
+        if (!ssl->method->add_change_cipher_spec(ssl) ||
+            !tls1_change_cipher_state(hs, SSL3_CHANGE_CIPHER_SERVER_WRITE)) {
           ret = -1;
           goto end;
         }
+
+        hs->state = SSL3_ST_SW_FINISHED_A;
         break;
 
       case SSL3_ST_SW_FINISHED_A:
-      case SSL3_ST_SW_FINISHED_B:
-        ret = ssl3_send_finished(hs, SSL3_ST_SW_FINISHED_A,
-                                 SSL3_ST_SW_FINISHED_B);
+        ret = ssl3_send_finished(hs);
         if (ret <= 0) {
           goto end;
         }
@@ -493,8 +484,11 @@
           ssl->s3->new_session = NULL;
         }
 
-        /* remove buffering on output */
-        ssl_free_wbio_buffer(ssl);
+        if (hs->v2_clienthello) {
+          CRYPTO_STATIC_MUTEX_lock_write(&g_v2clienthello_lock);
+          g_v2clienthello_count++;
+          CRYPTO_STATIC_MUTEX_unlock_write(&g_v2clienthello_lock);
+        }
 
         ssl->s3->initial_handshake_complete = 1;
         ssl_update_cache(hs, SSL_SESS_CACHE_SERVER);
@@ -546,6 +540,7 @@
 static int negotiate_version(SSL_HANDSHAKE *hs, uint8_t *out_alert,
                              const SSL_CLIENT_HELLO *client_hello) {
   SSL *const ssl = hs->ssl;
+  assert(!ssl->s3->have_version);
   uint16_t min_version, max_version;
   if (!ssl_get_version_range(ssl, &min_version, &max_version)) {
     *out_alert = SSL_AD_PROTOCOL_VERSION;
@@ -703,7 +698,7 @@
   uint32_t mask_k = 0;
   uint32_t mask_a = 0;
 
-  if (ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl)) {
+  if (ssl_has_certificate(ssl)) {
     int type = ssl_private_key_type(ssl);
     if (type == NID_rsaEncryption) {
       mask_k |= SSL_kRSA;
@@ -820,12 +815,15 @@
 
   if (hs->state == SSL3_ST_SR_CLNT_HELLO_A) {
     /* The first time around, read the ClientHello. */
-    int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_HELLO,
-                                               ssl_hash_message);
+    int msg_ret = ssl->method->ssl_get_message(ssl);
     if (msg_ret <= 0) {
       return msg_ret;
     }
 
+    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+      return -1;
+    }
+
     hs->state = SSL3_ST_SR_CLNT_HELLO_B;
   }
 
@@ -855,23 +853,11 @@
           /* fallthrough */;
       }
     }
-    hs->state = SSL3_ST_SR_CLNT_HELLO_C;
-  }
 
-  /* Negotiate the protocol version if we have not done so yet. */
-  if (!ssl->s3->have_version) {
     if (!negotiate_version(hs, &al, &client_hello)) {
       goto f_err;
     }
 
-    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
-      hs->state = SSL_ST_TLS13;
-      hs->do_tls13_handshake = tls13_server_handshake;
-      return 1;
-    }
-  }
-
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_C) {
     /* Load the client random. */
     if (client_hello.random_len != SSL3_RANDOM_SIZE) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -880,11 +866,14 @@
     OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
                    client_hello.random_len);
 
-    /* Only null compression is supported. */
+    /* Only null compression is supported. TLS 1.3 further requires the peer
+     * advertise no other compression. */
     if (OPENSSL_memchr(client_hello.compression_methods, 0,
-                       client_hello.compression_methods_len) == NULL) {
+                       client_hello.compression_methods_len) == NULL ||
+        (ssl3_protocol_version(ssl) >= TLS1_3_VERSION &&
+         client_hello.compression_methods_len != 1)) {
       al = SSL_AD_ILLEGAL_PARAMETER;
-      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_COMPRESSION_SPECIFIED);
+      OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
       goto f_err;
     }
 
@@ -894,10 +883,10 @@
       goto err;
     }
 
-    hs->state = SSL3_ST_SR_CLNT_HELLO_D;
+    hs->state = SSL3_ST_SR_CLNT_HELLO_C;
   }
 
-  if (hs->state == SSL3_ST_SR_CLNT_HELLO_D) {
+  if (hs->state == SSL3_ST_SR_CLNT_HELLO_C) {
     /* Call |cert_cb| to update server certificates if required. */
     if (ssl->cert->cert_cb != NULL) {
       int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
@@ -916,6 +905,13 @@
       goto err;
     }
 
+    if (ssl3_protocol_version(ssl) >= TLS1_3_VERSION) {
+      /* Jump to the TLS 1.3 state machine. */
+      hs->state = SSL_ST_TLS13;
+      hs->do_tls13_handshake = tls13_server_handshake;
+      return 1;
+    }
+
     /* Negotiate the cipher suite. This must be done after |cert_cb| so the
      * certificate is finalized. */
     ssl->s3->tmp.new_cipher =
@@ -926,10 +922,10 @@
       goto f_err;
     }
 
-    hs->state = SSL3_ST_SR_CLNT_HELLO_E;
+    hs->state = SSL3_ST_SR_CLNT_HELLO_D;
   }
 
-  assert(hs->state == SSL3_ST_SR_CLNT_HELLO_E);
+  assert(hs->state == SSL3_ST_SR_CLNT_HELLO_D);
 
   /* Determine whether we are doing session resumption. */
   int tickets_supported = 0, renew_ticket = 0;
@@ -1028,8 +1024,10 @@
     goto f_err;
   }
 
-  /* Now that all parameters are known, initialize the handshake hash. */
-  if (!ssl3_init_handshake_hash(ssl)) {
+  /* Now that all parameters are known, initialize the handshake hash and hash
+   * the ClientHello. */
+  if (!ssl3_init_handshake_hash(ssl) ||
+      !ssl_hash_current_message(ssl)) {
     goto f_err;
   }
 
@@ -1052,11 +1050,6 @@
 
 static int ssl3_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SRVR_HELLO_B) {
-    return ssl->method->write_message(ssl);
-  }
-
-  assert(hs->state == SSL3_ST_SW_SRVR_HELLO_A);
 
   /* We only accept ChannelIDs on connections with ECDHE in order to avoid a
    * known attack while we fix ChannelID itself. */
@@ -1101,40 +1094,30 @@
       !CBB_add_u16(&body, ssl_cipher_get_value(ssl->s3->tmp.new_cipher)) ||
       !CBB_add_u8(&body, 0 /* no compression */) ||
       !ssl_add_serverhello_tlsext(hs, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SRVR_HELLO_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   if (!ssl_has_certificate(ssl)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return 0;
+    return -1;
   }
 
   if (!ssl3_output_cert_chain(ssl)) {
-    return 0;
+    return -1;
   }
-  hs->state = SSL3_ST_SW_CERT_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_certificate_status(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_STATUS_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body, ocsp_response;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_CERTIFICATE_STATUS) ||
@@ -1142,22 +1125,17 @@
       !CBB_add_u24_length_prefixed(&body, &ocsp_response) ||
       !CBB_add_bytes(&ocsp_response, CRYPTO_BUFFER_data(ssl->ocsp_response),
                      CRYPTO_BUFFER_len(ssl->ocsp_response)) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_CERT_STATUS_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_send_server_key_exchange(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_KEY_EXCH_C) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, child;
   CBB_zero(&cbb);
 
@@ -1317,7 +1295,7 @@
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -1325,8 +1303,7 @@
   hs->server_params = NULL;
   hs->server_params_len = 0;
 
-  hs->state = SSL3_ST_SW_KEY_EXCH_C;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   CBB_cleanup(&cbb);
@@ -1373,10 +1350,6 @@
 
 static int ssl3_send_certificate_request(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_CERT_REQ_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body, cert_types, sigalgs_cbb;
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_CERTIFICATE_REQUEST) ||
@@ -1400,12 +1373,11 @@
   }
 
   if (!ssl_add_client_CA_list(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->state = SSL3_ST_SW_CERT_REQ_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 
 err:
   OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
@@ -1415,27 +1387,22 @@
 
 static int ssl3_send_server_hello_done(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SRVR_DONE_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO_DONE) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SRVR_DONE_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 static int ssl3_get_client_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   assert(hs->cert_request);
 
-  int msg_ret = ssl->method->ssl_get_message(ssl, -1, ssl_hash_message);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
@@ -1463,6 +1430,10 @@
     return -1;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS certificate_msg;
   CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
 
@@ -1539,11 +1510,15 @@
   uint8_t psk[PSK_MAX_PSK_LEN];
 
   if (hs->state == SSL3_ST_SR_KEY_EXCH_A) {
-    int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE,
-                                           ssl_hash_message);
+    int ret = ssl->method->ssl_get_message(ssl);
     if (ret <= 0) {
       return ret;
     }
+
+    if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_KEY_EXCHANGE) ||
+        !ssl_hash_current_message(ssl)) {
+      return -1;
+    }
   }
 
   CBS_init(&client_key_exchange, ssl->init_msg, ssl->init_num);
@@ -1807,12 +1782,15 @@
     return 1;
   }
 
-  int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CERTIFICATE_VERIFY,
-                                             ssl_dont_hash_message);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY)) {
+    return -1;
+  }
+
   CBS_init(&certificate_verify, ssl->init_msg, ssl->init_num);
 
   /* Determine the digest type if needbe. */
@@ -1900,12 +1878,16 @@
  * sets the next_proto member in s if found */
 static int ssl3_get_next_proto(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret =
-      ssl->method->ssl_get_message(ssl, SSL3_MT_NEXT_PROTO, ssl_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_NEXT_PROTO) ||
+      !ssl_hash_current_message(ssl)) {
+    return -1;
+  }
+
   CBS next_protocol, selected_protocol, padding;
   CBS_init(&next_protocol, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u8_length_prefixed(&next_protocol, &selected_protocol) ||
@@ -1927,13 +1909,13 @@
 /* ssl3_get_channel_id reads and verifies a ClientID handshake message. */
 static int ssl3_get_channel_id(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int msg_ret = ssl->method->ssl_get_message(ssl, SSL3_MT_CHANNEL_ID,
-                                             ssl_dont_hash_message);
+  int msg_ret = ssl->method->ssl_get_message(ssl);
   if (msg_ret <= 0) {
     return msg_ret;
   }
 
-  if (!tls1_verify_channel_id(ssl) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
+      !tls1_verify_channel_id(ssl) ||
       !ssl_hash_current_message(ssl)) {
     return -1;
   }
@@ -1942,15 +1924,11 @@
 
 static int ssl3_send_new_session_ticket(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == SSL3_ST_SW_SESSION_TICKET_B) {
-    return ssl->method->write_message(ssl);
-  }
-
   const SSL_SESSION *session;
   SSL_SESSION *session_copy = NULL;
   if (ssl->session == NULL) {
     /* Fix the timeout to measure from the ticket issuance time. */
-    ssl_session_refresh_time(ssl, ssl->s3->new_session);
+    ssl_session_rebase_time(ssl, ssl->s3->new_session);
     session = ssl->s3->new_session;
   } else {
     /* We are renewing an existing session. Duplicate the session to adjust the
@@ -1960,7 +1938,7 @@
       return -1;
     }
 
-    ssl_session_refresh_time(ssl, session_copy);
+    ssl_session_rebase_time(ssl, session_copy);
     session = session_copy;
   }
 
@@ -1970,7 +1948,7 @@
       CBB_add_u32(&body, session->timeout) &&
       CBB_add_u16_length_prefixed(&body, &ticket) &&
       ssl_encrypt_ticket(ssl, &ticket, session) &&
-      ssl_complete_message(ssl, &cbb);
+      ssl_add_message_cbb(ssl, &cbb);
 
   SSL_SESSION_free(session_copy);
   CBB_cleanup(&cbb);
@@ -1979,6 +1957,5 @@
     return -1;
   }
 
-  hs->state = SSL3_ST_SW_SESSION_TICKET_B;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index a320e72..544259c 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -273,22 +273,22 @@
   uint8_t fixed_nonce_len, variable_nonce_len;
   /* variable_nonce_included_in_record is non-zero if the variable nonce
    * for a record is included as a prefix before the ciphertext. */
-  char variable_nonce_included_in_record;
+  unsigned variable_nonce_included_in_record : 1;
   /* random_variable_nonce is non-zero if the variable nonce is
    * randomly generated, rather than derived from the sequence
    * number. */
-  char random_variable_nonce;
+  unsigned random_variable_nonce : 1;
   /* omit_length_in_ad is non-zero if the length should be omitted in the
    * AEAD's ad parameter. */
-  char omit_length_in_ad;
+  unsigned omit_length_in_ad : 1;
   /* omit_version_in_ad is non-zero if the version should be omitted
    * in the AEAD's ad parameter. */
-  char omit_version_in_ad;
+  unsigned omit_version_in_ad : 1;
   /* omit_ad is non-zero if the AEAD's ad parameter should be omitted. */
-  char omit_ad;
+  unsigned omit_ad : 1;
   /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
    * variable nonce rather than prepended. */
-  char xor_fixed_nonce;
+  unsigned xor_fixed_nonce : 1;
 } SSL_AEAD_CTX;
 
 /* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
@@ -433,6 +433,10 @@
   dtls1_use_current_epoch,
 };
 
+/* dtls_max_seal_overhead returns the maximum overhead, in bytes, of sealing a
+ * record. */
+size_t dtls_max_seal_overhead(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
+
 /* dtls_seal_prefix_len returns the number of bytes of prefix to reserve in
  * front of the plaintext when sealing a record in-place. */
 size_t dtls_seal_prefix_len(const SSL *ssl, enum dtls1_use_epoch_t use_epoch);
@@ -769,10 +773,6 @@
                                               CBS *cbs,
                                               CRYPTO_BUFFER_POOL *pool);
 
-/* ssl_add_cert_to_cbb adds |x509| to |cbb|. It returns one on success and zero
- * on error. */
-int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509);
-
 /* ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used
  * by a TLS Certificate message. If there is no certificate chain, it emits an
  * empty certificate list. It returns one on success and zero on error. */
@@ -884,7 +884,6 @@
   ssl_hs_error,
   ssl_hs_ok,
   ssl_hs_read_message,
-  ssl_hs_write_message,
   ssl_hs_flush,
   ssl_hs_flush_and_read_message,
   ssl_hs_x509_lookup,
@@ -1053,6 +1052,9 @@
    * or received. */
   unsigned ticket_expected:1;
 
+  /* v2_clienthello is one if we received a V2ClientHello. */
+  unsigned v2_clienthello:1;
+
   /* client_version is the value sent or received in the ClientHello version. */
   uint16_t client_version;
 } /* SSL_HANDSHAKE */;
@@ -1062,6 +1064,10 @@
 /* ssl_handshake_free releases all memory associated with |hs|. */
 void ssl_handshake_free(SSL_HANDSHAKE *hs);
 
+/* ssl_check_message_type checks if the current message has type |type|. If so
+ * it returns one. Otherwise, it sends an alert and returns zero. */
+int ssl_check_message_type(SSL *ssl, int type);
+
 /* tls13_handshake runs the TLS 1.3 handshake. It returns one on success and <=
  * 0 on error. */
 int tls13_handshake(SSL_HANDSHAKE *hs);
@@ -1075,18 +1081,14 @@
  * success and zero on failure. */
 int tls13_post_handshake(SSL *ssl);
 
-/* tls13_check_message_type checks if the current message has type |type|. If so
- * it returns one. Otherwise, it sends an alert and returns zero. */
-int tls13_check_message_type(SSL *ssl, int type);
-
 int tls13_process_certificate(SSL_HANDSHAKE *hs, int allow_anonymous);
 int tls13_process_certificate_verify(SSL_HANDSHAKE *hs);
 int tls13_process_finished(SSL_HANDSHAKE *hs);
 
-int tls13_prepare_certificate(SSL_HANDSHAKE *hs);
-enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL_HANDSHAKE *hs, int is_first_run);
-int tls13_prepare_finished(SSL_HANDSHAKE *hs);
+int tls13_add_certificate(SSL_HANDSHAKE *hs);
+enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs,
+                                                           int is_first_run);
+int tls13_add_finished(SSL_HANDSHAKE *hs);
 int tls13_process_new_session_ticket(SSL *ssl);
 
 int ssl_ext_key_share_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t **out_secret,
@@ -1222,28 +1224,36 @@
 /* From RFC4492, used in encoding the curve type in ECParameters */
 #define NAMED_CURVE_TYPE 3
 
-enum ssl_hash_message_t {
-  ssl_dont_hash_message,
-  ssl_hash_message,
-};
-
 typedef struct cert_st {
   EVP_PKEY *privatekey;
-  X509 *x509_leaf;
+
+  /* chain contains the certificate chain, with the leaf at the beginning. The
+   * first element of |chain| may be NULL to indicate that the leaf certificate
+   * has not yet been set.
+   *   If |chain| != NULL -> len(chain) >= 1
+   *   If |chain[0]| == NULL -> len(chain) >= 2.
+   *   |chain[1..]| != NULL */
+  STACK_OF(CRYPTO_BUFFER) *chain;
+
+  /* x509_chain may contain a parsed copy of |chain[1..]|. This is only used as
+   * a cache in order to implement “get0” functions that return a non-owning
+   * pointer to the certificate chain. */
   STACK_OF(X509) *x509_chain;
 
+  /* x509_leaf may contain a parsed copy of the first element of |chain|. This
+   * is only used as a cache in order to implement “get0” functions that return
+   * a non-owning pointer to the certificate chain. */
+  X509 *x509_leaf;
+
+  /* x509_stash contains the last |X509| object append to the chain. This is a
+   * workaround for some third-party code that continue to use an |X509| object
+   * even after passing ownership with an “add0” function. */
+  X509 *x509_stash;
+
   /* key_method, if non-NULL, is a set of callbacks to call for private key
    * operations. */
   const SSL_PRIVATE_KEY_METHOD *key_method;
 
-  /* For clients the following masks are of *disabled* key and auth algorithms
-   * based on the current configuration.
-   *
-   * TODO(davidben): Remove these. They get checked twice: when sending the
-   * ClientHello and when processing the ServerHello. */
-  uint32_t mask_k;
-  uint32_t mask_a;
-
   DH *dh_tmp;
   DH *(*dh_tmp_cb)(SSL *ssl, int is_export, int keysize);
 
@@ -1294,12 +1304,10 @@
   uint16_t (*version_to_wire)(uint16_t version);
   int (*ssl_new)(SSL *ssl);
   void (*ssl_free)(SSL *ssl);
-  /* ssl_get_message reads the next handshake message. If |msg_type| is not -1,
-   * the message must have the specified type. On success, it returns one and
-   * sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and |ssl->init_num|.
-   * Otherwise, it returns <= 0. */
-  int (*ssl_get_message)(SSL *ssl, int msg_type,
-                         enum ssl_hash_message_t hash_message);
+  /* ssl_get_message reads the next handshake message. On success, it returns
+   * one and sets |ssl->s3->tmp.message_type|, |ssl->init_msg|, and
+   * |ssl->init_num|. Otherwise, it returns <= 0. */
+  int (*ssl_get_message)(SSL *ssl);
   /* get_current_message sets |*out| to the current handshake message. This
    * includes the protocol-specific message header. */
   void (*get_current_message)(const SSL *ssl, CBS *out);
@@ -1311,11 +1319,11 @@
    * and sets |*out_got_handshake| to whether the failure was due to a
    * post-handshake handshake message. If so, it fills in the current message as
    * in |ssl_get_message|. */
-  int (*read_app_data)(SSL *ssl, int *out_got_handshake,  uint8_t *buf, int len,
+  int (*read_app_data)(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
   int (*read_change_cipher_spec)(SSL *ssl);
   void (*read_close_notify)(SSL *ssl);
-  int (*write_app_data)(SSL *ssl, const void *buf_, int len);
+  int (*write_app_data)(SSL *ssl, const uint8_t *buf, int len);
   int (*dispatch_alert)(SSL *ssl);
   /* supports_cipher returns one if |cipher| is supported by this protocol and
    * zero otherwise. */
@@ -1329,16 +1337,17 @@
    * release it with |OPENSSL_free| when done. It returns one on success and
    * zero on error. */
   int (*finish_message)(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
-  /* queue_message queues a handshake message and prepares it to be written. It
-   * takes ownership of |msg| and releases it with |OPENSSL_free| when done. It
-   * returns one on success and zero on error. */
-  int (*queue_message)(SSL *ssl, uint8_t *msg, size_t len);
-  /* write_message writes the next message to the transport. It returns one on
-   * success and <= 0 on error. */
-  int (*write_message)(SSL *ssl);
-  /* send_change_cipher_spec sends a ChangeCipherSpec message. */
-  int (*send_change_cipher_spec)(SSL *ssl);
-  /* flush_flight flushes the current flight to the transport. It returns one on
+  /* add_message adds a handshake message to the pending flight. It returns one
+   * on success and zero on error. In either case, it takes ownership of |msg|
+   * and releases it with |OPENSSL_free| when done. */
+  int (*add_message)(SSL *ssl, uint8_t *msg, size_t len);
+  /* add_change_cipher_spec adds a ChangeCipherSpec record to the pending
+   * flight. It returns one on success and zero on error. */
+  int (*add_change_cipher_spec)(SSL *ssl);
+  /* add_alert adds an alert to the pending flight. It returns one on success
+   * and zero on error. */
+  int (*add_alert)(SSL *ssl, uint8_t level, uint8_t desc);
+  /* flush_flight flushes the pending flight to the transport. It returns one on
    * success and <= 0 on error. */
   int (*flush_flight)(SSL *ssl);
   /* expect_flight is called when the handshake expects a flight of messages from
@@ -1466,6 +1475,10 @@
    * and future messages should use the record layer. */
   unsigned v2_hello_done:1;
 
+  /* is_v2_hello is true if the current handshake message was derived from a
+   * V2ClientHello rather than received from the peer directly. */
+  unsigned is_v2_hello:1;
+
   /* initial_handshake_complete is true if the initial handshake has
    * completed. */
   unsigned initial_handshake_complete:1;
@@ -1487,9 +1500,13 @@
 
   uint8_t send_alert[2];
 
-  /* pending_message is the current outgoing handshake message. */
-  uint8_t *pending_message;
-  uint32_t pending_message_len;
+  /* pending_flight is the pending outgoing flight. This is used to flush each
+   * handshake flight in a single write. */
+  BUF_MEM *pending_flight;
+
+  /* pending_flight_offset is the number of bytes of |pending_flight| which have
+   * been successfully written. */
+  uint32_t pending_flight_offset;
 
   /* aead_read_ctx is the current read cipher state. */
   SSL_AEAD_CTX *aead_read_ctx;
@@ -1645,6 +1662,13 @@
   DTLS_OUTGOING_MESSAGE outgoing_messages[SSL_MAX_HANDSHAKE_FLIGHT];
   uint8_t outgoing_messages_len;
 
+  /* outgoing_written is the number of outgoing messages that have been
+   * written. */
+  uint8_t outgoing_written;
+  /* outgoing_offset is the number of bytes of the next outgoing message have
+   * been written. */
+  uint32_t outgoing_offset;
+
   unsigned int mtu; /* max DTLS packet size */
 
   /* num_timeouts is the number of times the retransmit timer has fired since
@@ -1675,6 +1699,15 @@
 CERT *ssl_cert_dup(CERT *cert);
 void ssl_cert_clear_certs(CERT *c);
 void ssl_cert_free(CERT *c);
+CRYPTO_BUFFER *x509_to_buffer(X509 *x509);
+void ssl_cert_flush_cached_x509_leaf(CERT *cert);
+int ssl_cert_cache_leaf_cert(CERT *cert);
+/* ssl_compare_public_and_private_key returns one if |pubkey| is the public
+ * counterpart to |privkey|. Otherwise it returns zero and pushes a helpful
+ * message on the error queue. */
+int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+                                       const EVP_PKEY *privkey);
+int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey);
 int ssl_get_new_session(SSL_HANDSHAKE *hs, int is_server);
 int ssl_encrypt_ticket(SSL *ssl, CBB *out, const SSL_SESSION *session);
 
@@ -1720,9 +1753,14 @@
 OPENSSL_EXPORT SSL_SESSION *SSL_SESSION_dup(SSL_SESSION *session,
                                             int dup_flags);
 
-/* ssl_session_refresh_time updates |session|'s start time to the current time,
+/* ssl_session_rebase_time updates |session|'s start time to the current time,
  * adjusting the timeout so the expiration time is unchanged. */
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session);
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session);
+
+/* ssl_session_renew_timeout calls |ssl_session_rebase_time| and renews
+ * |session|'s timeout to |timeout| (measured from the current time). The
+ * renewal is clamped to the session's auth_timeout. */
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout);
 
 void ssl_cipher_preference_list_free(
     struct ssl_cipher_preference_list_st *cipher_list);
@@ -1739,10 +1777,8 @@
 int ssl_verify_alarm_type(long type);
 
 int ssl3_get_finished(SSL_HANDSHAKE *hs);
-int ssl3_send_change_cipher_spec(SSL *ssl);
 int ssl3_send_alert(SSL *ssl, int level, int desc);
-int ssl3_get_message(SSL *ssl, int msg_type,
-                     enum ssl_hash_message_t hash_message);
+int ssl3_get_message(SSL *ssl);
 void ssl3_get_current_message(const SSL *ssl, CBS *out);
 void ssl3_release_current_message(SSL *ssl, int free_buffer);
 
@@ -1753,15 +1789,14 @@
 int ssl3_cert_verify_hash(SSL *ssl, const EVP_MD **out_md, uint8_t *out,
                           size_t *out_len, uint16_t signature_algorithm);
 
-int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b);
+int ssl3_send_finished(SSL_HANDSHAKE *hs);
 int ssl3_dispatch_alert(SSL *ssl);
 int ssl3_read_app_data(SSL *ssl, int *out_got_handshake, uint8_t *buf, int len,
                        int peek);
 int ssl3_read_change_cipher_spec(SSL *ssl);
 void ssl3_read_close_notify(SSL *ssl);
 int ssl3_read_handshake_bytes(SSL *ssl, uint8_t *buf, int len);
-int ssl3_write_app_data(SSL *ssl, const void *buf, int len);
-int ssl3_write_bytes(SSL *ssl, int type, const void *buf, int len);
+int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len);
 int ssl3_output_cert_chain(SSL *ssl);
 
 int ssl3_new(SSL *ssl);
@@ -1771,18 +1806,22 @@
 
 int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
 int ssl3_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg, size_t *out_len);
-int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len);
-int ssl3_write_message(SSL *ssl);
+int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len);
+int ssl3_add_change_cipher_spec(SSL *ssl);
+int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
+int ssl3_flush_flight(SSL *ssl);
 
 int dtls1_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type);
 int dtls1_finish_message(SSL *ssl, CBB *cbb, uint8_t **out_msg,
                          size_t *out_len);
-int dtls1_queue_message(SSL *ssl, uint8_t *msg, size_t len);
-int dtls1_write_message(SSL *ssl);
+int dtls1_add_message(SSL *ssl, uint8_t *msg, size_t len);
+int dtls1_add_change_cipher_spec(SSL *ssl);
+int dtls1_add_alert(SSL *ssl, uint8_t level, uint8_t desc);
+int dtls1_flush_flight(SSL *ssl);
 
-/* ssl_complete_message calls |finish_message| and |queue_message| on |cbb| to
- * queue the message for writing. */
-int ssl_complete_message(SSL *ssl, CBB *cbb);
+/* ssl_add_message_cbb finishes the handshake message in |cbb| and adds it to
+ * the pending flight. It returns one on success and zero on error. */
+int ssl_add_message_cbb(SSL *ssl, CBB *cbb);
 
 /* ssl_hash_current_message incorporates the current handshake message into the
  * handshake hash. It returns one on success and zero on allocation failure. */
@@ -1798,14 +1837,13 @@
 int dtls1_read_change_cipher_spec(SSL *ssl);
 void dtls1_read_close_notify(SSL *ssl);
 
-int dtls1_write_app_data(SSL *ssl, const void *buf, int len);
+int dtls1_write_app_data(SSL *ssl, const uint8_t *buf, int len);
 
 /* 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 dtls1_send_finished(SSL *ssl, int a, int b, const char *sender, int slen);
 int dtls1_retransmit_outgoing_messages(SSL *ssl);
 void dtls1_clear_record_buffer(SSL *ssl);
@@ -1825,18 +1863,11 @@
 int dtls1_connect(SSL *ssl);
 void dtls1_free(SSL *ssl);
 
-int dtls1_get_message(SSL *ssl, int mt, enum ssl_hash_message_t hash_message);
+int dtls1_get_message(SSL *ssl);
 void dtls1_get_current_message(const SSL *ssl, CBS *out);
 void dtls1_release_current_message(SSL *ssl, int free_buffer);
 int dtls1_dispatch_alert(SSL *ssl);
 
-/* ssl_is_wbio_buffered returns one if |ssl|'s write BIO is buffered and zero
- * otherwise. */
-int ssl_is_wbio_buffered(const SSL *ssl);
-
-int ssl_init_wbio_buffer(SSL *ssl);
-void ssl_free_wbio_buffer(SSL *ssl);
-
 int tls1_change_cipher_state(SSL_HANDSHAKE *hs, int which);
 int tls1_handshake_digest(SSL *ssl, uint8_t *out, size_t out_len);
 int tls1_generate_master_secret(SSL *ssl, uint8_t *out, const uint8_t *premaster,
@@ -1935,8 +1966,6 @@
 
 uint32_t ssl_get_algorithm_prf(const SSL *ssl);
 
-void ssl_set_client_disabled(SSL *ssl);
-
 void ssl_get_current_time(const SSL *ssl, struct timeval *out_clock);
 
 /* ssl_reset_error_state resets state for |SSL_get_error|. */
diff --git a/src/ssl/s3_both.c b/src/ssl/s3_both.c
index 492884f..8d2657f 100644
--- a/src/ssl/s3_both.c
+++ b/src/ssl/s3_both.c
@@ -180,33 +180,56 @@
   OPENSSL_free(hs);
 }
 
-/* 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 success
- * and <= 0 on error. */
-static int ssl3_do_write(SSL *ssl, int type, const uint8_t *data, size_t len) {
-  int ret = ssl3_write_bytes(ssl, type, data, len);
-  if (ret <= 0) {
-    return ret;
+int ssl_check_message_type(SSL *ssl, int type) {
+  if (ssl->s3->tmp.message_type != type) {
+    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
+    ERR_add_error_dataf("got type %d, wanted type %d",
+                        ssl->s3->tmp.message_type, type);
+    return 0;
   }
 
-  /* ssl3_write_bytes writes the data in its entirety. */
-  assert((size_t)ret == len);
-  ssl_do_msg_callback(ssl, 1 /* write */, type, data, len);
+  return 1;
+}
+
+static int add_record_to_flight(SSL *ssl, uint8_t type, const uint8_t *in,
+                                size_t in_len) {
+  /* We'll never add a flight while in the process of writing it out. */
+  assert(ssl->s3->pending_flight_offset == 0);
+
+  if (ssl->s3->pending_flight == NULL) {
+    ssl->s3->pending_flight = BUF_MEM_new();
+    if (ssl->s3->pending_flight == NULL) {
+      return 0;
+    }
+  }
+
+  size_t max_out = in_len + SSL_max_seal_overhead(ssl);
+  size_t new_cap = ssl->s3->pending_flight->length + max_out;
+  if (max_out < in_len || new_cap < max_out) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+    return 0;
+  }
+
+  size_t len;
+  if (!BUF_MEM_reserve(ssl->s3->pending_flight, new_cap) ||
+      !tls_seal_record(ssl, (uint8_t *)ssl->s3->pending_flight->data +
+                                ssl->s3->pending_flight->length,
+                       &len, max_out, type, in, in_len)) {
+    return 0;
+  }
+
+  ssl->s3->pending_flight->length += len;
   return 1;
 }
 
 int ssl3_init_message(SSL *ssl, CBB *cbb, CBB *body, uint8_t type) {
-  CBB_zero(cbb);
-  if (ssl->s3->pending_message != NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
-  }
-
   /* Pick a modest size hint to save most of the |realloc| calls. */
   if (!CBB_init(cbb, 64) ||
       !CBB_add_u8(cbb, type) ||
       !CBB_add_u24_length_prefixed(cbb, body)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    CBB_cleanup(cbb);
     return 0;
   }
 
@@ -223,56 +246,113 @@
   return 1;
 }
 
-int ssl3_queue_message(SSL *ssl, uint8_t *msg, size_t len) {
-  if (ssl->s3->pending_message != NULL ||
-      len > 0xffffffffu) {
-    OPENSSL_free(msg);
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+int ssl3_add_message(SSL *ssl, uint8_t *msg, size_t len) {
+  /* Add the message to the current flight, splitting into several records if
+   * needed. */
+  int ret = 0;
+  size_t added = 0;
+  do {
+    size_t todo = len - added;
+    if (todo > ssl->max_send_fragment) {
+      todo = ssl->max_send_fragment;
+    }
+
+    if (!add_record_to_flight(ssl, SSL3_RT_HANDSHAKE, msg + added, todo)) {
+      goto err;
+    }
+    added += todo;
+  } while (added < len);
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_HANDSHAKE, msg, len);
+  ssl3_update_handshake_hash(ssl, msg, len);
+  ret = 1;
+
+err:
+  OPENSSL_free(msg);
+  return ret;
+}
+
+int ssl3_add_change_cipher_spec(SSL *ssl) {
+  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
+
+  if (!add_record_to_flight(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
+                            sizeof(kChangeCipherSpec))) {
     return 0;
   }
 
-  ssl3_update_handshake_hash(ssl, msg, len);
-
-  ssl->s3->pending_message = msg;
-  ssl->s3->pending_message_len = (uint32_t)len;
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_CHANGE_CIPHER_SPEC,
+                      kChangeCipherSpec, sizeof(kChangeCipherSpec));
   return 1;
 }
 
-int ssl_complete_message(SSL *ssl, CBB *cbb) {
+int ssl3_add_alert(SSL *ssl, uint8_t level, uint8_t desc) {
+  uint8_t alert[2] = {level, desc};
+  if (!add_record_to_flight(ssl, SSL3_RT_ALERT, alert, sizeof(alert))) {
+    return 0;
+  }
+
+  ssl_do_msg_callback(ssl, 1 /* write */, SSL3_RT_ALERT, alert, sizeof(alert));
+  ssl_do_info_callback(ssl, SSL_CB_WRITE_ALERT, ((int)level << 8) | desc);
+  return 1;
+}
+
+int ssl_add_message_cbb(SSL *ssl, CBB *cbb) {
   uint8_t *msg;
   size_t len;
   if (!ssl->method->finish_message(ssl, cbb, &msg, &len) ||
-      !ssl->method->queue_message(ssl, msg, len)) {
+      !ssl->method->add_message(ssl, msg, len)) {
     return 0;
   }
 
   return 1;
 }
 
-int ssl3_write_message(SSL *ssl) {
-  if (ssl->s3->pending_message == NULL) {
+int ssl3_flush_flight(SSL *ssl) {
+  if (ssl->s3->pending_flight == NULL) {
+    return 1;
+  }
+
+  if (ssl->s3->pending_flight->length > 0xffffffff ||
+      ssl->s3->pending_flight->length > INT_MAX) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+    return -1;
   }
 
-  int ret = ssl3_do_write(ssl, SSL3_RT_HANDSHAKE, ssl->s3->pending_message,
-                          ssl->s3->pending_message_len);
-  if (ret <= 0) {
-    return ret;
+  /* The handshake flight buffer is mutually exclusive with application data.
+   *
+   * TODO(davidben): This will not be true when closure alerts use this. */
+  if (ssl_write_buffer_is_pending(ssl)) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
 
-  OPENSSL_free(ssl->s3->pending_message);
-  ssl->s3->pending_message = NULL;
-  ssl->s3->pending_message_len = 0;
+  /* Write the pending flight. */
+  while (ssl->s3->pending_flight_offset < ssl->s3->pending_flight->length) {
+    int ret = BIO_write(
+        ssl->wbio,
+        ssl->s3->pending_flight->data + ssl->s3->pending_flight_offset,
+        ssl->s3->pending_flight->length - ssl->s3->pending_flight_offset);
+    if (ret <= 0) {
+      ssl->rwstate = SSL_WRITING;
+      return ret;
+    }
+
+    ssl->s3->pending_flight_offset += ret;
+  }
+
+  if (BIO_flush(ssl->wbio) <= 0) {
+    ssl->rwstate = SSL_WRITING;
+    return -1;
+  }
+
+  BUF_MEM_free(ssl->s3->pending_flight);
+  ssl->s3->pending_flight = NULL;
+  ssl->s3->pending_flight_offset = 0;
   return 1;
 }
 
-int ssl3_send_finished(SSL_HANDSHAKE *hs, int a, int b) {
+int ssl3_send_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (hs->state == b) {
-    return ssl->method->write_message(ssl);
-  }
-
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len =
       ssl->s3->enc_method->final_finish_mac(ssl, ssl->server, finished);
@@ -307,24 +387,26 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, finished, finished_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return -1;
   }
 
-  hs->state = b;
-  return ssl->method->write_message(ssl);
+  return 1;
 }
 
 int ssl3_get_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  int ret = ssl->method->ssl_get_message(ssl, SSL3_MT_FINISHED,
-                                         ssl_dont_hash_message);
+  int ret = ssl->method->ssl_get_message(ssl);
   if (ret <= 0) {
     return ret;
   }
 
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED)) {
+    return -1;
+  }
+
   /* Snapshot the finished hash before incorporating the new message. */
   uint8_t finished[EVP_MAX_MD_SIZE];
   size_t finished_len =
@@ -365,18 +447,11 @@
   return 1;
 }
 
-int ssl3_send_change_cipher_spec(SSL *ssl) {
-  static const uint8_t kChangeCipherSpec[1] = {SSL3_MT_CCS};
-
-  return ssl3_do_write(ssl, SSL3_RT_CHANGE_CIPHER_SPEC, kChangeCipherSpec,
-                       sizeof(kChangeCipherSpec));
-}
-
 int ssl3_output_cert_chain(SSL *ssl) {
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
       !ssl_add_cert_chain(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     CBB_cleanup(&cbb);
     return 0;
@@ -431,7 +506,7 @@
   return 1;
 }
 
-static int read_v2_client_hello(SSL *ssl, int *out_is_v2_client_hello) {
+static int read_v2_client_hello(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. */
@@ -459,7 +534,6 @@
   if ((p[0] & 0x80) == 0 || p[2] != SSL2_MT_CLIENT_HELLO ||
       p[3] != SSL3_VERSION_MAJOR) {
     /* Not a V2ClientHello. */
-    *out_is_v2_client_hello = 0;
     return 1;
   }
 
@@ -581,12 +655,13 @@
   ssl_read_buffer_consume(ssl, 2 + msg_length);
   ssl_read_buffer_discard(ssl);
 
-  *out_is_v2_client_hello = 1;
+  ssl->s3->is_v2_hello = 1;
+  /* This is the first message, so hs must be non-NULL. */
+  ssl->s3->hs->v2_clienthello = 1;
   return 1;
 }
 
-int ssl3_get_message(SSL *ssl, int msg_type,
-                     enum ssl_hash_message_t hash_message) {
+int ssl3_get_message(SSL *ssl) {
 again:
   /* Re-create the handshake buffer if needed. */
   if (ssl->init_buf == NULL) {
@@ -598,28 +673,17 @@
 
   if (ssl->server && !ssl->s3->v2_hello_done) {
     /* Bypass the record layer for the first message to handle V2ClientHello. */
-    assert(hash_message == ssl_hash_message);
-    int is_v2_client_hello = 0;
-    int ret = read_v2_client_hello(ssl, &is_v2_client_hello);
+    int ret = read_v2_client_hello(ssl);
     if (ret <= 0) {
       return ret;
     }
-    if (is_v2_client_hello) {
-      /* V2ClientHello is hashed separately. */
-      hash_message = ssl_dont_hash_message;
-    }
     ssl->s3->v2_hello_done = 1;
   }
 
   if (ssl->s3->tmp.reuse_message) {
-    /* A ssl_dont_hash_message call cannot be combined with reuse_message; the
-     * ssl_dont_hash_message would have to have been applied to the previous
-     * call. */
-    assert(hash_message == ssl_hash_message);
+    /* There must be a current message. */
     assert(ssl->init_msg != NULL);
-
     ssl->s3->tmp.reuse_message = 0;
-    hash_message = ssl_dont_hash_message;
   } else {
     ssl3_release_current_message(ssl, 0 /* don't free buffer */);
   }
@@ -663,17 +727,6 @@
     goto again;
   }
 
-  if (msg_type >= 0 && ssl->s3->tmp.message_type != msg_type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    return -1;
-  }
-
-  /* Feed this message into MAC computation. */
-  if (hash_message == ssl_hash_message && !ssl_hash_current_message(ssl)) {
-    return -1;
-  }
-
   return 1;
 }
 
@@ -682,6 +735,11 @@
 }
 
 int ssl_hash_current_message(SSL *ssl) {
+  /* V2ClientHellos are hashed implicitly. */
+  if (ssl->s3->is_v2_hello) {
+    return 1;
+  }
+
   CBS cbs;
   ssl->method->get_current_message(ssl, &cbs);
   return ssl3_update_handshake_hash(ssl, CBS_data(&cbs), CBS_len(&cbs));
@@ -696,6 +754,7 @@
     ssl->init_msg = NULL;
     ssl->init_num = 0;
     ssl->init_buf->length = 0;
+    ssl->s3->is_v2_hello = 0;
   }
 
   if (free_buffer) {
diff --git a/src/ssl/s3_lib.c b/src/ssl/s3_lib.c
index 7039418..3f44629 100644
--- a/src/ssl/s3_lib.c
+++ b/src/ssl/s3_lib.c
@@ -209,7 +209,7 @@
   OPENSSL_free(ssl->s3->alpn_selected);
   SSL_AEAD_CTX_free(ssl->s3->aead_read_ctx);
   SSL_AEAD_CTX_free(ssl->s3->aead_write_ctx);
-  OPENSSL_free(ssl->s3->pending_message);
+  BUF_MEM_free(ssl->s3->pending_flight);
 
   OPENSSL_cleanse(ssl->s3, sizeof *ssl->s3);
   OPENSSL_free(ssl->s3);
diff --git a/src/ssl/s3_pkt.c b/src/ssl/s3_pkt.c
index 9bd9f1f..5d5b7e8 100644
--- a/src/ssl/s3_pkt.c
+++ b/src/ssl/s3_pkt.c
@@ -188,16 +188,9 @@
   return -1;
 }
 
-int ssl3_write_app_data(SSL *ssl, const void *buf, int len) {
+int ssl3_write_app_data(SSL *ssl, const uint8_t *buf, int len) {
   assert(!SSL_in_init(ssl) || SSL_in_false_start(ssl));
 
-  return ssl3_write_bytes(ssl, SSL3_RT_APPLICATION_DATA, buf, 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 ssl3_write_bytes(SSL *ssl, int type, const void *buf_, int len) {
-  const uint8_t *buf = buf_;
   unsigned tot, n, nw;
 
   assert(ssl->s3->wnum <= INT_MAX);
@@ -216,7 +209,7 @@
     return -1;
   }
 
-  n = (len - tot);
+  n = len - tot;
   for (;;) {
     /* max contains the maximum number of bytes that we can put into a
      * record. */
@@ -227,14 +220,13 @@
       nw = n;
     }
 
-    int ret = do_ssl3_write(ssl, type, &buf[tot], nw);
+    int ret = do_ssl3_write(ssl, SSL3_RT_APPLICATION_DATA, &buf[tot], nw);
     if (ret <= 0) {
       ssl->s3->wnum = tot;
       return ret;
     }
 
-    if (ret == (int)n || (type == SSL3_RT_APPLICATION_DATA &&
-                          (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE))) {
+    if (ret == (int)n || (ssl->mode & SSL_MODE_ENABLE_PARTIAL_WRITE)) {
       return tot + ret;
     }
 
@@ -267,13 +259,12 @@
     return ssl3_write_pending(ssl, type, buf, len);
   }
 
-  /* If we have an alert to send, lets send it */
-  if (ssl->s3->alert_dispatch) {
-    int ret = ssl->method->dispatch_alert(ssl);
-    if (ret <= 0) {
-      return ret;
-    }
-    /* if it went, fall through and send more stuff */
+  /* The handshake flight buffer is mutually exclusive with application data.
+   *
+   * TODO(davidben): This will not be true when closure alerts use this. */
+  if (ssl->s3->pending_flight != NULL) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+    return -1;
   }
 
   if (len > SSL3_RT_MAX_PLAIN_LENGTH) {
@@ -364,7 +355,7 @@
       }
 
       /* Parse post-handshake handshake messages. */
-      int ret = ssl3_get_message(ssl, -1, ssl_dont_hash_message);
+      int ret = ssl3_get_message(ssl);
       if (ret <= 0) {
         return ret;
       }
@@ -456,10 +447,11 @@
     return -1;
   }
 
-  if (level == SSL3_AL_FATAL) {
-    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
-  } else if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
+  if (level == SSL3_AL_WARNING && desc == SSL_AD_CLOSE_NOTIFY) {
     ssl->s3->send_shutdown = ssl_shutdown_close_notify;
+  } else {
+    assert(level == SSL3_AL_FATAL);
+    ssl->s3->send_shutdown = ssl_shutdown_fatal_alert;
   }
 
   ssl->s3->alert_dispatch = 1;
@@ -476,12 +468,11 @@
 }
 
 int ssl3_dispatch_alert(SSL *ssl) {
-  ssl->s3->alert_dispatch = 0;
   int ret = do_ssl3_write(ssl, SSL3_RT_ALERT, &ssl->s3->send_alert[0], 2);
   if (ret <= 0) {
-    ssl->s3->alert_dispatch = 1;
     return ret;
   }
+  ssl->s3->alert_dispatch = 0;
 
   /* If the alert is fatal, flush the BIO now. */
   if (ssl->s3->send_alert[0] == SSL3_AL_FATAL) {
diff --git a/src/ssl/ssl_asn1.c b/src/ssl/ssl_asn1.c
index c9dfdcc..4c1ee89 100644
--- a/src/ssl/ssl_asn1.c
+++ b/src/ssl/ssl_asn1.c
@@ -129,10 +129,11 @@
  *     isServer                [22] BOOLEAN DEFAULT TRUE,
  *     peerSignatureAlgorithm  [23] INTEGER OPTIONAL,
  *     ticketMaxEarlyData      [24] INTEGER OPTIONAL,
+ *     authTimeout             [25] INTEGER OPTIONAL, -- defaults to timeout
  * }
  *
  * Note: historically this serialization has included other optional
- * fields. Their presense is currently treated as a parse error:
+ * fields. Their presence is currently treated as a parse error:
  *
  *     keyArg                  [0] IMPLICIT OCTET STRING OPTIONAL,
  *     pskIdentityHint         [7] OCTET STRING OPTIONAL,
@@ -183,6 +184,8 @@
     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 23;
 static const int kTicketMaxEarlyDataTag =
     CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 24;
+static const int kAuthTimeoutTag =
+    CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 25;
 
 static int SSL_SESSION_to_bytes_full(const SSL_SESSION *in, uint8_t **out_data,
                                      size_t *out_len, int for_ticket) {
@@ -402,6 +405,13 @@
     goto err;
   }
 
+  if (in->timeout != in->auth_timeout &&
+      (!CBB_add_asn1(&session, &child, kAuthTimeoutTag) ||
+       !CBB_add_asn1_uint64(&child, in->auth_timeout))) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
   if (!CBB_finish(&cbb, out_data, out_len)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
     goto err;
@@ -787,6 +797,8 @@
                              kPeerSignatureAlgorithmTag, 0) ||
       !SSL_SESSION_parse_u32(&session, &ret->ticket_max_early_data,
                              kTicketMaxEarlyDataTag, 0) ||
+      !SSL_SESSION_parse_long(&session, &ret->auth_timeout, kAuthTimeoutTag,
+                              ret->timeout) ||
       CBS_len(&session) != 0) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SSL_SESSION);
     goto err;
diff --git a/src/ssl/ssl_cert.c b/src/ssl/ssl_cert.c
index 66edf37..6452620 100644
--- a/src/ssl/ssl_cert.c
+++ b/src/ssl/ssl_cert.c
@@ -152,6 +152,11 @@
   return ret;
 }
 
+static CRYPTO_BUFFER *buffer_up_ref(CRYPTO_BUFFER *buffer) {
+  CRYPTO_BUFFER_up_ref(buffer);
+  return buffer;
+}
+
 CERT *ssl_cert_dup(CERT *cert) {
   CERT *ret = OPENSSL_malloc(sizeof(CERT));
   if (ret == NULL) {
@@ -160,29 +165,16 @@
   }
   OPENSSL_memset(ret, 0, sizeof(CERT));
 
-  if (cert->x509_leaf != NULL) {
-    X509_up_ref(cert->x509_leaf);
-    ret->x509_leaf = cert->x509_leaf;
-  }
+  ret->chain = sk_CRYPTO_BUFFER_deep_copy(cert->chain, buffer_up_ref,
+                                          CRYPTO_BUFFER_free);
 
   if (cert->privatekey != NULL) {
     EVP_PKEY_up_ref(cert->privatekey);
     ret->privatekey = cert->privatekey;
   }
 
-  if (cert->x509_chain) {
-    ret->x509_chain = X509_chain_up_ref(cert->x509_chain);
-    if (!ret->x509_chain) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-  }
-
   ret->key_method = cert->key_method;
 
-  ret->mask_k = cert->mask_k;
-  ret->mask_a = cert->mask_a;
-
   if (cert->dh_tmp != NULL) {
     ret->dh_tmp = DHparams_dup(cert->dh_tmp);
     if (ret->dh_tmp == NULL) {
@@ -216,18 +208,32 @@
   return NULL;
 }
 
+void ssl_cert_flush_cached_x509_leaf(CERT *cert) {
+  X509_free(cert->x509_leaf);
+  cert->x509_leaf = NULL;
+}
+
+static void ssl_cert_flush_cached_x509_chain(CERT *cert) {
+  sk_X509_pop_free(cert->x509_chain, X509_free);
+  cert->x509_chain = NULL;
+}
+
 /* Free up and clear all certificates and chains */
 void ssl_cert_clear_certs(CERT *cert) {
   if (cert == NULL) {
     return;
   }
 
-  X509_free(cert->x509_leaf);
-  cert->x509_leaf = NULL;
+  ssl_cert_flush_cached_x509_leaf(cert);
+  ssl_cert_flush_cached_x509_chain(cert);
+
+  X509_free(cert->x509_stash);
+  cert->x509_stash = NULL;
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = NULL;
   EVP_PKEY_free(cert->privatekey);
   cert->privatekey = NULL;
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = NULL;
   cert->key_method = NULL;
 }
 
@@ -245,25 +251,125 @@
   OPENSSL_free(c);
 }
 
+/* new_leafless_chain returns a fresh stack of buffers set to {NULL}. */
+static STACK_OF(CRYPTO_BUFFER) *new_leafless_chain(void) {
+  STACK_OF(CRYPTO_BUFFER) *chain = sk_CRYPTO_BUFFER_new_null();
+  if (chain == NULL) {
+    return NULL;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(chain, NULL)) {
+    sk_CRYPTO_BUFFER_free(chain);
+    return NULL;
+  }
+
+  return chain;
+}
+
+/* x509_to_buffer returns a |CRYPTO_BUFFER| that contains the serialised
+ * contents of |x509|. */
+CRYPTO_BUFFER *x509_to_buffer(X509 *x509) {
+  uint8_t *buf = NULL;
+  int cert_len = i2d_X509(x509, &buf);
+  if (cert_len <= 0) {
+    return 0;
+  }
+
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(buf, cert_len, NULL);
+  OPENSSL_free(buf);
+
+  return buffer;
+}
+
+/* ssl_cert_set_chain sets elements 1.. of |cert->chain| to the serialised
+ * forms of elements of |chain|. It returns one on success or zero on error, in
+ * which case no change to |cert->chain| is made. It preverses the existing
+ * leaf from |cert->chain|, if any. */
+static int ssl_cert_set_chain(CERT *cert, STACK_OF(X509) *chain) {
+  STACK_OF(CRYPTO_BUFFER) *new_chain = NULL;
+
+  if (cert->chain != NULL) {
+    new_chain = sk_CRYPTO_BUFFER_new_null();
+    if (new_chain == NULL) {
+      return 0;
+    }
+
+    CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+    if (!sk_CRYPTO_BUFFER_push(new_chain, leaf)) {
+      goto err;
+    }
+    /* |leaf| might be NULL if it's a “leafless” chain. */
+    if (leaf != NULL) {
+      CRYPTO_BUFFER_up_ref(leaf);
+    }
+  }
+
+  for (size_t i = 0; i < sk_X509_num(chain); i++) {
+    if (new_chain == NULL) {
+      new_chain = new_leafless_chain();
+      if (new_chain == NULL) {
+        goto err;
+      }
+    }
+
+    CRYPTO_BUFFER *buffer = x509_to_buffer(sk_X509_value(chain, i));
+    if (buffer == NULL ||
+        !sk_CRYPTO_BUFFER_push(new_chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      goto err;
+    }
+  }
+
+  sk_CRYPTO_BUFFER_pop_free(cert->chain, CRYPTO_BUFFER_free);
+  cert->chain = new_chain;
+
+  return 1;
+
+err:
+  sk_CRYPTO_BUFFER_pop_free(new_chain, CRYPTO_BUFFER_free);
+  return 0;
+}
+
 static int ssl_cert_set0_chain(CERT *cert, STACK_OF(X509) *chain) {
-  sk_X509_pop_free(cert->x509_chain, X509_free);
-  cert->x509_chain = chain;
+  if (!ssl_cert_set_chain(cert, chain)) {
+    return 0;
+  }
+
+  sk_X509_pop_free(chain, X509_free);
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
 static int ssl_cert_set1_chain(CERT *cert, STACK_OF(X509) *chain) {
-  STACK_OF(X509) *dchain;
-  if (chain == NULL) {
-    return ssl_cert_set0_chain(cert, NULL);
-  }
-
-  dchain = X509_chain_up_ref(chain);
-  if (dchain == NULL) {
+  if (!ssl_cert_set_chain(cert, chain)) {
     return 0;
   }
 
-  if (!ssl_cert_set0_chain(cert, dchain)) {
-    sk_X509_pop_free(dchain, X509_free);
+  ssl_cert_flush_cached_x509_chain(cert);
+  return 1;
+}
+
+static int ssl_cert_append_cert(CERT *cert, X509 *x509) {
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x509);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  if (cert->chain != NULL) {
+    if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+      CRYPTO_BUFFER_free(buffer);
+      return 0;
+    }
+
+    return 1;
+  }
+
+  cert->chain = new_leafless_chain();
+  if (cert->chain == NULL ||
+      !sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    CRYPTO_BUFFER_free(buffer);
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
     return 0;
   }
 
@@ -271,22 +377,22 @@
 }
 
 static int ssl_cert_add0_chain_cert(CERT *cert, X509 *x509) {
-  if (cert->x509_chain == NULL) {
-    cert->x509_chain = sk_X509_new_null();
-  }
-  if (cert->x509_chain == NULL || !sk_X509_push(cert->x509_chain, x509)) {
+  if (!ssl_cert_append_cert(cert, x509)) {
     return 0;
   }
 
+  X509_free(cert->x509_stash);
+  cert->x509_stash = x509;
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
 static int ssl_cert_add1_chain_cert(CERT *cert, X509 *x509) {
-  if (!ssl_cert_add0_chain_cert(cert, x509)) {
+  if (!ssl_cert_append_cert(cert, x509)) {
     return 0;
   }
 
-  X509_up_ref(x509);
+  ssl_cert_flush_cached_x509_chain(cert);
   return 1;
 }
 
@@ -446,7 +552,9 @@
 }
 
 int ssl_has_certificate(const SSL *ssl) {
-  return ssl->cert->x509_leaf != NULL && ssl_has_private_key(ssl);
+  return ssl->cert->chain != NULL &&
+         sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0) != NULL &&
+         ssl_has_private_key(ssl);
 }
 
 STACK_OF(CRYPTO_BUFFER) *ssl_parse_cert_chain(uint8_t *out_alert,
@@ -515,49 +623,33 @@
   return NULL;
 }
 
-int ssl_add_cert_to_cbb(CBB *cbb, X509 *x509) {
-  int len = i2d_X509(x509, NULL);
-  if (len < 0) {
-    return 0;
-  }
-  uint8_t *buf;
-  if (!CBB_add_space(cbb, &buf, len)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    return 0;
-  }
-  if (buf != NULL && i2d_X509(x509, &buf) < 0) {
-    return 0;
-  }
-  return 1;
-}
-
-static int ssl_add_cert_with_length(CBB *cbb, X509 *x509) {
-  CBB child;
-  return CBB_add_u24_length_prefixed(cbb, &child) &&
-         ssl_add_cert_to_cbb(&child, x509) &&
-         CBB_flush(cbb);
-}
-
 int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
   if (!ssl_has_certificate(ssl)) {
     return CBB_add_u24(cbb, 0);
   }
 
-  CBB child;
-  if (!CBB_add_u24_length_prefixed(cbb, &child) ||
-      !ssl_add_cert_with_length(&child, ssl->cert->x509_leaf)) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return 0;
+  CBB certs;
+  if (!CBB_add_u24_length_prefixed(cbb, &certs)) {
+    goto err;
   }
 
-  STACK_OF(X509) *chain = ssl->cert->x509_chain;
-  for (size_t i = 0; i < sk_X509_num(chain); i++) {
-    if (!ssl_add_cert_with_length(&child, sk_X509_value(chain, i))) {
-      return 0;
+  STACK_OF(CRYPTO_BUFFER) *chain = ssl->cert->chain;
+  for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(chain, i);
+    CBB child;
+    if (!CBB_add_u24_length_prefixed(&certs, &child) ||
+        !CBB_add_bytes(&child, CRYPTO_BUFFER_data(buffer),
+                       CRYPTO_BUFFER_len(buffer)) ||
+        !CBB_flush(&certs)) {
+      goto err;
     }
   }
 
   return CBB_flush(cbb);
+
+err:
+  OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
+  return 0;
 }
 
 int ssl_auto_chain_if_needed(SSL *ssl) {
@@ -565,30 +657,41 @@
    * isn't disabled. */
   if ((ssl->mode & SSL_MODE_NO_AUTO_CHAIN) ||
       !ssl_has_certificate(ssl) ||
-      ssl->cert->x509_chain != NULL) {
+      ssl->cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(ssl->cert->chain) > 1) {
     return 1;
   }
 
+  X509 *leaf =
+      X509_parse_from_buffer(sk_CRYPTO_BUFFER_value(ssl->cert->chain, 0));
+  if (!leaf) {
+    OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
+    return 0;
+  }
+
   X509_STORE_CTX ctx;
-  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, ssl->cert->x509_leaf,
-                           NULL)) {
+  if (!X509_STORE_CTX_init(&ctx, ssl->ctx->cert_store, leaf, NULL)) {
+    X509_free(leaf);
     OPENSSL_PUT_ERROR(SSL, ERR_R_X509_LIB);
     return 0;
   }
 
   /* Attempt to build a chain, ignoring the result. */
   X509_verify_cert(&ctx);
+  X509_free(leaf);
   ERR_clear_error();
 
-  /* Configure the intermediates from any partial chain we managed to build. */
-  for (size_t i = 1; i < sk_X509_num(ctx.chain); i++) {
-    if (!SSL_add1_chain_cert(ssl, sk_X509_value(ctx.chain, i))) {
-      X509_STORE_CTX_cleanup(&ctx);
-      return 0;
-    }
+  /* Remove the leaf from the generated chain. */
+  X509_free(sk_X509_shift(ctx.chain));
+
+  const int ok = ssl_cert_set_chain(ssl->cert, ctx.chain);
+  X509_STORE_CTX_cleanup(&ctx);
+  if (!ok) {
+    return 0;
   }
 
-  X509_STORE_CTX_cleanup(&ctx);
+  ssl_cert_flush_cached_x509_chain(ssl->cert);
+
   return 1;
 }
 
@@ -647,6 +750,55 @@
   return EVP_parse_public_key(&tbs_cert);
 }
 
+int ssl_compare_public_and_private_key(const EVP_PKEY *pubkey,
+                                       const EVP_PKEY *privkey) {
+  int ret = 0;
+
+  switch (EVP_PKEY_cmp(pubkey, privkey)) {
+    case 1:
+      ret = 1;
+      break;
+    case 0:
+      OPENSSL_PUT_ERROR(X509, X509_R_KEY_VALUES_MISMATCH);
+      break;
+    case -1:
+      OPENSSL_PUT_ERROR(X509, X509_R_KEY_TYPE_MISMATCH);
+      break;
+    case -2:
+      OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
+    default:
+      assert(0);
+      break;
+  }
+
+  return ret;
+}
+
+int ssl_cert_check_private_key(const CERT *cert, const EVP_PKEY *privkey) {
+  if (privkey == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
+    return 0;
+  }
+
+  if (cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_value(cert->chain, 0) == NULL) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
+    return 0;
+  }
+
+  CBS cert_cbs;
+  CRYPTO_BUFFER_init_CBS(sk_CRYPTO_BUFFER_value(cert->chain, 0), &cert_cbs);
+  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
+  if (!pubkey) {
+    OPENSSL_PUT_ERROR(X509, X509_R_UNKNOWN_KEY_TYPE);
+    return 0;
+  }
+
+  const int ok = ssl_compare_public_and_private_key(pubkey, privkey);
+  EVP_PKEY_free(pubkey);
+  return ok;
+}
+
 int ssl_cert_check_digital_signature_key_usage(const CBS *in) {
   CBS buf = *in;
 
@@ -891,7 +1043,61 @@
   ssl_cert_set_cert_cb(ssl->cert, cb, arg);
 }
 
+/* ssl_cert_cache_leaf_cert sets |cert->x509_leaf|, if currently NULL, from the
+ * first element of |cert->chain|. */
+int ssl_cert_cache_leaf_cert(CERT *cert) {
+  if (cert->x509_leaf != NULL ||
+      cert->chain == NULL) {
+    return 1;
+  }
+
+  CRYPTO_BUFFER *leaf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
+  if (!leaf) {
+    return 1;
+  }
+
+  cert->x509_leaf = X509_parse_from_buffer(leaf);
+  return cert->x509_leaf != NULL;
+}
+
+/* ssl_cert_cache_chain_certs fills in |cert->x509_chain| from elements 1.. of
+ * |cert->chain|. */
+static int ssl_cert_cache_chain_certs(CERT *cert) {
+  if (cert->x509_chain != NULL ||
+      cert->chain == NULL ||
+      sk_CRYPTO_BUFFER_num(cert->chain) < 2) {
+    return 1;
+  }
+
+  STACK_OF(X509) *chain = sk_X509_new_null();
+  if (chain == NULL) {
+    return 0;
+  }
+
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *buffer = sk_CRYPTO_BUFFER_value(cert->chain, i);
+    X509 *x509 = X509_parse_from_buffer(buffer);
+    if (x509 == NULL ||
+        !sk_X509_push(chain, x509)) {
+      X509_free(x509);
+      goto err;
+    }
+  }
+
+  cert->x509_chain = chain;
+  return 1;
+
+err:
+  sk_X509_pop_free(chain, X509_free);
+  return 0;
+}
+
 int SSL_CTX_get0_chain_certs(const SSL_CTX *ctx, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ctx->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
   *out_chain = ctx->cert->x509_chain;
   return 1;
 }
@@ -902,6 +1108,11 @@
 }
 
 int SSL_get0_chain_certs(const SSL *ssl, STACK_OF(X509) **out_chain) {
+  if (!ssl_cert_cache_chain_certs(ssl->cert)) {
+    *out_chain = NULL;
+    return 0;
+  }
+
   *out_chain = ssl->cert->x509_chain;
   return 1;
 }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index ba1ee8f..851c81f 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -254,8 +254,8 @@
   ret->session_cache_mode = SSL_SESS_CACHE_SERVER;
   ret->session_cache_size = SSL_SESSION_CACHE_MAX_SIZE_DEFAULT;
 
-  /* We take the system default */
   ret->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  ret->session_psk_dhe_timeout = SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT;
 
   ret->references = 1;
 
@@ -425,22 +425,21 @@
   ssl->initial_ctx = ctx;
 
   if (ctx->supported_group_list) {
-    ssl->supported_group_list =
-        BUF_memdup(ctx->supported_group_list,
-                   ctx->supported_group_list_len * 2);
+    ssl->supported_group_list = BUF_memdup(ctx->supported_group_list,
+                                           ctx->supported_group_list_len * 2);
     if (!ssl->supported_group_list) {
       goto err;
     }
     ssl->supported_group_list_len = ctx->supported_group_list_len;
   }
 
-  if (ssl->ctx->alpn_client_proto_list) {
-    ssl->alpn_client_proto_list = BUF_memdup(
-        ssl->ctx->alpn_client_proto_list, ssl->ctx->alpn_client_proto_list_len);
+  if (ctx->alpn_client_proto_list) {
+    ssl->alpn_client_proto_list = BUF_memdup(ctx->alpn_client_proto_list,
+                                             ctx->alpn_client_proto_list_len);
     if (ssl->alpn_client_proto_list == NULL) {
       goto err;
     }
-    ssl->alpn_client_proto_list_len = ssl->ctx->alpn_client_proto_list_len;
+    ssl->alpn_client_proto_list_len = ctx->alpn_client_proto_list_len;
   }
 
   ssl->method = ctx->method;
@@ -469,16 +468,11 @@
     ssl->tlsext_channel_id_private = ctx->tlsext_channel_id_private;
   }
 
-  ssl->signed_cert_timestamps_enabled =
-      ssl->ctx->signed_cert_timestamps_enabled;
-  ssl->ocsp_stapling_enabled = ssl->ctx->ocsp_stapling_enabled;
+  ssl->signed_cert_timestamps_enabled = ctx->signed_cert_timestamps_enabled;
+  ssl->ocsp_stapling_enabled = ctx->ocsp_stapling_enabled;
 
-  ssl->session_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
-
-  /* If the context has a default timeout, use it over the default. */
-  if (ctx->session_timeout != 0) {
-    ssl->session_timeout = ctx->session_timeout;
-  }
+  ssl->session_timeout = ctx->session_timeout;
+  ssl->session_psk_dhe_timeout = ctx->session_psk_dhe_timeout;
 
   /* If the context has an OCSP response, use it. */
   if (ctx->ocsp_response != NULL) {
@@ -504,9 +498,6 @@
 
   CRYPTO_free_ex_data(&g_ex_data_class_ssl, ssl, &ssl->ex_data);
 
-  ssl_free_wbio_buffer(ssl);
-  assert(ssl->bbio == NULL);
-
   BIO_free_all(ssl->rbio);
   BIO_free_all(ssl->wbio);
 
@@ -553,18 +544,8 @@
 }
 
 void SSL_set0_wbio(SSL *ssl, BIO *wbio) {
-  /* If the output buffering BIO is still in place, remove it. */
-  if (ssl->bbio != NULL) {
-    ssl->wbio = BIO_pop(ssl->wbio);
-  }
-
   BIO_free_all(ssl->wbio);
   ssl->wbio = wbio;
-
-  /* Re-attach |bbio| to the new |wbio|. */
-  if (ssl->bbio != NULL) {
-    ssl->wbio = BIO_push(ssl->bbio, ssl->wbio);
-  }
 }
 
 void SSL_set_bio(SSL *ssl, BIO *rbio, BIO *wbio) {
@@ -603,14 +584,7 @@
 
 BIO *SSL_get_rbio(const SSL *ssl) { return ssl->rbio; }
 
-BIO *SSL_get_wbio(const SSL *ssl) {
-  if (ssl->bbio != NULL) {
-    /* If |bbio| is active, the true caller-configured BIO is its |next_bio|. */
-    assert(ssl->bbio == ssl->wbio);
-    return ssl->bbio->next_bio;
-  }
-  return ssl->wbio;
-}
+BIO *SSL_get_wbio(const SSL *ssl) { return ssl->wbio; }
 
 void ssl_reset_error_state(SSL *ssl) {
   /* Functions which use |SSL_get_error| must reset I/O and error state on
@@ -1292,34 +1266,12 @@
 
 /* Fix this so it checks all the valid key/cert options */
 int SSL_CTX_check_private_key(const SSL_CTX *ctx) {
-  if (ctx->cert->privatekey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
-    return 0;
-  }
-
-  X509 *x509 = ctx->cert->x509_leaf;
-  if (x509 == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
-  }
-
-  return X509_check_private_key(x509, ctx->cert->privatekey);
+  return ssl_cert_check_private_key(ctx->cert, ctx->cert->privatekey);
 }
 
 /* Fix this function so that it takes an optional type parameter */
 int SSL_check_private_key(const SSL *ssl) {
-  if (ssl->cert->privatekey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED);
-    return 0;
-  }
-
-  X509 *x509 = ssl->cert->x509_leaf;
-  if (x509 == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_ASSIGNED);
-    return 0;
-  }
-
-  return X509_check_private_key(x509, ssl->cert->privatekey);
+  return ssl_cert_check_private_key(ssl->cert, ssl->cert->privatekey);
 }
 
 long SSL_get_default_timeout(const SSL *ssl) {
@@ -2023,41 +1975,6 @@
 
 int *SSL_get_server_tmp_key(SSL *ssl, EVP_PKEY **out_key) { return 0; }
 
-int ssl_is_wbio_buffered(const SSL *ssl) {
-  return ssl->bbio != NULL;
-}
-
-int ssl_init_wbio_buffer(SSL *ssl) {
-  if (ssl->bbio != NULL) {
-    /* Already buffered. */
-    assert(ssl->bbio == ssl->wbio);
-    return 1;
-  }
-
-  BIO *bbio = BIO_new(BIO_f_buffer());
-  if (bbio == NULL ||
-      !BIO_set_read_buffer_size(bbio, 1)) {
-    BIO_free(bbio);
-    return 0;
-  }
-
-  ssl->bbio = bbio;
-  ssl->wbio = BIO_push(bbio, ssl->wbio);
-  return 1;
-}
-
-void ssl_free_wbio_buffer(SSL *ssl) {
-  if (ssl->bbio == NULL) {
-    return;
-  }
-
-  assert(ssl->bbio == ssl->wbio);
-
-  ssl->wbio = BIO_pop(ssl->wbio);
-  BIO_free(ssl->bbio);
-  ssl->bbio = NULL;
-}
-
 void SSL_CTX_set_quiet_shutdown(SSL_CTX *ctx, int mode) {
   ctx->quiet_shutdown = (mode != 0);
 }
diff --git a/src/ssl/ssl_rsa.c b/src/ssl/ssl_rsa.c
index 34d1f86..6ad2b71 100644
--- a/src/ssl/ssl_rsa.c
+++ b/src/ssl/ssl_rsa.c
@@ -70,8 +70,8 @@
 #include "internal.h"
 
 
-static int ssl_set_cert(CERT *c, X509 *x509);
-static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey);
+static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer);
+static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey);
 
 static int is_key_type_supported(int key_type) {
   return key_type == EVP_PKEY_RSA || key_type == EVP_PKEY_EC;
@@ -82,26 +82,26 @@
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
-  return ssl_set_cert(ssl->cert, x);
+
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
+    return 0;
+  }
+
+  const int ok = ssl_set_cert(ssl->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_use_certificate_ASN1(SSL *ssl, const uint8_t *der, size_t der_len) {
-  if (der_len > LONG_MAX) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
     return 0;
   }
 
-  const uint8_t *p = der;
-  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
-  if (x509 == NULL || p != der + der_len) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-    X509_free(x509);
-    return 0;
-  }
-
-  int ret = SSL_use_certificate(ssl, x509);
-  X509_free(x509);
-  return ret;
+  const int ok = ssl_set_cert(ssl->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_use_RSAPrivateKey(SSL *ssl, RSA *rsa) {
@@ -128,41 +128,35 @@
   return ret;
 }
 
-static int ssl_set_pkey(CERT *c, EVP_PKEY *pkey) {
+static int ssl_set_pkey(CERT *cert, EVP_PKEY *pkey) {
   if (!is_key_type_supported(pkey->type)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
     return 0;
   }
 
-  X509 *x509_leaf = c->x509_leaf;
-  if (x509_leaf != NULL) {
-    /* Sanity-check that the private key and the certificate match, unless the
-     * key is opaque (in case of, say, a smartcard). */
-    if (!EVP_PKEY_is_opaque(pkey) &&
-        !X509_check_private_key(x509_leaf, pkey)) {
-      X509_free(c->x509_leaf);
-      c->x509_leaf = NULL;
-      return 0;
-    }
+  if (cert->chain != NULL &&
+      sk_CRYPTO_BUFFER_value(cert->chain, 0) != NULL &&
+      /* Sanity-check that the private key and the certificate match, unless
+       * the key is opaque (in case of, say, a smartcard). */
+      !EVP_PKEY_is_opaque(pkey) &&
+      !ssl_cert_check_private_key(cert, pkey)) {
+    return 0;
   }
 
-  EVP_PKEY_free(c->privatekey);
+  EVP_PKEY_free(cert->privatekey);
   EVP_PKEY_up_ref(pkey);
-  c->privatekey = pkey;
+  cert->privatekey = pkey;
 
   return 1;
 }
 
 int SSL_use_PrivateKey(SSL *ssl, EVP_PKEY *pkey) {
-  int ret;
-
   if (pkey == NULL) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER);
     return 0;
   }
 
-  ret = ssl_set_pkey(ssl->cert, pkey);
-  return ret;
+  return ssl_set_pkey(ssl->cert, pkey);
 }
 
 int SSL_use_PrivateKey_ASN1(int type, SSL *ssl, const uint8_t *der,
@@ -191,77 +185,90 @@
     return 0;
   }
 
-  return ssl_set_cert(ctx->cert, x);
-}
-
-static int ssl_set_cert(CERT *c, X509 *x) {
-  EVP_PKEY *pkey = X509_get_pubkey(x);
-  if (pkey == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_X509_LIB);
+  CRYPTO_BUFFER *buffer = x509_to_buffer(x);
+  if (buffer == NULL) {
     return 0;
   }
 
-  if (!is_key_type_supported(pkey->type)) {
+  const int ok = ssl_set_cert(ctx->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
+}
+
+static int ssl_set_cert(CERT *cert, CRYPTO_BUFFER *buffer) {
+  CBS cert_cbs;
+  CRYPTO_BUFFER_init_CBS(buffer, &cert_cbs);
+  EVP_PKEY *pubkey = ssl_cert_parse_pubkey(&cert_cbs);
+  if (pubkey == NULL) {
+    return 0;
+  }
+
+  if (!is_key_type_supported(pubkey->type)) {
     OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-    EVP_PKEY_free(pkey);
+    EVP_PKEY_free(pubkey);
     return 0;
   }
 
   /* An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
    * certificates, so sanity-check the key usage extension. */
-  if (pkey->type == EVP_PKEY_EC) {
-    /* This call populates extension flags (ex_flags). */
-    X509_check_purpose(x, -1, 0);
-    if ((x->ex_flags & EXFLAG_KUSAGE) &&
-        !(x->ex_kusage & X509v3_KU_DIGITAL_SIGNATURE)) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
-      EVP_PKEY_free(pkey);
-      return 0;
-    }
+  if (pubkey->type == EVP_PKEY_EC &&
+      !ssl_cert_check_digital_signature_key_usage(&cert_cbs)) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
+    EVP_PKEY_free(pubkey);
+    return 0;
   }
 
-  if (c->privatekey != NULL) {
+  if (cert->privatekey != NULL) {
     /* Sanity-check that the private key and the certificate match, unless the
      * key is opaque (in case of, say, a smartcard). */
-    if (!EVP_PKEY_is_opaque(c->privatekey) &&
-        !X509_check_private_key(x, c->privatekey)) {
+    if (!EVP_PKEY_is_opaque(cert->privatekey) &&
+        !ssl_compare_public_and_private_key(pubkey, cert->privatekey)) {
       /* don't fail for a cert/key mismatch, just free current private key
        * (when switching to a different cert & key, first this function should
        * be used, then ssl_set_pkey */
-      EVP_PKEY_free(c->privatekey);
-      c->privatekey = NULL;
+      EVP_PKEY_free(cert->privatekey);
+      cert->privatekey = NULL;
       /* clear error queue */
       ERR_clear_error();
     }
   }
 
-  EVP_PKEY_free(pkey);
+  EVP_PKEY_free(pubkey);
 
-  X509_free(c->x509_leaf);
-  X509_up_ref(x);
-  c->x509_leaf = x;
+  ssl_cert_flush_cached_x509_leaf(cert);
+
+  if (cert->chain != NULL) {
+    CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(cert->chain, 0));
+    sk_CRYPTO_BUFFER_set(cert->chain, 0, buffer);
+    CRYPTO_BUFFER_up_ref(buffer);
+    return 1;
+  }
+
+  cert->chain = sk_CRYPTO_BUFFER_new_null();
+  if (cert->chain == NULL) {
+    return 0;
+  }
+
+  if (!sk_CRYPTO_BUFFER_push(cert->chain, buffer)) {
+    sk_CRYPTO_BUFFER_free(cert->chain);
+    cert->chain = NULL;
+    return 0;
+  }
+  CRYPTO_BUFFER_up_ref(buffer);
 
   return 1;
 }
 
 int SSL_CTX_use_certificate_ASN1(SSL_CTX *ctx, size_t der_len,
                                  const uint8_t *der) {
-  if (der_len > LONG_MAX) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_OVERFLOW);
+  CRYPTO_BUFFER *buffer = CRYPTO_BUFFER_new(der, der_len, NULL);
+  if (buffer == NULL) {
     return 0;
   }
 
-  const uint8_t *p = der;
-  X509 *x509 = d2i_X509(NULL, &p, (long)der_len);
-  if (x509 == NULL || p != der + der_len) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_ASN1_LIB);
-    X509_free(x509);
-    return 0;
-  }
-
-  int ret = SSL_CTX_use_certificate(ctx, x509);
-  X509_free(x509);
-  return ret;
+  const int ok = ssl_set_cert(ctx->cert, buffer);
+  CRYPTO_BUFFER_free(buffer);
+  return ok;
 }
 
 int SSL_CTX_use_RSAPrivateKey(SSL_CTX *ctx, RSA *rsa) {
diff --git a/src/ssl/ssl_session.c b/src/ssl/ssl_session.c
index 7adef1a..5dffc70 100644
--- a/src/ssl/ssl_session.c
+++ b/src/ssl/ssl_session.c
@@ -171,6 +171,7 @@
   session->verify_result = X509_V_ERR_INVALID_CALL;
   session->references = 1;
   session->timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  session->auth_timeout = SSL_DEFAULT_SESSION_TIMEOUT;
   session->time = (long)time(NULL);
   CRYPTO_new_ex_data(&session->ex_data);
   return session;
@@ -259,6 +260,7 @@
   new_session->peer_signature_algorithm = session->peer_signature_algorithm;
 
   new_session->timeout = session->timeout;
+  new_session->auth_timeout = session->auth_timeout;
   new_session->time = session->time;
 
   /* Copy non-authentication connection properties. */
@@ -303,7 +305,7 @@
   return 0;
 }
 
-void ssl_session_refresh_time(SSL *ssl, SSL_SESSION *session) {
+void ssl_session_rebase_time(SSL *ssl, SSL_SESSION *session) {
   struct timeval now;
   ssl_get_current_time(ssl, &now);
 
@@ -314,11 +316,12 @@
       now.tv_sec < 0) {
     session->time = now.tv_sec;
     session->timeout = 0;
+    session->auth_timeout = 0;
     return;
   }
 
-  /* Adjust the session time and timeout. If the session has already expired,
-   * clamp the timeout at zero. */
+  /* Adjust the session time and timeouts. If the session has already expired,
+   * clamp the timeouts at zero. */
   long delta = now.tv_sec - session->time;
   session->time = now.tv_sec;
   if (session->timeout < delta) {
@@ -326,6 +329,26 @@
   } else {
     session->timeout -= delta;
   }
+  if (session->auth_timeout < delta) {
+    session->auth_timeout = 0;
+  } else {
+    session->auth_timeout -= delta;
+  }
+}
+
+void ssl_session_renew_timeout(SSL *ssl, SSL_SESSION *session, long timeout) {
+  /* Rebase the timestamp relative to the current time so |timeout| is measured
+   * correctly. */
+  ssl_session_rebase_time(ssl, session);
+
+  if (session->timeout > timeout) {
+    return;
+  }
+
+  session->timeout = timeout;
+  if (session->timeout > session->auth_timeout) {
+    session->timeout = session->auth_timeout;
+  }
 }
 
 int SSL_SESSION_up_ref(SSL_SESSION *session) {
@@ -408,6 +431,7 @@
   }
 
   session->timeout = timeout;
+  session->auth_timeout = timeout;
   return 1;
 }
 
@@ -490,10 +514,21 @@
   ssl_get_current_time(ssl, &now);
   session->time = now.tv_sec;
 
-  session->timeout = ssl->session_timeout;
+  uint16_t version = ssl3_protocol_version(ssl);
+  if (version >= TLS1_3_VERSION) {
+    /* TLS 1.3 uses tickets as authenticators, so we are willing to use them for
+     * longer. */
+    session->timeout = ssl->session_psk_dhe_timeout;
+    session->auth_timeout = SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
+  } else {
+    /* TLS 1.2 resumption does not incorporate new key material, so we use a
+     * much shorter timeout. */
+    session->timeout = ssl->session_timeout;
+    session->auth_timeout = ssl->session_timeout;
+  }
 
   if (is_server) {
-    if (hs->ticket_expected) {
+    if (hs->ticket_expected || version >= TLS1_3_VERSION) {
       /* Don't set session IDs for sessions resumed with tickets. This will keep
        * them out of the session cache. */
       session->session_id_length = 0;
@@ -934,6 +969,11 @@
     return 0;
   }
 
+  /* Historically, zero was treated as |SSL_DEFAULT_SESSION_TIMEOUT|. */
+  if (timeout == 0) {
+    timeout = SSL_DEFAULT_SESSION_TIMEOUT;
+  }
+
   long old_timeout = ctx->session_timeout;
   ctx->session_timeout = timeout;
   return old_timeout;
@@ -947,12 +987,20 @@
   return ctx->session_timeout;
 }
 
+void SSL_CTX_set_session_psk_dhe_timeout(SSL_CTX *ctx, long timeout) {
+  ctx->session_psk_dhe_timeout = timeout;
+}
+
 long SSL_set_session_timeout(SSL *ssl, long timeout) {
   long old_timeout = ssl->session_timeout;
   ssl->session_timeout = timeout;
   return old_timeout;
 }
 
+void SSL_set_session_psk_dhe_timeout(SSL *ssl, long timeout) {
+  ssl->session_psk_dhe_timeout = timeout;
+}
+
 typedef struct timeout_param_st {
   SSL_CTX *ctx;
   long time;
diff --git a/src/ssl/ssl_stat.c b/src/ssl/ssl_stat.c
index d657788..09a43d1 100644
--- a/src/ssl/ssl_stat.c
+++ b/src/ssl/ssl_stat.c
@@ -104,9 +104,6 @@
     case SSL3_ST_CW_CLNT_HELLO_A:
       return "SSLv3 write client hello A";
 
-    case SSL3_ST_CW_CLNT_HELLO_B:
-      return "SSLv3 write client hello B";
-
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "SSLv3 read server hello A";
 
@@ -128,15 +125,9 @@
     case SSL3_ST_CW_CERT_A:
       return "SSLv3 write client certificate A";
 
-    case SSL3_ST_CW_CERT_B:
-      return "SSLv3 write client certificate B";
-
     case SSL3_ST_CW_KEY_EXCH_A:
       return "SSLv3 write client key exchange A";
 
-    case SSL3_ST_CW_KEY_EXCH_B:
-      return "SSLv3 write client key exchange B";
-
     case SSL3_ST_CW_CERT_VRFY_A:
       return "SSLv3 write certificate verify A";
 
@@ -151,10 +142,6 @@
     case SSL3_ST_SW_FINISHED_A:
       return "SSLv3 write finished A";
 
-    case SSL3_ST_CW_FINISHED_B:
-    case SSL3_ST_SW_FINISHED_B:
-      return "SSLv3 write finished B";
-
     case SSL3_ST_CR_CHANGE:
     case SSL3_ST_SR_CHANGE:
       return "SSLv3 read change cipher spec";
@@ -188,39 +175,21 @@
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "SSLv3 write server hello A";
 
-    case SSL3_ST_SW_SRVR_HELLO_B:
-      return "SSLv3 write server hello B";
-
     case SSL3_ST_SW_CERT_A:
       return "SSLv3 write certificate A";
 
-    case SSL3_ST_SW_CERT_B:
-      return "SSLv3 write certificate B";
-
     case SSL3_ST_SW_KEY_EXCH_A:
       return "SSLv3 write key exchange A";
 
-    case SSL3_ST_SW_KEY_EXCH_B:
-      return "SSLv3 write key exchange B";
-
     case SSL3_ST_SW_CERT_REQ_A:
       return "SSLv3 write certificate request A";
 
-    case SSL3_ST_SW_CERT_REQ_B:
-      return "SSLv3 write certificate request B";
-
     case SSL3_ST_SW_SESSION_TICKET_A:
       return "SSLv3 write session ticket A";
 
-    case SSL3_ST_SW_SESSION_TICKET_B:
-      return "SSLv3 write session ticket B";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "SSLv3 write server done A";
 
-    case SSL3_ST_SW_SRVR_DONE_B:
-      return "SSLv3 write server done B";
-
     case SSL3_ST_SR_CERT_A:
       return "SSLv3 read client certificate A";
 
@@ -261,9 +230,6 @@
     case SSL3_ST_CW_CLNT_HELLO_A:
       return "3WCH_A";
 
-    case SSL3_ST_CW_CLNT_HELLO_B:
-      return "3WCH_B";
-
     case SSL3_ST_CR_SRVR_HELLO_A:
       return "3RSH_A";
 
@@ -282,15 +248,9 @@
     case SSL3_ST_CW_CERT_A:
       return "3WCC_A";
 
-    case SSL3_ST_CW_CERT_B:
-      return "3WCC_B";
-
     case SSL3_ST_CW_KEY_EXCH_A:
       return "3WCKEA";
 
-    case SSL3_ST_CW_KEY_EXCH_B:
-      return "3WCKEB";
-
     case SSL3_ST_CW_CERT_VRFY_A:
       return "3WCV_A";
 
@@ -305,10 +265,6 @@
     case SSL3_ST_CW_FINISHED_A:
       return "3WFINA";
 
-    case SSL3_ST_SW_FINISHED_B:
-    case SSL3_ST_CW_FINISHED_B:
-      return "3WFINB";
-
     case SSL3_ST_CR_CHANGE:
     case SSL3_ST_SR_CHANGE:
       return "3RCCS_";
@@ -338,15 +294,9 @@
     case SSL3_ST_SW_SRVR_HELLO_A:
       return "3WSH_A";
 
-    case SSL3_ST_SW_SRVR_HELLO_B:
-      return "3WSH_B";
-
     case SSL3_ST_SW_CERT_A:
       return "3WSC_A";
 
-    case SSL3_ST_SW_CERT_B:
-      return "3WSC_B";
-
     case SSL3_ST_SW_KEY_EXCH_A:
       return "3WSKEA";
 
@@ -356,15 +306,9 @@
     case SSL3_ST_SW_CERT_REQ_A:
       return "3WCR_A";
 
-    case SSL3_ST_SW_CERT_REQ_B:
-      return "3WCR_B";
-
     case SSL3_ST_SW_SRVR_DONE_A:
       return "3WSD_A";
 
-    case SSL3_ST_SW_SRVR_DONE_B:
-      return "3WSD_B";
-
     case SSL3_ST_SR_CERT_A:
       return "3RCC_A";
 
diff --git a/src/ssl/ssl_test.cc b/src/ssl/ssl_test.cc
index 22a9b7b..aa265c8 100644
--- a/src/ssl/ssl_test.cc
+++ b/src/ssl/ssl_test.cc
@@ -2154,6 +2154,11 @@
   *out_clock = g_current_time;
 }
 
+static void FrozenTimeCallback(const SSL *ssl, timeval *out_clock) {
+  out_clock->tv_sec = 1000;
+  out_clock->tv_usec = 0;
+}
+
 static int RenewTicketCallback(SSL *ssl, uint8_t *key_name, uint8_t *iv,
                                EVP_CIPHER_CTX *ctx, HMAC_CTX *hmac_ctx,
                                int encrypt) {
@@ -2221,9 +2226,15 @@
   }
 
   for (bool server_test : std::vector<bool>{false, true}) {
-    static const int kStartTime = 1000;
+    static const time_t kStartTime = 1000;
     g_current_time.tv_sec = kStartTime;
 
+    // We are willing to use a longer lifetime for TLS 1.3 sessions as
+    // resumptions still perform ECDHE.
+    const time_t timeout = version == TLS1_3_VERSION
+                               ? SSL_DEFAULT_SESSION_PSK_DHE_TIMEOUT
+                               : SSL_DEFAULT_SESSION_TIMEOUT;
+
     bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method));
     bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(method));
     if (!server_ctx || !client_ctx ||
@@ -2239,11 +2250,14 @@
     SSL_CTX_set_session_cache_mode(client_ctx.get(), SSL_SESS_CACHE_BOTH);
     SSL_CTX_set_session_cache_mode(server_ctx.get(), SSL_SESS_CACHE_BOTH);
 
-    // Both client and server must enforce session timeouts.
+    // Both client and server must enforce session timeouts. We configure the
+    // other side with a frozen clock so it never expires tickets.
     if (server_test) {
+      SSL_CTX_set_current_time_cb(client_ctx.get(), FrozenTimeCallback);
       SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
     } else {
       SSL_CTX_set_current_time_cb(client_ctx.get(), CurrentTimeCallback);
+      SSL_CTX_set_current_time_cb(server_ctx.get(), FrozenTimeCallback);
     }
 
     // Configure a ticket callback which renews tickets.
@@ -2257,7 +2271,7 @@
     }
 
     // Advance the clock just behind the timeout.
-    g_current_time.tv_sec += SSL_DEFAULT_SESSION_TIMEOUT - 1;
+    g_current_time.tv_sec += timeout - 1;
 
     if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(), session.get(),
                              true /* expect session reused */)) {
@@ -2289,7 +2303,8 @@
     }
 
     // Renew the session 10 seconds before expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 10;
+    time_t new_start_time = kStartTime + timeout - 10;
+    g_current_time.tv_sec = new_start_time;
     bssl::UniquePtr<SSL_SESSION> new_session =
         ExpectSessionRenewed(client_ctx.get(), server_ctx.get(), session.get());
     if (!new_session) {
@@ -2319,23 +2334,76 @@
       return false;
     }
 
-    // The new session is usable just before the old expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT - 1;
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                             new_session.get(),
-                             true /* expect session reused */)) {
-      fprintf(stderr, "Error resuming renewed session.\n");
-      return false;
-    }
+    if (version == TLS1_3_VERSION) {
+      // Renewal incorporates fresh key material in TLS 1.3, so we extend the
+      // lifetime TLS 1.3.
+      g_current_time.tv_sec = new_start_time + timeout - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
 
-    // Renewal does not extend the lifetime, so it is not usable beyond the
-    // old expiration.
-    g_current_time.tv_sec = kStartTime + SSL_DEFAULT_SESSION_TIMEOUT + 1;
-    if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
-                             new_session.get(),
-                             false /* expect session not reused */)) {
-      fprintf(stderr, "Renewed session's lifetime is too long.\n");
-      return false;
+      // The new session expires after the new timeout.
+      g_current_time.tv_sec = new_start_time + timeout + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session ot reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
+
+      // Renew the session until it begins just past the auth timeout.
+      time_t auth_end_time = kStartTime + SSL_DEFAULT_SESSION_AUTH_TIMEOUT;
+      while (new_start_time < auth_end_time - 1000) {
+        // Get as close as possible to target start time.
+        new_start_time =
+            std::min(auth_end_time - 1000, new_start_time + timeout - 1);
+        g_current_time.tv_sec = new_start_time;
+        new_session = ExpectSessionRenewed(client_ctx.get(), server_ctx.get(),
+                                           new_session.get());
+        if (!new_session) {
+          fprintf(stderr, "Error renewing session.\n");
+          return false;
+        }
+      }
+
+      // Now the session's lifetime is bound by the auth timeout.
+      g_current_time.tv_sec = auth_end_time - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
+
+      g_current_time.tv_sec = auth_end_time + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session ot reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
+    } else {
+      // The new session is usable just before the old expiration.
+      g_current_time.tv_sec = kStartTime + timeout - 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               true /* expect session reused */)) {
+        fprintf(stderr, "Error resuming renewed session.\n");
+        return false;
+      }
+
+      // Renewal does not extend the lifetime, so it is not usable beyond the
+      // old expiration.
+      g_current_time.tv_sec = kStartTime + timeout + 1;
+      if (!ExpectSessionReused(client_ctx.get(), server_ctx.get(),
+                               new_session.get(),
+                               false /* expect session not reused */)) {
+        fprintf(stderr, "Renewed session's lifetime is too long.\n");
+        return false;
+      }
     }
   }
 
@@ -2348,9 +2416,20 @@
   return 1;
 }
 
+static int SetSessionTimeoutCallbackTLS13(SSL *ssl, void *arg) {
+  long timeout = *(long *) arg;
+  SSL_set_session_psk_dhe_timeout(ssl, timeout);
+  return 1;
+}
+
 static bool TestSessionTimeoutCertCallback(bool is_dtls,
                                            const SSL_METHOD *method,
                                            uint16_t version) {
+  if (version == TLS1_3_VERSION) {
+    // |SSL_set_session_timeout| only applies to TLS 1.2 style resumption.
+    return true;
+  }
+
   static const int kStartTime = 1000;
   g_current_time.tv_sec = kStartTime;
 
@@ -2378,7 +2457,12 @@
   SSL_CTX_set_current_time_cb(server_ctx.get(), CurrentTimeCallback);
 
   long timeout = 25;
-  SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+  if (version == TLS1_3_VERSION) {
+    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallbackTLS13,
+                        &timeout);
+  } else {
+    SSL_CTX_set_cert_cb(server_ctx.get(), SetSessionTimeoutCallback, &timeout);
+  }
 
   bssl::UniquePtr<SSL_SESSION> session =
       CreateClientSession(client_ctx.get(), server_ctx.get());
@@ -2428,7 +2512,11 @@
   timeout = 25;
   g_current_time.tv_sec = kStartTime;
 
-  SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+  if (version == TLS1_3_VERSION) {
+    SSL_CTX_set_session_psk_dhe_timeout(server_ctx.get(), timeout - 10);
+  } else {
+    SSL_CTX_set_timeout(server_ctx.get(), timeout - 10);
+  }
 
   bssl::UniquePtr<SSL_SESSION> ctx_and_cb_session =
       CreateClientSession(client_ctx.get(), server_ctx.get());
@@ -3142,6 +3230,61 @@
   return true;
 }
 
+TEST(SSLTest, AddChainCertHack) {
+  // Ensure that we don't accidently break the hack that we have in place to
+  // keep curl and serf happy when they use an |X509| even after transfering
+  // ownership.
+
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  X509 *cert = GetTestCertificate().release();
+  ASSERT_TRUE(cert);
+  SSL_CTX_add0_chain_cert(ctx.get(), cert);
+
+  // This should not trigger a use-after-free.
+  X509_cmp(cert, cert);
+}
+
+TEST(SSLTest, GetCertificate) {
+  bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+  ASSERT_TRUE(ctx);
+  bssl::UniquePtr<X509> cert = GetTestCertificate();
+  ASSERT_TRUE(cert);
+  ASSERT_TRUE(SSL_CTX_use_certificate(ctx.get(), cert.get()));
+  bssl::UniquePtr<SSL> ssl(SSL_new(ctx.get()));
+  ASSERT_TRUE(ssl);
+
+  X509 *cert2 = SSL_CTX_get0_certificate(ctx.get());
+  ASSERT_TRUE(cert2);
+  X509 *cert3 = SSL_get_certificate(ssl.get());
+  ASSERT_TRUE(cert3);
+
+  // The old and new certificates must be identical.
+  EXPECT_EQ(0, X509_cmp(cert.get(), cert2));
+  EXPECT_EQ(0, X509_cmp(cert.get(), cert3));
+
+  uint8_t *der = nullptr;
+  long der_len = i2d_X509(cert.get(), &der);
+  ASSERT_LT(0, der_len);
+  bssl::UniquePtr<uint8_t> free_der(der);
+
+  uint8_t *der2 = nullptr;
+  long der2_len = i2d_X509(cert2, &der2);
+  ASSERT_LT(0, der2_len);
+  bssl::UniquePtr<uint8_t> free_der2(der2);
+
+  uint8_t *der3 = nullptr;
+  long der3_len = i2d_X509(cert3, &der3);
+  ASSERT_LT(0, der3_len);
+  bssl::UniquePtr<uint8_t> free_der3(der3);
+
+  // They must also encode identically.
+  ASSERT_EQ(der2_len, der_len);
+  EXPECT_EQ(0, OPENSSL_memcmp(der, der2, static_cast<size_t>(der_len)));
+  ASSERT_EQ(der3_len, der_len);
+  EXPECT_EQ(0, OPENSSL_memcmp(der, der3, static_cast<size_t>(der_len)));
+}
+
 // TODO(davidben): Convert this file to GTest properly.
 TEST(SSLTest, AllTests) {
   if (!TestCipherRules() ||
diff --git a/src/ssl/ssl_x509.c b/src/ssl/ssl_x509.c
index 3fc62b7..5d78deb 100644
--- a/src/ssl/ssl_x509.c
+++ b/src/ssl/ssl_x509.c
@@ -281,20 +281,21 @@
   X509_VERIFY_PARAM_set_depth(ctx->param, depth);
 }
 
-X509 *SSL_get_certificate(const SSL *ssl) {
-  if (ssl->cert != NULL) {
-    return ssl->cert->x509_leaf;
+static X509 *ssl_cert_get0_leaf(CERT *cert) {
+  if (cert->x509_leaf == NULL &&
+      !ssl_cert_cache_leaf_cert(cert)) {
+    return NULL;
   }
 
-  return NULL;
+  return cert->x509_leaf;
+}
+
+X509 *SSL_get_certificate(const SSL *ssl) {
+  return ssl_cert_get0_leaf(ssl->cert);
 }
 
 X509 *SSL_CTX_get0_certificate(const SSL_CTX *ctx) {
-  if (ctx->cert != NULL) {
-    return ctx->cert->x509_leaf;
-  }
-
-  return NULL;
+  return ssl_cert_get0_leaf(ctx->cert);
 }
 
 int SSL_CTX_set_default_verify_paths(SSL_CTX *ctx) {
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index ec5dce0..6fdef45 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -527,56 +527,6 @@
   return 0;
 }
 
-/* Get a mask of disabled algorithms: an algorithm is disabled if it isn't
- * supported or doesn't appear in supported signature algorithms. Unlike
- * ssl_cipher_get_disabled this applies to a specific session and not global
- * settings. */
-void ssl_set_client_disabled(SSL *ssl) {
-  CERT *c = ssl->cert;
-  int have_rsa = 0, have_ecdsa = 0;
-  c->mask_a = 0;
-  c->mask_k = 0;
-
-  /* Now go through all signature algorithms seeing if we support any for RSA or
-   * ECDSA. Do this for all versions not just TLS 1.2. */
-  const uint16_t *sigalgs;
-  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
-  for (size_t i = 0; i < num_sigalgs; i++) {
-    switch (sigalgs[i]) {
-      case SSL_SIGN_RSA_PSS_SHA512:
-      case SSL_SIGN_RSA_PSS_SHA384:
-      case SSL_SIGN_RSA_PSS_SHA256:
-      case SSL_SIGN_RSA_PKCS1_SHA512:
-      case SSL_SIGN_RSA_PKCS1_SHA384:
-      case SSL_SIGN_RSA_PKCS1_SHA256:
-      case SSL_SIGN_RSA_PKCS1_SHA1:
-        have_rsa = 1;
-        break;
-
-      case SSL_SIGN_ECDSA_SECP521R1_SHA512:
-      case SSL_SIGN_ECDSA_SECP384R1_SHA384:
-      case SSL_SIGN_ECDSA_SECP256R1_SHA256:
-      case SSL_SIGN_ECDSA_SHA1:
-        have_ecdsa = 1;
-        break;
-    }
-  }
-
-  /* Disable auth if we don't include any appropriate signature algorithms. */
-  if (!have_rsa) {
-    c->mask_a |= SSL_aRSA;
-  }
-  if (!have_ecdsa) {
-    c->mask_a |= SSL_aECDSA;
-  }
-
-  /* with PSK there must be client callback set */
-  if (!ssl->psk_client_callback) {
-    c->mask_a |= SSL_aPSK;
-    c->mask_k |= SSL_kPSK;
-  }
-}
-
 /* tls_extension represents a TLS extension that is handled internally. The
  * |init| function is called for each handshake, before any other functions of
  * the extension. Then the add and parse callbacks are called as needed.
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index 12f0b46..66a71a0 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1254,6 +1254,17 @@
     }
   }
 
+  if (!is_resume) {
+    if (config->expect_session_id && !GetTestState(ssl)->got_new_session) {
+      fprintf(stderr, "session was not cached on the server.\n");
+      return false;
+    }
+    if (config->expect_no_session_id && GetTestState(ssl)->got_new_session) {
+      fprintf(stderr, "session was unexpectedly cached on the server.\n");
+      return false;
+    }
+  }
+
   if (config->is_server && !GetTestState(ssl)->early_callback_called) {
     fprintf(stderr, "early callback not called\n");
     return false;
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 3afd73f..92f3e15 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -1132,6 +1132,19 @@
 	// data interleaved with the second ClientHello and the client Finished.
 	InterleaveEarlyData bool
 
+	// SendHalfRTTData causes a TLS 1.3 server to send the provided
+	// data in application data records before reading the client's
+	// Finished message.
+	SendHalfRTTData [][]byte
+
+	// ExpectHalfRTTData causes a TLS 1.3 client to read application
+	// data after reading the server's Finished message and before
+	// sending any other handshake messages. It checks that the
+	// application data it reads matches what is provided in
+	// ExpectHalfRTTData and errors if the number of records or their
+	// content do not match.
+	ExpectHalfRTTData [][]byte
+
 	// EmptyEncryptedExtensions, if true, causes the TLS 1.3 server to
 	// emit an empty EncryptedExtensions block.
 	EmptyEncryptedExtensions bool
@@ -1257,6 +1270,10 @@
 	// MaxReceivePlaintext, if non-zero, is the maximum plaintext record
 	// length accepted from the peer.
 	MaxReceivePlaintext int
+
+	// SendTicketLifetime, if non-zero, is the ticket lifetime to send in
+	// NewSessionTicket messages.
+	SendTicketLifetime time.Duration
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index 3cbd496..0cc0b38 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -38,6 +38,7 @@
 	haveVers             bool       // version has been negotiated
 	config               *Config    // configuration passed to constructor
 	handshakeComplete    bool
+	skipEarlyData        bool
 	didResume            bool // whether this connection was a session resumption
 	extendedMasterSecret bool // whether this session used an extended master secret
 	cipherSuite          *cipherSuite
@@ -726,6 +727,7 @@
 }
 
 func (c *Conn) doReadRecord(want recordType) (recordType, *block, error) {
+RestartReadRecord:
 	if c.isDTLS {
 		return c.dtlsDoReadRecord(want)
 	}
@@ -829,10 +831,24 @@
 	// Process message.
 	b, c.rawInput = c.in.splitBlock(b, recordHeaderLen+n)
 	ok, off, encTyp, alertValue := c.in.decrypt(b)
+
+	// Handle skipping over early data.
+	if !ok && c.skipEarlyData {
+		goto RestartReadRecord
+	}
+
+	// If the server is expecting a second ClientHello (in response to
+	// a HelloRetryRequest) and the client sends early data, there
+	// won't be a decryption failure but it still needs to be skipped.
+	if c.in.cipher == nil && typ == recordTypeApplicationData && c.skipEarlyData {
+		goto RestartReadRecord
+	}
+
 	if !ok {
 		return 0, nil, c.in.setErrorLocked(c.sendAlert(alertValue))
 	}
 	b.off = off
+	c.skipEarlyData = false
 
 	if c.vers >= VersionTLS13 && c.in.cipher != nil {
 		if typ != recordTypeApplicationData {
@@ -860,7 +876,7 @@
 			return c.in.setErrorLocked(errors.New("tls: handshake or ChangeCipherSpec requested after handshake complete"))
 		}
 	case recordTypeApplicationData:
-		if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart {
+		if !c.handshakeComplete && !c.config.Bugs.ExpectFalseStart && len(c.config.Bugs.ExpectHalfRTTData) == 0 {
 			c.sendAlert(alertInternalError)
 			return c.in.setErrorLocked(errors.New("tls: application data record requested before handshake complete"))
 		}
@@ -1779,6 +1795,10 @@
 		ticketAgeAdd:           ticketAgeAdd,
 	}
 
+	if c.config.Bugs.SendTicketLifetime != 0 {
+		m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+	}
+
 	state := sessionState{
 		vers:               c.vers,
 		cipherSuite:        c.cipherSuite.id,
diff --git a/src/ssl/test/runner/handshake_client.go b/src/ssl/test/runner/handshake_client.go
index 507ea40..02daa78 100644
--- a/src/ssl/test/runner/handshake_client.go
+++ b/src/ssl/test/runner/handshake_client.go
@@ -835,6 +835,20 @@
 	hs.finishedHash.addEntropy(zeroSecret)
 	clientTrafficSecret := hs.finishedHash.deriveSecret(clientApplicationTrafficLabel)
 	serverTrafficSecret := hs.finishedHash.deriveSecret(serverApplicationTrafficLabel)
+	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
+
+	// If we're expecting 0.5-RTT messages from the server, read them
+	// now.
+	for _, expectedMsg := range c.config.Bugs.ExpectHalfRTTData {
+		if err := c.readRecord(recordTypeApplicationData); err != nil {
+			return err
+		}
+		if !bytes.Equal(c.input.data[c.input.off:], expectedMsg) {
+			return errors.New("ExpectHalfRTTData: did not get expected message")
+		}
+		c.in.freeBlock(c.input)
+		c.input = nil
+	}
 
 	if certReq != nil && !c.config.Bugs.SkipClientCertificate {
 		certMsg := &certificateMsg{
@@ -919,7 +933,6 @@
 
 	// Switch to application data keys.
 	c.out.useTrafficSecret(c.vers, hs.suite, clientTrafficSecret, clientWrite)
-	c.in.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
 	c.exporterSecret = hs.finishedHash.deriveSecret(exporterLabel)
 	c.resumptionSecret = hs.finishedHash.deriveSecret(resumptionLabel)
diff --git a/src/ssl/test/runner/handshake_server.go b/src/ssl/test/runner/handshake_server.go
index 1116d6c..38925e9 100644
--- a/src/ssl/test/runner/handshake_server.go
+++ b/src/ssl/test/runner/handshake_server.go
@@ -509,6 +509,12 @@
 		}
 	}
 
+	// Decide whether or not to accept early data.
+	if hs.clientHello.hasEarlyData {
+		// For now, we'll reject and skip early data.
+		c.skipEarlyData = true
+	}
+
 	// Resolve PSK and compute the early secret.
 	if hs.sessionState != nil {
 		hs.finishedHash.addEntropy(hs.sessionState.masterSecret)
@@ -852,6 +858,13 @@
 	// from the client certificate are sent over these keys.
 	c.out.useTrafficSecret(c.vers, hs.suite, serverTrafficSecret, serverWrite)
 
+	// Send 0.5-RTT messages.
+	for _, halfRTTMsg := range config.Bugs.SendHalfRTTData {
+		if _, err := c.writeRecord(recordTypeApplicationData, halfRTTMsg); err != nil {
+			return err
+		}
+	}
+
 	// If we requested a client certificate, then the client must send a
 	// certificate message, even if it's empty.
 	if config.ClientAuth >= RequestClientCert {
@@ -1668,6 +1681,9 @@
 	}
 
 	m := new(newSessionTicketMsg)
+	if c.config.Bugs.SendTicketLifetime != 0 {
+		m.ticketLifetime = uint32(c.config.Bugs.SendTicketLifetime / time.Second)
+	}
 
 	if !c.config.Bugs.SendEmptySessionTicket {
 		var err error
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index 54bfca5..4e08200 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2349,7 +2349,7 @@
 				},
 			},
 			shouldFail:         true,
-			expectedError:      ":NO_COMPRESSION_SPECIFIED:",
+			expectedError:      ":INVALID_COMPRESSION_LIST:",
 			expectedLocalError: "remote error: illegal parameter",
 		},
 		{
@@ -3417,6 +3417,7 @@
 			},
 		},
 		resumeSession: true,
+		flags:         []string{"-expect-no-session-id"},
 	})
 	tests = append(tests, testCase{
 		testType: serverTest,
@@ -3426,6 +3427,7 @@
 			SessionTicketsDisabled: true,
 		},
 		resumeSession: true,
+		flags:         []string{"-expect-session-id"},
 	})
 	tests = append(tests, testCase{
 		testType: serverTest,
@@ -3467,6 +3469,9 @@
 			},
 			resumeSession:        true,
 			resumeRenewedSession: true,
+			// TLS 1.3 uses tickets, so the session should not be
+			// cached statefully.
+			flags: []string{"-expect-no-session-id"},
 		})
 
 		tests = append(tests, testCase{
@@ -8443,10 +8448,23 @@
 			},
 		},
 		flags: []string{
+			"-enable-early-data",
 			"-expect-early-data-info",
 		},
 	})
 
+	// Test that 0-RTT tickets are ignored in clients unless opted in.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-SendTicketEarlyDataInfo-Disabled",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketEarlyDataInfo: 16384,
+			},
+		},
+	})
+
 	testCases = append(testCases, testCase{
 		testType: clientTest,
 		name:     "TLS13-DuplicateTicketEarlyDataInfo",
@@ -8475,6 +8493,40 @@
 			"-enable-early-data",
 		},
 	})
+
+	// Test that, in TLS 1.3, the server-offered NewSessionTicket lifetime
+	// is honored.
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-HonorServerSessionTicketLifetime",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketLifetime: 20 * time.Second,
+			},
+		},
+		flags: []string{
+			"-resumption-delay", "19",
+		},
+		resumeSession: true,
+	})
+	testCases = append(testCases, testCase{
+		testType: clientTest,
+		name:     "TLS13-HonorServerSessionTicketLifetime-2",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				SendTicketLifetime: 20 * time.Second,
+				// The client should not offer the expired session.
+				ExpectNoTLS13PSK: true,
+			},
+		},
+		flags: []string{
+			"-resumption-delay", "21",
+		},
+		resumeSession: true,
+		expectResumeRejected: true,
+	})
 }
 
 func addChangeCipherSpecTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index 5bc9544..b729f69 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -123,6 +123,8 @@
     &TestConfig::expect_secure_renegotiation },
   { "-expect-no-secure-renegotiation",
     &TestConfig::expect_no_secure_renegotiation },
+  { "-expect-session-id", &TestConfig::expect_session_id },
+  { "-expect-no-session-id", &TestConfig::expect_no_session_id },
 };
 
 const Flag<std::string> kStringFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 7cf0a6f..7122856 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -131,6 +131,8 @@
   bool expect_no_secure_renegotiation = false;
   int max_send_fragment = 0;
   int read_size = 0;
+  bool expect_session_id = false;
+  bool expect_no_session_id = false;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/ssl/tls13_both.c b/src/ssl/tls13_both.c
index 1425665..a49ee14 100644
--- a/src/ssl/tls13_both.c
+++ b/src/ssl/tls13_both.c
@@ -58,15 +58,7 @@
       }
 
       case ssl_hs_read_message: {
-        int ret = ssl->method->ssl_get_message(ssl, -1, ssl_dont_hash_message);
-        if (ret <= 0) {
-          return ret;
-        }
-        break;
-      }
-
-      case ssl_hs_write_message: {
-        int ret = ssl->method->write_message(ssl);
+        int ret = ssl->method->ssl_get_message(ssl);
         if (ret <= 0) {
           return ret;
         }
@@ -406,18 +398,6 @@
   return ret;
 }
 
-int tls13_check_message_type(SSL *ssl, int type) {
-  if (ssl->s3->tmp.message_type != type) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNEXPECTED_MESSAGE);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_MESSAGE);
-    ERR_add_error_dataf("got type %d, wanted type %d",
-                        ssl->s3->tmp.message_type, type);
-    return 0;
-  }
-
-  return 1;
-}
-
 int tls13_process_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
@@ -441,7 +421,7 @@
   return 1;
 }
 
-int tls13_prepare_certificate(SSL_HANDSHAKE *hs) {
+int tls13_add_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   CBB cbb, body, certificate_list;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CERTIFICATE) ||
@@ -453,7 +433,7 @@
   }
 
   if (!ssl_has_certificate(ssl)) {
-    if (!ssl_complete_message(ssl, &cbb)) {
+    if (!ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
 
@@ -461,9 +441,11 @@
   }
 
   CERT *cert = ssl->cert;
+  CRYPTO_BUFFER *leaf_buf = sk_CRYPTO_BUFFER_value(cert->chain, 0);
   CBB leaf, extensions;
   if (!CBB_add_u24_length_prefixed(&certificate_list, &leaf) ||
-      !ssl_add_cert_to_cbb(&leaf, cert->x509_leaf) ||
+      !CBB_add_bytes(&leaf, CRYPTO_BUFFER_data(leaf_buf),
+                     CRYPTO_BUFFER_len(leaf_buf)) ||
       !CBB_add_u16_length_prefixed(&certificate_list, &extensions)) {
     OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
     goto err;
@@ -497,17 +479,19 @@
     }
   }
 
-  for (size_t i = 0; i < sk_X509_num(cert->x509_chain); i++) {
+  for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(cert->chain); i++) {
+    CRYPTO_BUFFER *cert_buf = sk_CRYPTO_BUFFER_value(cert->chain, i);
     CBB child;
     if (!CBB_add_u24_length_prefixed(&certificate_list, &child) ||
-        !ssl_add_cert_to_cbb(&child, sk_X509_value(cert->x509_chain, i)) ||
+        !CBB_add_bytes(&child, CRYPTO_BUFFER_data(cert_buf),
+                       CRYPTO_BUFFER_len(cert_buf)) ||
         !CBB_add_u16(&certificate_list, 0 /* no extensions */)) {
       OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
       goto err;
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -518,8 +502,8 @@
   return 0;
 }
 
-enum ssl_private_key_result_t tls13_prepare_certificate_verify(
-    SSL_HANDSHAKE *hs, int is_first_run) {
+enum ssl_private_key_result_t tls13_add_certificate_verify(SSL_HANDSHAKE *hs,
+                                                           int is_first_run) {
   SSL *const ssl = hs->ssl;
   enum ssl_private_key_result_t ret = ssl_private_key_failure;
   uint8_t *msg = NULL;
@@ -569,7 +553,7 @@
   }
 
   if (!CBB_did_write(&child, sig_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
@@ -581,7 +565,7 @@
   return ret;
 }
 
-int tls13_prepare_finished(SSL_HANDSHAKE *hs) {
+int tls13_add_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
   size_t verify_data_len;
   uint8_t verify_data[EVP_MAX_MD_SIZE];
@@ -595,7 +579,7 @@
   CBB cbb, body;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_FINISHED) ||
       !CBB_add_bytes(&body, verify_data, verify_data_len) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return 0;
   }
diff --git a/src/ssl/tls13_client.c b/src/ssl/tls13_client.c
index 6f2bb21..74d3646 100644
--- a/src/ssl/tls13_client.c
+++ b/src/ssl/tls13_client.c
@@ -15,6 +15,7 @@
 #include <openssl/ssl.h>
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #include <openssl/bytestring.h>
@@ -31,7 +32,6 @@
 enum client_hs_state_t {
   state_process_hello_retry_request = 0,
   state_send_second_client_hello,
-  state_flush_second_client_hello,
   state_process_server_hello,
   state_process_encrypted_extensions,
   state_process_certificate_request,
@@ -41,9 +41,7 @@
   state_send_client_certificate,
   state_send_client_certificate_verify,
   state_complete_client_certificate_verify,
-  state_send_channel_id,
-  state_send_client_finished,
-  state_flush,
+  state_complete_second_flight,
   state_done,
 };
 
@@ -137,6 +135,10 @@
     hs->retry_group = group_id;
   }
 
+  if (!ssl_hash_current_message(ssl)) {
+    return ssl_hs_error;
+  }
+
   hs->received_hello_retry_request = 1;
   hs->tls13_state = state_send_second_client_hello;
   return ssl_hs_ok;
@@ -147,18 +149,13 @@
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_flush_second_client_hello;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush_second_client_hello(SSL_HANDSHAKE *hs) {
   hs->tls13_state = state_process_server_hello;
   return ssl_hs_flush_and_read_message;
 }
 
 static enum ssl_hs_wait_t do_process_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_SERVER_HELLO)) {
     return ssl_hs_error;
   }
 
@@ -261,6 +258,10 @@
       return ssl_hs_error;
     }
     ssl_set_session(ssl, NULL);
+
+    /* Resumption incorporates fresh key material, so refresh the timeout. */
+    ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+                              ssl->session_psk_dhe_timeout);
   } else if (!ssl_get_new_session(hs, 0)) {
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
     return ssl_hs_error;
@@ -325,14 +326,8 @@
     ssl->s3->short_header = 1;
   }
 
-  /* If there was no HelloRetryRequest, the version negotiation logic has
-   * already hashed the message. */
-  if (hs->received_hello_retry_request &&
-      !ssl_hash_current_message(ssl)) {
-    return ssl_hs_error;
-  }
-
-  if (!tls13_derive_handshake_secrets(hs) ||
+  if (!ssl_hash_current_message(ssl) ||
+      !tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->server_handshake_secret,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_handshake_secret,
@@ -346,7 +341,7 @@
 
 static enum ssl_hs_wait_t do_process_encrypted_extensions(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_ENCRYPTED_EXTENSIONS)) {
     return ssl_hs_error;
   }
 
@@ -428,7 +423,7 @@
 
 static enum ssl_hs_wait_t do_process_server_certificate(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, 0 /* certificate required */) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -441,7 +436,7 @@
 static enum ssl_hs_wait_t do_process_server_certificate_verify(
     SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -453,7 +448,7 @@
 
 static enum ssl_hs_wait_t do_process_server_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* Update the secret to the master secret and derive traffic keys. */
@@ -471,7 +466,7 @@
   SSL *const ssl = hs->ssl;
   /* The peer didn't request a certificate. */
   if (!hs->cert_request) {
-    hs->tls13_state = state_send_channel_id;
+    hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
 
@@ -490,12 +485,12 @@
   }
 
   if (!ssl_auto_chain_if_needed(ssl) ||
-      !tls13_prepare_certificate(hs)) {
+      !tls13_add_certificate(hs)) {
     return ssl_hs_error;
   }
 
   hs->tls13_state = state_send_client_certificate_verify;
-  return ssl_hs_write_message;
+  return ssl_hs_ok;
 }
 
 static enum ssl_hs_wait_t do_send_client_certificate_verify(SSL_HANDSHAKE *hs,
@@ -503,14 +498,14 @@
   SSL *const ssl = hs->ssl;
   /* Don't send CertificateVerify if there is no certificate. */
   if (!ssl_has_certificate(ssl)) {
-    hs->tls13_state = state_send_channel_id;
+    hs->tls13_state = state_complete_second_flight;
     return ssl_hs_ok;
   }
 
-  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
+  switch (tls13_add_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
-      hs->tls13_state = state_send_channel_id;
-      return ssl_hs_write_message;
+      hs->tls13_state = state_complete_second_flight;
+      return ssl_hs_ok;
 
     case ssl_private_key_retry:
       hs->tls13_state = state_complete_client_certificate_verify;
@@ -524,44 +519,35 @@
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_channel_id(SSL_HANDSHAKE *hs) {
+static enum ssl_hs_wait_t do_complete_second_flight(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!ssl->s3->tlsext_channel_id_valid) {
-    hs->tls13_state = state_send_client_finished;
-    return ssl_hs_ok;
+
+  /* Send a Channel ID assertion if necessary. */
+  if (ssl->s3->tlsext_channel_id_valid) {
+    if (!ssl_do_channel_id_callback(ssl)) {
+      hs->tls13_state = state_complete_second_flight;
+      return ssl_hs_error;
+    }
+
+    if (ssl->tlsext_channel_id_private == NULL) {
+      return ssl_hs_channel_id_lookup;
+    }
+
+    CBB cbb, body;
+    if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
+        !tls1_write_channel_id(ssl, &body) ||
+        !ssl_add_message_cbb(ssl, &cbb)) {
+      CBB_cleanup(&cbb);
+      return ssl_hs_error;
+    }
   }
 
-  if (!ssl_do_channel_id_callback(ssl)) {
+  /* Send a Finished message. */
+  if (!tls13_add_finished(hs)) {
     return ssl_hs_error;
   }
 
-  if (ssl->tlsext_channel_id_private == NULL) {
-    return ssl_hs_channel_id_lookup;
-  }
-
-  CBB cbb, body;
-  if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_CHANNEL_ID) ||
-      !tls1_write_channel_id(ssl, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_send_client_finished;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_send_client_finished(SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_flush;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+  /* Derive the final keys and enable them. */
   if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->server_traffic_secret_0,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->client_traffic_secret_0,
@@ -585,9 +571,6 @@
       case state_send_second_client_hello:
         ret = do_send_second_client_hello(hs);
         break;
-      case state_flush_second_client_hello:
-        ret = do_flush_second_client_hello(hs);
-        break;
       case state_process_server_hello:
         ret = do_process_server_hello(hs);
         break;
@@ -615,14 +598,8 @@
       case state_complete_client_certificate_verify:
         ret = do_send_client_certificate_verify(hs, 0 /* complete */);
         break;
-      case state_send_channel_id:
-        ret = do_send_channel_id(hs);
-        break;
-      case state_send_client_finished:
-        ret = do_send_client_finished(hs);
-        break;
-      case state_flush:
-        ret = do_flush(hs);
+      case state_complete_second_flight:
+        ret = do_complete_second_flight(hs);
         break;
       case state_done:
         ret = ssl_hs_ok;
@@ -646,11 +623,12 @@
     return 0;
   }
 
-  ssl_session_refresh_time(ssl, session);
+  ssl_session_rebase_time(ssl, session);
 
+  uint32_t server_timeout;
   CBS cbs, ticket, extensions;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
-  if (!CBS_get_u32(&cbs, &session->tlsext_tick_lifetime_hint) ||
+  if (!CBS_get_u32(&cbs, &server_timeout) ||
       !CBS_get_u32(&cbs, &session->ticket_age_add) ||
       !CBS_get_u16_length_prefixed(&cbs, &ticket) ||
       !CBS_stow(&ticket, &session->tlsext_tick, &session->tlsext_ticklen) ||
@@ -661,6 +639,21 @@
     goto err;
   }
 
+  /* Cap the renewable lifetime by the server advertised value. This avoids
+   * wasting bandwidth on 0-RTT when we know the server will reject it.
+   *
+   * TODO(davidben): This dance where we're not sure if long or uint32_t is
+   * bigger is silly. session->timeout should not be a long to begin with.
+   * https://crbug.com/boringssl/155. */
+#if LONG_MAX < 0xffffffff
+  if (server_timeout > LONG_MAX) {
+    server_timeout = LONG_MAX;
+  }
+#endif
+  if (session->timeout > (long)server_timeout) {
+    session->timeout = (long)server_timeout;
+  }
+
   /* Parse out the extensions. */
   int have_early_data_info = 0;
   CBS early_data_info;
@@ -677,7 +670,7 @@
     goto err;
   }
 
-  if (have_early_data_info) {
+  if (have_early_data_info && ssl->ctx->enable_early_data) {
     if (!CBS_get_u32(&early_data_info, &session->ticket_max_early_data) ||
         CBS_len(&early_data_info) != 0) {
       ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
diff --git a/src/ssl/tls13_server.c b/src/ssl/tls13_server.c
index 750e47f..db5fb24 100644
--- a/src/ssl/tls13_server.c
+++ b/src/ssl/tls13_server.c
@@ -35,25 +35,18 @@
 static const size_t kMaxEarlyDataAccepted = 14336;
 
 enum server_hs_state_t {
-  state_process_client_hello = 0,
-  state_select_parameters,
+  state_select_parameters = 0,
   state_send_hello_retry_request,
-  state_flush_hello_retry_request,
   state_process_second_client_hello,
   state_send_server_hello,
-  state_send_encrypted_extensions,
-  state_send_certificate_request,
-  state_send_server_certificate,
   state_send_server_certificate_verify,
   state_complete_server_certificate_verify,
   state_send_server_finished,
-  state_flush,
   state_process_client_certificate,
   state_process_client_certificate_verify,
   state_process_channel_id,
   state_process_client_finished,
   state_send_new_session_ticket,
-  state_flush_new_session_tickets,
   state_done,
 };
 
@@ -94,54 +87,6 @@
   return ok;
 }
 
-static enum ssl_hs_wait_t do_process_client_hello(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
-    return ssl_hs_error;
-  }
-
-  SSL_CLIENT_HELLO client_hello;
-  if (!ssl_client_hello_init(ssl, &client_hello, ssl->init_msg,
-                             ssl->init_num)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_CLIENTHELLO_PARSE_FAILED);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
-    return ssl_hs_error;
-  }
-
-  assert(ssl->s3->have_version);
-
-  /* Load the client random. */
-  if (client_hello.random_len != SSL3_RANDOM_SIZE) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-    return ssl_hs_error;
-  }
-  OPENSSL_memcpy(ssl->s3->client_random, client_hello.random,
-                 client_hello.random_len);
-
-  /* TLS 1.3 requires the peer only advertise the null compression. */
-  if (client_hello.compression_methods_len != 1 ||
-      client_hello.compression_methods[0] != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_COMPRESSION_LIST);
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    return ssl_hs_error;
-  }
-
-  /* TLS extensions. */
-  if (!ssl_parse_clienthello_tlsext(hs, &client_hello)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_PARSE_TLSEXT);
-    return ssl_hs_error;
-  }
-
-  /* The short record header extension is incompatible with early data. */
-  if (ssl->s3->skip_early_data && ssl->s3->short_header) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_select_parameters;
-  return ssl_hs_ok;
-}
-
 static const SSL_CIPHER *choose_tls13_cipher(
     const SSL *ssl, const SSL_CLIENT_HELLO *client_hello) {
   if (client_hello->cipher_suites_len % 2 != 0) {
@@ -190,21 +135,9 @@
 
 static enum ssl_hs_wait_t do_select_parameters(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* Call |cert_cb| to update server certificates if required. */
-  if (ssl->cert->cert_cb != NULL) {
-    int rv = ssl->cert->cert_cb(ssl, ssl->cert->cert_cb_arg);
-    if (rv == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_CB_ERROR);
-      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
-      return ssl_hs_error;
-    }
-    if (rv < 0) {
-      hs->tls13_state = state_select_parameters;
-      return ssl_hs_x509_lookup;
-    }
-  }
-
-  if (!ssl_auto_chain_if_needed(ssl)) {
+  /* The short record header extension is incompatible with early data. */
+  if (ssl->s3->skip_early_data && ssl->s3->short_header) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
     return ssl_hs_error;
   }
 
@@ -287,6 +220,10 @@
     }
     ssl->s3->session_reused = 1;
     SSL_SESSION_free(session);
+
+    /* Resumption incorporates fresh key material, so refresh the timeout. */
+    ssl_session_renew_timeout(ssl, ssl->s3->new_session,
+                              ssl->session_psk_dhe_timeout);
   }
 
   if (ssl->ctx->dos_protection_cb != NULL &&
@@ -304,10 +241,12 @@
     return ssl_hs_error;
   }
 
-  /* The PRF hash is now known. Set up the key schedule. */
+  /* The PRF hash is now known. Set up the key schedule and hash the
+   * ClientHello. */
   size_t hash_len =
       EVP_MD_size(ssl_get_handshake_digest(ssl_get_algorithm_prf(ssl)));
-  if (!tls13_init_key_schedule(hs)) {
+  if (!tls13_init_key_schedule(hs) ||
+      !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
   }
 
@@ -349,23 +288,18 @@
       !CBB_add_u16(&extensions, TLSEXT_TYPE_key_share) ||
       !CBB_add_u16(&extensions, 2 /* length */) ||
       !CBB_add_u16(&extensions, group_id) ||
-      !ssl_complete_message(ssl, &cbb)) {
+      !ssl_add_message_cbb(ssl, &cbb)) {
     CBB_cleanup(&cbb);
     return ssl_hs_error;
   }
 
-  hs->tls13_state = state_flush_hello_retry_request;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush_hello_retry_request(SSL_HANDSHAKE *hs) {
   hs->tls13_state = state_process_second_client_hello;
   return ssl_hs_flush_and_read_message;
 }
 
 static enum ssl_hs_wait_t do_process_second_client_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
+  if (!ssl_check_message_type(ssl, SSL3_MT_CLIENT_HELLO)) {
     return ssl_hs_error;
   }
 
@@ -398,6 +332,8 @@
 
 static enum ssl_hs_wait_t do_send_server_hello(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
+
+  /* Send a ServerHello. */
   CBB cbb, body, extensions;
   if (!ssl->method->init_message(ssl, &cbb, &body, SSL3_MT_SERVER_HELLO) ||
       !CBB_add_u16(&body, ssl->version) ||
@@ -417,43 +353,27 @@
     }
   }
 
-  if (!ssl_complete_message(ssl, &cbb)) {
+  if (!ssl_add_message_cbb(ssl, &cbb)) {
     goto err;
   }
 
-  hs->tls13_state = state_send_encrypted_extensions;
-  return ssl_hs_write_message;
-
-err:
-  CBB_cleanup(&cbb);
-  return ssl_hs_error;
-}
-
-static enum ssl_hs_wait_t do_send_encrypted_extensions(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
+  /* Derive and enable the handshake traffic secrets. */
   if (!tls13_derive_handshake_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
                              hs->hash_len) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret,
                              hs->hash_len)) {
-    return ssl_hs_error;
+    goto err;
   }
 
-  CBB cbb, body;
+  /* Send EncryptedExtensions. */
   if (!ssl->method->init_message(ssl, &cbb, &body,
                                  SSL3_MT_ENCRYPTED_EXTENSIONS) ||
       !ssl_add_serverhello_tlsext(hs, &body) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    CBB_cleanup(&cbb);
-    return ssl_hs_error;
+      !ssl_add_message_cbb(ssl, &cbb)) {
+    goto err;
   }
 
-  hs->tls13_state = state_send_certificate_request;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_send_certificate_request(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
   /* Determine whether to request a client certificate. */
   hs->cert_request = !!(ssl->verify_mode & SSL_VERIFY_PEER);
   /* CertificateRequest may only be sent in non-resumption handshakes. */
@@ -461,71 +381,63 @@
     hs->cert_request = 0;
   }
 
-  if (!hs->cert_request) {
-    /* Skip this state. */
-    hs->tls13_state = state_send_server_certificate;
-    return ssl_hs_ok;
-  }
+  /* Send a CertificateRequest, if necessary. */
+  if (hs->cert_request) {
+    CBB sigalgs_cbb;
+    if (!ssl->method->init_message(ssl, &cbb, &body,
+                                   SSL3_MT_CERTIFICATE_REQUEST) ||
+        !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
+      goto err;
+    }
 
-  CBB cbb, body, sigalgs_cbb;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_CERTIFICATE_REQUEST) ||
-      !CBB_add_u8(&body, 0 /* no certificate_request_context. */)) {
-    goto err;
-  }
+    const uint16_t *sigalgs;
+    size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
+    if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
+      goto err;
+    }
 
-  const uint16_t *sigalgs;
-  size_t num_sigalgs = tls12_get_verify_sigalgs(ssl, &sigalgs);
-  if (!CBB_add_u16_length_prefixed(&body, &sigalgs_cbb)) {
-    goto err;
-  }
+    for (size_t i = 0; i < num_sigalgs; i++) {
+      if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
+        goto err;
+      }
+    }
 
-  for (size_t i = 0; i < num_sigalgs; i++) {
-    if (!CBB_add_u16(&sigalgs_cbb, sigalgs[i])) {
+    if (!ssl_add_client_CA_list(ssl, &body) ||
+        !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
+        !ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
   }
 
-  if (!ssl_add_client_CA_list(ssl, &body) ||
-      !CBB_add_u16(&body, 0 /* empty certificate_extensions. */) ||
-      !ssl_complete_message(ssl, &cbb)) {
-    goto err;
+  /* Send the server Certificate message, if necessary. */
+  if (!ssl->s3->session_reused) {
+    if (!ssl_has_certificate(ssl)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
+      goto err;
+    }
+
+    if (!tls13_add_certificate(hs)) {
+      goto err;
+    }
+
+    hs->tls13_state = state_send_server_certificate_verify;
+    return ssl_hs_ok;
   }
 
-  hs->tls13_state = state_send_server_certificate;
-  return ssl_hs_write_message;
+  hs->tls13_state = state_send_server_finished;
+  return ssl_hs_ok;
 
 err:
   CBB_cleanup(&cbb);
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_send_server_certificate(SSL_HANDSHAKE *hs) {
-  SSL *const ssl = hs->ssl;
-  if (ssl->s3->session_reused) {
-    hs->tls13_state = state_send_server_finished;
-    return ssl_hs_ok;
-  }
-
-  if (!ssl_has_certificate(ssl)) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_CERTIFICATE_SET);
-    return ssl_hs_error;
-  }
-
-  if (!tls13_prepare_certificate(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_send_server_certificate_verify;
-  return ssl_hs_write_message;
-}
-
 static enum ssl_hs_wait_t do_send_server_certificate_verify(SSL_HANDSHAKE *hs,
                                                             int is_first_run) {
-  switch (tls13_prepare_certificate_verify(hs, is_first_run)) {
+  switch (tls13_add_certificate_verify(hs, is_first_run)) {
     case ssl_private_key_success:
       hs->tls13_state = state_send_server_finished;
-      return ssl_hs_write_message;
+      return ssl_hs_ok;
 
     case ssl_private_key_retry:
       hs->tls13_state = state_complete_server_certificate_verify;
@@ -540,18 +452,10 @@
 }
 
 static enum ssl_hs_wait_t do_send_server_finished(SSL_HANDSHAKE *hs) {
-  if (!tls13_prepare_finished(hs)) {
-    return ssl_hs_error;
-  }
-
-  hs->tls13_state = state_flush;
-  return ssl_hs_write_message;
-}
-
-static enum ssl_hs_wait_t do_flush(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  /* Update the secret to the master secret and derive traffic keys. */
-  if (!tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
+  if (!tls13_add_finished(hs) ||
+      /* Update the secret to the master secret and derive traffic keys. */
+      !tls13_advance_key_schedule(hs, kZeroes, hs->hash_len) ||
       !tls13_derive_application_secrets(hs) ||
       !tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_traffic_secret_0,
                              hs->hash_len)) {
@@ -577,7 +481,7 @@
   const int allow_anonymous =
       (ssl->verify_mode & SSL_VERIFY_FAIL_IF_NO_PEER_CERT) == 0;
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE) ||
       !tls13_process_certificate(hs, allow_anonymous) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -596,7 +500,7 @@
     return ssl_hs_ok;
   }
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CERTIFICATE_VERIFY) ||
       !tls13_process_certificate_verify(hs) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -613,7 +517,7 @@
     return ssl_hs_ok;
   }
 
-  if (!tls13_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_CHANNEL_ID) ||
       !tls1_verify_channel_id(ssl) ||
       !ssl_hash_current_message(ssl)) {
     return ssl_hs_error;
@@ -625,7 +529,7 @@
 
 static enum ssl_hs_wait_t do_process_client_finished(SSL_HANDSHAKE *hs) {
   SSL *const ssl = hs->ssl;
-  if (!tls13_check_message_type(ssl, SSL3_MT_FINISHED) ||
+  if (!ssl_check_message_type(ssl, SSL3_MT_FINISHED) ||
       !tls13_process_finished(hs) ||
       !ssl_hash_current_message(ssl) ||
       /* evp_aead_seal keys have already been switched. */
@@ -637,18 +541,18 @@
 
   ssl->method->received_flight(ssl);
 
-  /* Refresh the session timestamp so that it is measured from ticket
+  /* Rebase the session timestamp so that it is measured from ticket
    * issuance. */
-  ssl_session_refresh_time(ssl, ssl->s3->new_session);
+  ssl_session_rebase_time(ssl, ssl->s3->new_session);
   hs->tls13_state = state_send_new_session_ticket;
   return ssl_hs_ok;
 }
 
-/* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
- * client makes several connections before getting a renewal. */
-static const int kNumTickets = 2;
-
 static enum ssl_hs_wait_t do_send_new_session_ticket(SSL_HANDSHAKE *hs) {
+  /* TLS 1.3 recommends single-use tickets, so issue multiple tickets in case the
+   * client makes several connections before getting a renewal. */
+  static const int kNumTickets = 2;
+
   SSL *const ssl = hs->ssl;
   /* If the client doesn't accept resumption with PSK_DHE_KE, don't send a
    * session ticket. */
@@ -658,95 +562,75 @@
   }
 
   SSL_SESSION *session = ssl->s3->new_session;
-  if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
-    goto err;
-  }
+  CBB cbb;
+  CBB_zero(&cbb);
 
-  CBB cbb, body, ticket, extensions;
-  if (!ssl->method->init_message(ssl, &cbb, &body,
-                                 SSL3_MT_NEW_SESSION_TICKET) ||
-      !CBB_add_u32(&body, session->timeout) ||
-      !CBB_add_u32(&body, session->ticket_age_add) ||
-      !CBB_add_u16_length_prefixed(&body, &ticket) ||
-      !ssl_encrypt_ticket(ssl, &ticket, session) ||
-      !CBB_add_u16_length_prefixed(&body, &extensions)) {
-    goto err;
-  }
+  for (int i = 0; i < kNumTickets; i++) {
+    if (!RAND_bytes((uint8_t *)&session->ticket_age_add, 4)) {
+      goto err;
+    }
 
-  if (ssl->ctx->enable_early_data) {
-    session->ticket_max_early_data = kMaxEarlyDataAccepted;
+    CBB body, ticket, extensions;
+    if (!ssl->method->init_message(ssl, &cbb, &body,
+                                   SSL3_MT_NEW_SESSION_TICKET) ||
+        !CBB_add_u32(&body, session->timeout) ||
+        !CBB_add_u32(&body, session->ticket_age_add) ||
+        !CBB_add_u16_length_prefixed(&body, &ticket) ||
+        !ssl_encrypt_ticket(ssl, &ticket, session) ||
+        !CBB_add_u16_length_prefixed(&body, &extensions)) {
+      goto err;
+    }
 
-    CBB early_data_info;
-    if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) ||
-        !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
-        !CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
-        !CBB_flush(&extensions)) {
+    if (ssl->ctx->enable_early_data) {
+      session->ticket_max_early_data = kMaxEarlyDataAccepted;
+
+      CBB early_data_info;
+      if (!CBB_add_u16(&extensions, TLSEXT_TYPE_ticket_early_data_info) ||
+          !CBB_add_u16_length_prefixed(&extensions, &early_data_info) ||
+          !CBB_add_u32(&early_data_info, session->ticket_max_early_data) ||
+          !CBB_flush(&extensions)) {
+        goto err;
+      }
+    }
+
+    /* Add a fake extension. See draft-davidben-tls-grease-01. */
+    if (!CBB_add_u16(&extensions,
+                     ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
+        !CBB_add_u16(&extensions, 0 /* empty */)) {
+      goto err;
+    }
+
+    if (!ssl_add_message_cbb(ssl, &cbb)) {
       goto err;
     }
   }
 
-  /* Add a fake extension. See draft-davidben-tls-grease-01. */
-  if (!CBB_add_u16(&extensions,
-                   ssl_get_grease_value(ssl, ssl_grease_ticket_extension)) ||
-      !CBB_add_u16(&extensions, 0 /* empty */)) {
-    goto err;
-  }
-
-  if (!ssl_complete_message(ssl, &cbb)) {
-    goto err;
-  }
-
   hs->session_tickets_sent++;
-  if (hs->session_tickets_sent >= kNumTickets) {
-    hs->tls13_state = state_flush_new_session_tickets;
-  } else {
-    hs->tls13_state = state_send_new_session_ticket;
-  }
-
-  return ssl_hs_write_message;
+  hs->tls13_state = state_done;
+  return ssl_hs_flush;
 
 err:
   CBB_cleanup(&cbb);
   return ssl_hs_error;
 }
 
-static enum ssl_hs_wait_t do_flush_new_session_tickets(SSL_HANDSHAKE *hs) {
-  hs->tls13_state = state_done;
-  return ssl_hs_flush;
-}
-
 enum ssl_hs_wait_t tls13_server_handshake(SSL_HANDSHAKE *hs) {
   while (hs->tls13_state != state_done) {
     enum ssl_hs_wait_t ret = ssl_hs_error;
     enum server_hs_state_t state = hs->tls13_state;
     switch (state) {
-      case state_process_client_hello:
-        ret = do_process_client_hello(hs);
-        break;
       case state_select_parameters:
         ret = do_select_parameters(hs);
         break;
       case state_send_hello_retry_request:
         ret = do_send_hello_retry_request(hs);
         break;
-      case state_flush_hello_retry_request:
-        ret = do_flush_hello_retry_request(hs);
-        break;
       case state_process_second_client_hello:
         ret = do_process_second_client_hello(hs);
         break;
       case state_send_server_hello:
         ret = do_send_server_hello(hs);
         break;
-      case state_send_encrypted_extensions:
-        ret = do_send_encrypted_extensions(hs);
-        break;
-      case state_send_certificate_request:
-        ret = do_send_certificate_request(hs);
-        break;
-      case state_send_server_certificate:
-        ret = do_send_server_certificate(hs);
-        break;
       case state_send_server_certificate_verify:
         ret = do_send_server_certificate_verify(hs, 1 /* first run */);
       break;
@@ -756,9 +640,6 @@
       case state_send_server_finished:
         ret = do_send_server_finished(hs);
         break;
-      case state_flush:
-        ret = do_flush(hs);
-        break;
       case state_process_client_certificate:
         ret = do_process_client_certificate(hs);
         break;
@@ -774,9 +655,6 @@
       case state_send_new_session_ticket:
         ret = do_send_new_session_ticket(hs);
         break;
-      case state_flush_new_session_tickets:
-        ret = do_flush_new_session_tickets(hs);
-        break;
       case state_done:
         ret = ssl_hs_ok;
         break;
diff --git a/src/ssl/tls_method.c b/src/ssl/tls_method.c
index a6584c1..70683e4 100644
--- a/src/ssl/tls_method.c
+++ b/src/ssl/tls_method.c
@@ -100,14 +100,6 @@
 
 static int ssl3_supports_cipher(const SSL_CIPHER *cipher) { return 1; }
 
-static int ssl3_flush_flight(SSL *ssl) {
-  int ret = BIO_flush(ssl->wbio);
-  if (ret <= 0) {
-    ssl->rwstate = SSL_WRITING;
-  }
-  return ret;
-}
-
 static void ssl3_expect_flight(SSL *ssl) {}
 
 static void ssl3_received_flight(SSL *ssl) {}
@@ -155,9 +147,9 @@
     ssl3_supports_cipher,
     ssl3_init_message,
     ssl3_finish_message,
-    ssl3_queue_message,
-    ssl3_write_message,
-    ssl3_send_change_cipher_spec,
+    ssl3_add_message,
+    ssl3_add_change_cipher_spec,
+    ssl3_add_alert,
     ssl3_flush_flight,
     ssl3_expect_flight,
     ssl3_received_flight,
diff --git a/src/ssl/tls_record.c b/src/ssl/tls_record.c
index 362b0c2..6ff79c4 100644
--- a/src/ssl/tls_record.c
+++ b/src/ssl/tls_record.c
@@ -205,20 +205,19 @@
 }
 
 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)) {
-    ret += DTLS1_RT_HEADER_LENGTH;
-  } else if (ssl_uses_short_header(ssl, evp_aead_seal)) {
-    ret += 2;
-  } else {
-    ret += SSL3_RT_HEADER_LENGTH;
+    return dtls_max_seal_overhead(ssl, dtls1_use_current_epoch);
   }
+
+  size_t ret =
+      ssl_uses_short_header(ssl, evp_aead_seal) ? 2 : SSL3_RT_HEADER_LENGTH;
+  ret += SSL_AEAD_CTX_max_overhead(ssl->s3->aead_write_ctx);
   /* 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)) {
+  if (ssl_needs_record_splitting(ssl)) {
     ret *= 2;
   }
   return ret;
diff --git a/src/tool/client.cc b/src/tool/client.cc
index c832ab5..0301a81 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -106,6 +106,11 @@
       "Establish a second connection resuming the original connection.",
     },
     {
+      "-root-certs", kOptionalArgument,
+      "A filename containing one of more PEM root certificates. Implies that"
+      "verification is required.",
+    },
+    {
      "", kOptionalArgument, "",
     },
 };
@@ -390,6 +395,16 @@
     SSL_CTX_set_grease_enabled(ctx.get(), 1);
   }
 
+  if (args_map.count("-root-certs") != 0) {
+    if (!SSL_CTX_load_verify_locations(
+            ctx.get(), args_map["-root-certs"].c_str(), nullptr)) {
+      fprintf(stderr, "Failed to load root certificates.\n");
+      ERR_print_errors_cb(PrintErrorCallback, stderr);
+      return false;
+    }
+    SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER, nullptr);
+  }
+
   if (args_map.count("-resume") != 0 &&
       !DoConnection(ctx.get(), args_map, &WaitForSession)) {
     return false;
diff --git a/src/util/all_tests.go b/src/util/all_tests.go
index 6cf8d13..d4bb802 100644
--- a/src/util/all_tests.go
+++ b/src/util/all_tests.go
@@ -35,6 +35,7 @@
 	useValgrind     = flag.Bool("valgrind", false, "If true, run code under valgrind")
 	useCallgrind    = flag.Bool("callgrind", false, "If true, run code under valgrind to generate callgrind traces.")
 	useGDB          = flag.Bool("gdb", false, "If true, run BoringSSL code under gdb")
+	useSDE          = flag.Bool("sde", false, "If true, run BoringSSL code under Intel's SDE for each supported chip")
 	buildDir        = flag.String("build-dir", "build", "The build directory to run the tests from.")
 	numWorkers      = flag.Int("num-workers", 1, "Runs the given number of workers when testing.")
 	jsonOutput      = flag.String("json-output", "", "The file to output JSON results to.")
@@ -42,7 +43,12 @@
 	mallocTestDebug = flag.Bool("malloc-test-debug", false, "If true, ask each test to abort rather than fail a malloc. This can be used with a specific value for --malloc-test to identity the malloc failing that is causing problems.")
 )
 
-type test []string
+type test struct {
+	args []string
+	// cpu, if not empty, contains an Intel CPU code to simulate. Run
+	// `sde64 -help` to get a list of these codes.
+	cpu string
+}
 
 type result struct {
 	Test   test
@@ -67,6 +73,27 @@
 	IsUnexpected bool   `json:"is_unexpected"`
 }
 
+// sdeCPUs contains a list of CPU code that we run all tests under when *useSDE
+// is true.
+var sdeCPUs = []string{
+	"p4p", // Pentium4 Prescott
+	"mrm", // Merom
+	"pnr", // Penryn
+	"nhm", // Nehalem
+	"wsm", // Westmere
+	"snb", // Sandy Bridge
+	"ivb", // Ivy Bridge
+	"hsw", // Haswell
+	"bdw", // Broadwell
+	"skx", // Skylake Server
+	"skl", // Skylake Client
+	"cnl", // Cannonlake
+	"knl", // Knights Landing
+	"slt", // Saltwell
+	"slm", // Silvermont
+	"glm", // Goldmont
+}
+
 func newTestOutput() *testOutput {
 	return &testOutput{
 		Version:           3,
@@ -130,6 +157,12 @@
 	return exec.Command("xterm", xtermArgs...)
 }
 
+func sdeOf(cpu, path string, args ...string) *exec.Cmd {
+	sdeArgs := []string{"-" + cpu, "--", path}
+	sdeArgs = append(sdeArgs, args...)
+	return exec.Command("sde", sdeArgs...)
+}
+
 type moreMallocsError struct{}
 
 func (moreMallocsError) Error() string {
@@ -139,8 +172,8 @@
 var errMoreMallocs = moreMallocsError{}
 
 func runTestOnce(test test, mallocNumToFail int64) (passed bool, err error) {
-	prog := path.Join(*buildDir, test[0])
-	args := test[1:]
+	prog := path.Join(*buildDir, test.args[0])
+	args := test.args[1:]
 	var cmd *exec.Cmd
 	if *useValgrind {
 		cmd = valgrindOf(false, prog, args...)
@@ -148,6 +181,8 @@
 		cmd = callgrindOf(prog, args...)
 	} else if *useGDB {
 		cmd = gdbOf(prog, args...)
+	} else if *useSDE {
+		cmd = sdeOf(test.cpu, prog, args...)
 	} else {
 		cmd = exec.Command(prog, args...)
 	}
@@ -216,12 +251,12 @@
 // relevant to the test's uniqueness.
 func shortTestName(test test) string {
 	var args []string
-	for _, arg := range test {
-		if test[0] == "crypto/evp/evp_test" || !strings.HasSuffix(arg, ".txt") {
+	for _, arg := range test.args {
+		if test.args[0] == "crypto/evp/evp_test" || !strings.HasSuffix(arg, ".txt") {
 			args = append(args, arg)
 		}
 	}
-	return strings.Join(args, " ")
+	return strings.Join(args, " ") + test.cpuMsg()
 }
 
 // setWorkingDirectory walks up directories as needed until the current working
@@ -245,10 +280,15 @@
 	defer in.Close()
 
 	decoder := json.NewDecoder(in)
-	var result []test
-	if err := decoder.Decode(&result); err != nil {
+	var testArgs [][]string
+	if err := decoder.Decode(&testArgs); err != nil {
 		return nil, err
 	}
+
+	var result []test
+	for _, args := range testArgs {
+		result = append(result, test{args: args})
+	}
 	return result, nil
 }
 
@@ -260,6 +300,14 @@
 	}
 }
 
+func (t test) cpuMsg() string {
+	if len(t.cpu) == 0 {
+		return ""
+	}
+
+	return fmt.Sprintf(" (for CPU %q)", t.cpu)
+}
+
 func main() {
 	flag.Parse()
 	setWorkingDirectory()
@@ -281,7 +329,15 @@
 
 	go func() {
 		for _, test := range testCases {
-			tests <- test
+			if *useSDE {
+				for _, cpu := range sdeCPUs {
+					testForCPU := test
+					testForCPU.cpu = cpu
+					tests <- testForCPU
+				}
+			} else {
+				tests <- test
+			}
 		}
 		close(tests)
 
@@ -293,15 +349,16 @@
 	var failed []test
 	for testResult := range results {
 		test := testResult.Test
+		args := test.args
 
-		fmt.Printf("%s\n", strings.Join([]string(test), " "))
+		fmt.Printf("%s%s\n", strings.Join(args, " "), test.cpuMsg())
 		name := shortTestName(test)
 		if testResult.Error != nil {
-			fmt.Printf("%s failed to complete: %s\n", test[0], testResult.Error)
+			fmt.Printf("%s failed to complete: %s\n", args[0], testResult.Error)
 			failed = append(failed, test)
 			testOutput.addResult(name, "CRASHED")
 		} else if !testResult.Passed {
-			fmt.Printf("%s failed to print PASS on the last line.\n", test[0])
+			fmt.Printf("%s failed to print PASS on the last line.\n", args[0])
 			failed = append(failed, test)
 			testOutput.addResult(name, "FAIL")
 		} else {
@@ -318,7 +375,7 @@
 	if len(failed) > 0 {
 		fmt.Printf("\n%d of %d tests failed:\n", len(failed), len(testCases))
 		for _, test := range failed {
-			fmt.Printf("\t%s\n", strings.Join([]string(test), " "))
+			fmt.Printf("\t%s%s\n", strings.Join(test.args, ""), test.cpuMsg())
 		}
 		os.Exit(1)
 	}
diff --git a/win-x86_64/crypto/bn/x86_64-mont5.asm b/win-x86_64/crypto/bn/x86_64-mont5.asm
index cd9a6e5..58f19ac 100644
--- a/win-x86_64/crypto/bn/x86_64-mont5.asm
+++ b/win-x86_64/crypto/bn/x86_64-mont5.asm
@@ -1868,6 +1868,7 @@
 
 ALIGN	32
 $L$8x_tail_done:
+	xor	rax,rax
 	add	r8,QWORD[rdx]
 	adc	r9,0
 	adc	r10,0
@@ -1876,9 +1877,7 @@
 	adc	r13,0
 	adc	r14,0
 	adc	r15,0
-
-
-	xor	rax,rax
+	adc	rax,0
 
 	neg	rsi
 $L$8x_no_tail: