external/boringssl: Sync to 0726fb76ebe7f422e3c4fb2e25a0064926975770.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/9c33ae85621ef8e00a42309b5101e0bedd02b816..0726fb76ebe7f422e3c4fb2e25a0064926975770

Test: cts-tradefed run cts -m CtsLibcoreOkHttpTestCases -a arm64-v8a
Test: cts-tradefed run cts -m CtsLibcoreTestCases -a arm64-v8a

Change-Id: I6da679b1bbebffd35568794c7f6e45e2d620287b
diff --git a/src/BUILDING.md b/src/BUILDING.md
index 8e226c0..c87e8b5 100644
--- a/src/BUILDING.md
+++ b/src/BUILDING.md
@@ -33,7 +33,7 @@
     executable may be configured explicitly by setting `GO_EXECUTABLE`.
 
   * To build the x86 and x86\_64 assembly, your assembler must support AVX2
-    instructions. If using GNU binutils, you must have 2.22 or later.
+    instructions and MOVBE. If using GNU binutils, you must have 2.22 or later.
 
 ## Building
 
diff --git a/src/crypto/bn/bn.c b/src/crypto/bn/bn.c
index 31bb937..e3c55f2 100644
--- a/src/crypto/bn/bn.c
+++ b/src/crypto/bn/bn.c
@@ -172,12 +172,6 @@
   return &kOne;
 }
 
-void BN_with_flags(BIGNUM *out, const BIGNUM *in, int flags) {
-  OPENSSL_memcpy(out, in, sizeof(BIGNUM));
-  out->flags &= ~BN_FLG_MALLOCED;
-  out->flags |= BN_FLG_STATIC_DATA | flags;
-}
-
 /* BN_num_bits_word returns the minimum number of bits needed to represent the
  * value in |l|. */
 unsigned BN_num_bits_word(BN_ULONG l) {
@@ -369,11 +363,3 @@
     bn->neg = 0;
   }
 }
-
-int BN_get_flags(const BIGNUM *bn, int flags) {
-  return bn->flags & flags;
-}
-
-void BN_set_flags(BIGNUM *bn, int flags) {
-  bn->flags |= flags;
-}
diff --git a/src/crypto/bn/bn_test.cc b/src/crypto/bn/bn_test.cc
index 4f544a7..8f93ad0 100644
--- a/src/crypto/bn/bn_test.cc
+++ b/src/crypto/bn/bn_test.cc
@@ -632,15 +632,6 @@
     return false;
   }
 
-  BN_set_flags(a.get(), BN_FLG_CONSTTIME);
-
-  if (!ret ||
-      !BN_mod_inverse(ret.get(), a.get(), m.get(), ctx) ||
-      !ExpectBIGNUMsEqual(t, "inv(A) (mod M) (constant-time)", mod_inv.get(),
-                          ret.get())) {
-    return false;
-  }
-
   return true;
 }
 
diff --git a/src/crypto/bn/exponentiation.c b/src/crypto/bn/exponentiation.c
index 3161a2a..933a731 100644
--- a/src/crypto/bn/exponentiation.c
+++ b/src/crypto/bn/exponentiation.c
@@ -140,12 +140,6 @@
   int i, bits, ret = 0;
   BIGNUM *v, *rr;
 
-  if ((p->flags & BN_FLG_CONSTTIME) != 0) {
-    /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
-    OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
-  }
-
   BN_CTX_start(ctx);
   if (r == a || r == p) {
     rr = BN_CTX_get(ctx);
@@ -437,12 +431,6 @@
   BIGNUM *val[TABLE_SIZE];
   BN_RECP_CTX recp;
 
-  if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
-    /* BN_FLG_CONSTTIME only supported by BN_mod_exp_mont() */
-    OPENSSL_PUT_ERROR(BN, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
-    return 0;
-  }
-
   bits = BN_num_bits(p);
 
   if (bits == 0) {
@@ -593,10 +581,6 @@
   BIGNUM *val[TABLE_SIZE];
   BN_MONT_CTX *new_mont = NULL;
 
-  if (BN_get_flags(p, BN_FLG_CONSTTIME) != 0) {
-    return BN_mod_exp_mont_consttime(rr, a, p, m, ctx, mont);
-  }
-
   if (!BN_is_odd(m)) {
     OPENSSL_PUT_ERROR(BN, BN_R_CALLED_WITH_EVEN_MODULUS);
     return 0;
diff --git a/src/crypto/bn/gcd.c b/src/crypto/bn/gcd.c
index 9e62da0..7c20b8e 100644
--- a/src/crypto/bn/gcd.c
+++ b/src/crypto/bn/gcd.c
@@ -399,10 +399,6 @@
 
 BIGNUM *BN_mod_inverse(BIGNUM *out, const BIGNUM *a, const BIGNUM *n,
                        BN_CTX *ctx) {
-  int no_inverse;
-
-  BIGNUM *a_reduced = NULL;
-
   BIGNUM *new_out = NULL;
   if (out == NULL) {
     new_out = BN_new();
@@ -414,10 +410,7 @@
   }
 
   int ok = 0;
-
-  int no_branch =
-      (a->flags & BN_FLG_CONSTTIME) != 0 || (n->flags & BN_FLG_CONSTTIME) != 0;
-
+  BIGNUM *a_reduced = NULL;
   if (a->neg || BN_ucmp(a, n) >= 0) {
     a_reduced = BN_dup(a);
     if (a_reduced == NULL) {
@@ -429,7 +422,8 @@
     a = a_reduced;
   }
 
-  if (no_branch || !BN_is_odd(n)) {
+  int no_inverse;
+  if (!BN_is_odd(n)) {
     if (!bn_mod_inverse_general(out, &no_inverse, a, n, ctx)) {
       goto err;
     }
diff --git a/src/crypto/bn/montgomery.c b/src/crypto/bn/montgomery.c
index 70f0585..aa5bc42 100644
--- a/src/crypto/bn/montgomery.c
+++ b/src/crypto/bn/montgomery.c
@@ -187,9 +187,6 @@
     OPENSSL_PUT_ERROR(BN, ERR_R_INTERNAL_ERROR);
     return 0;
   }
-  if (BN_get_flags(mod, BN_FLG_CONSTTIME)) {
-    BN_set_flags(&mont->N, BN_FLG_CONSTTIME);
-  }
 
   /* Find n0 such that n0 * N == -1 (mod r).
    *
diff --git a/src/crypto/dh/dh.c b/src/crypto/dh/dh.c
index 69a7ec8..33c36f3 100644
--- a/src/crypto/dh/dh.c
+++ b/src/crypto/dh/dh.c
@@ -258,7 +258,6 @@
   int generate_new_key = 0;
   BN_CTX *ctx = NULL;
   BIGNUM *pub_key = NULL, *priv_key = NULL;
-  BIGNUM local_priv;
 
   if (BN_num_bits(dh->p) > OPENSSL_DH_MAX_MODULUS_BITS) {
     OPENSSL_PUT_ERROR(DH, DH_R_MODULUS_TOO_LARGE);
@@ -317,8 +316,7 @@
     }
   }
 
-  BN_with_flags(&local_priv, priv_key, BN_FLG_CONSTTIME);
-  if (!BN_mod_exp_mont_consttime(pub_key, dh->g, &local_priv, dh->p, ctx,
+  if (!BN_mod_exp_mont_consttime(pub_key, dh->g, priv_key, dh->p, ctx,
                                  dh->method_mont_p)) {
     goto err;
   }
@@ -347,7 +345,6 @@
   BIGNUM *shared_key;
   int ret = -1;
   int check_result;
-  BIGNUM local_priv;
 
   if (BN_num_bits(dh->p) > OPENSSL_DH_MAX_MODULUS_BITS) {
     OPENSSL_PUT_ERROR(DH, DH_R_MODULUS_TOO_LARGE);
@@ -379,9 +376,8 @@
     goto err;
   }
 
-  BN_with_flags(&local_priv, dh->priv_key, BN_FLG_CONSTTIME);
-  if (!BN_mod_exp_mont_consttime(shared_key, peers_key, &local_priv, dh->p, ctx,
-                                 dh->method_mont_p)) {
+  if (!BN_mod_exp_mont_consttime(shared_key, peers_key, dh->priv_key, dh->p,
+                                 ctx, dh->method_mont_p)) {
     OPENSSL_PUT_ERROR(DH, ERR_R_BN_LIB);
     goto err;
   }
diff --git a/src/crypto/digest/digests.c b/src/crypto/digest/digests.c
index 351e031..9656027 100644
--- a/src/crypto/digest/digests.c
+++ b/src/crypto/digest/digests.c
@@ -59,9 +59,10 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/asn1.h>
 #include <openssl/md4.h>
 #include <openssl/md5.h>
-#include <openssl/obj.h>
+#include <openssl/nid.h>
 #include <openssl/sha.h>
 
 #include "internal.h"
@@ -306,8 +307,36 @@
   return NULL;
 }
 
-const EVP_MD* EVP_get_digestbyobj(const ASN1_OBJECT *obj) {
-  return EVP_get_digestbynid(OBJ_obj2nid(obj));
+static const struct {
+  uint8_t oid[9];
+  uint8_t oid_len;
+  const EVP_MD *(*md_func) (void);
+} kMDOIDs[] = {
+  /* 1.2.840.113549.2.4 */
+  { {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x04}, 8, EVP_md4 },
+  /* 1.2.840.113549.2.5 */
+  { {0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05}, 8, EVP_md5 },
+  /* 1.3.14.3.2.26 */
+  { {0x2b, 0x0e, 0x03, 0x02, 0x1a}, 5, EVP_sha1 },
+  /* 2.16.840.1.101.3.4.2.1 */
+  { {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01}, 9, EVP_sha256 },
+  /* 2.16.840.1.101.3.4.2.2 */
+  { {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02}, 9, EVP_sha384 },
+  /* 2.16.840.1.101.3.4.2.3 */
+  { {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03}, 9, EVP_sha512 },
+  /* 2.16.840.1.101.3.4.2.4 */
+  { {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04}, 9, EVP_sha224 },
+};
+
+const EVP_MD *EVP_get_digestbyobj(const ASN1_OBJECT *obj) {
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMDOIDs); i++) {
+    if (obj->length == kMDOIDs[i].oid_len &&
+        memcmp(obj->data, kMDOIDs[i].oid, obj->length) == 0) {
+      return kMDOIDs[i].md_func();
+    }
+  }
+
+  return NULL;
 }
 
 const EVP_MD *EVP_get_digestbyname(const char *name) {
diff --git a/src/crypto/dsa/dsa.c b/src/crypto/dsa/dsa.c
index 15583be..e2b6695 100644
--- a/src/crypto/dsa/dsa.c
+++ b/src/crypto/dsa/dsa.c
@@ -434,7 +434,6 @@
   int ok = 0;
   BN_CTX *ctx = NULL;
   BIGNUM *pub_key = NULL, *priv_key = NULL;
-  BIGNUM prk;
 
   ctx = BN_CTX_new();
   if (ctx == NULL) {
@@ -461,12 +460,9 @@
     }
   }
 
-  BN_init(&prk);
-  BN_with_flags(&prk, priv_key, BN_FLG_CONSTTIME);
-
   if (!BN_MONT_CTX_set_locked(&dsa->method_mont_p, &dsa->method_mont_lock,
                               dsa->p, ctx) ||
-      !BN_mod_exp_mont_consttime(pub_key, dsa->g, &prk, dsa->p, ctx,
+      !BN_mod_exp_mont_consttime(pub_key, dsa->g, priv_key, dsa->p, ctx,
                                  dsa->method_mont_p)) {
     goto err;
   }
@@ -844,8 +840,6 @@
     goto err;
   }
 
-  BN_set_flags(&k, BN_FLG_CONSTTIME);
-
   if (!BN_MONT_CTX_set_locked((BN_MONT_CTX **)&dsa->method_mont_p,
                               (CRYPTO_MUTEX *)&dsa->method_mont_lock, dsa->p,
                               ctx) ||
@@ -873,7 +867,6 @@
     goto err;
   }
 
-  BN_set_flags(&kq, BN_FLG_CONSTTIME);
   if (!BN_mod_exp_mont_consttime(r, dsa->g, &kq, dsa->p, ctx,
                                  dsa->method_mont_p)) {
     goto err;
diff --git a/src/crypto/ec/asm/p256-x86_64-asm.pl b/src/crypto/ec/asm/p256-x86_64-asm.pl
index a0b4b18..3cd7b01 100755
--- a/src/crypto/ec/asm/p256-x86_64-asm.pl
+++ b/src/crypto/ec/asm/p256-x86_64-asm.pl
@@ -78,57 +78,12 @@
 ___
 
 {
-################################################################################
-# void ecp_nistz256_mul_by_2(uint64_t res[4], uint64_t a[4]);
-
 my ($a0,$a1,$a2,$a3)=map("%r$_",(8..11));
 my ($t0,$t1,$t2,$t3,$t4)=("%rax","%rdx","%rcx","%r12","%r13");
 my ($r_ptr,$a_ptr,$b_ptr)=("%rdi","%rsi","%rdx");
 
 $code.=<<___;
 
-.type	ecp_nistz256_mul_by_2,\@function,2
-.align	64
-ecp_nistz256_mul_by_2:
-	push	%r12
-	push	%r13
-
-	mov	8*0($a_ptr), $a0
-	xor	$t4,$t4
-	mov	8*1($a_ptr), $a1
-	add	$a0, $a0		# a0:a3+a0:a3
-	mov	8*2($a_ptr), $a2
-	adc	$a1, $a1
-	mov	8*3($a_ptr), $a3
-	lea	.Lpoly(%rip), $a_ptr
-	 mov	$a0, $t0
-	adc	$a2, $a2
-	adc	$a3, $a3
-	 mov	$a1, $t1
-	adc	\$0, $t4
-
-	sub	8*0($a_ptr), $a0
-	 mov	$a2, $t2
-	sbb	8*1($a_ptr), $a1
-	sbb	8*2($a_ptr), $a2
-	 mov	$a3, $t3
-	sbb	8*3($a_ptr), $a3
-	sbb	\$0, $t4
-
-	cmovc	$t0, $a0
-	cmovc	$t1, $a1
-	mov	$a0, 8*0($r_ptr)
-	cmovc	$t2, $a2
-	mov	$a1, 8*1($r_ptr)
-	cmovc	$t3, $a3
-	mov	$a2, 8*2($r_ptr)
-	mov	$a3, 8*3($r_ptr)
-
-	pop	%r13
-	pop	%r12
-	ret
-.size	ecp_nistz256_mul_by_2,.-ecp_nistz256_mul_by_2
-
 ################################################################################
 # void ecp_nistz256_neg(uint64_t res[4], uint64_t a[4]);
 .globl	ecp_nistz256_neg
@@ -990,110 +945,6 @@
 }
 }
 {
-my ($r_ptr,$in_ptr)=("%rdi","%rsi");
-my ($acc0,$acc1,$acc2,$acc3)=map("%r$_",(8..11));
-my ($t0,$t1,$t2)=("%rcx","%r12","%r13");
-
-$code.=<<___;
-################################################################################
-# void ecp_nistz256_from_mont(
-#   uint64_t res[4],
-#   uint64_t in[4]);
-# This one performs Montgomery multiplication by 1, so we only need the reduction
-
-.globl	ecp_nistz256_from_mont
-.type	ecp_nistz256_from_mont,\@function,2
-.align	32
-ecp_nistz256_from_mont:
-	push	%r12
-	push	%r13
-
-	mov	8*0($in_ptr), %rax
-	mov	.Lpoly+8*3(%rip), $t2
-	mov	8*1($in_ptr), $acc1
-	mov	8*2($in_ptr), $acc2
-	mov	8*3($in_ptr), $acc3
-	mov	%rax, $acc0
-	mov	.Lpoly+8*1(%rip), $t1
-
-	#########################################
-	# First iteration
-	mov	%rax, $t0
-	shl	\$32, $acc0
-	mulq	$t2
-	shr	\$32, $t0
-	add	$acc0, $acc1
-	adc	$t0, $acc2
-	adc	%rax, $acc3
-	 mov	$acc1, %rax
-	adc	\$0, %rdx
-
-	#########################################
-	# Second iteration
-	mov	$acc1, $t0
-	shl	\$32, $acc1
-	mov	%rdx, $acc0
-	mulq	$t2
-	shr	\$32, $t0
-	add	$acc1, $acc2
-	adc	$t0, $acc3
-	adc	%rax, $acc0
-	 mov	$acc2, %rax
-	adc	\$0, %rdx
-
-	##########################################
-	# Third iteration
-	mov	$acc2, $t0
-	shl	\$32, $acc2
-	mov	%rdx, $acc1
-	mulq	$t2
-	shr	\$32, $t0
-	add	$acc2, $acc3
-	adc	$t0, $acc0
-	adc	%rax, $acc1
-	 mov	$acc3, %rax
-	adc	\$0, %rdx
-
-	###########################################
-	# Last iteration
-	mov	$acc3, $t0
-	shl	\$32, $acc3
-	mov	%rdx, $acc2
-	mulq	$t2
-	shr	\$32, $t0
-	add	$acc3, $acc0
-	adc	$t0, $acc1
-	 mov	$acc0, $t0
-	adc	%rax, $acc2
-	 mov	$acc1, $in_ptr
-	adc	\$0, %rdx
-
-	###########################################
-	# Branch-less conditional subtraction
-	sub	\$-1, $acc0
-	 mov	$acc2, %rax
-	sbb	$t1, $acc1
-	sbb	\$0, $acc2
-	 mov	%rdx, $acc3
-	sbb	$t2, %rdx
-	sbb	$t2, $t2
-
-	cmovnz	$t0, $acc0
-	cmovnz	$in_ptr, $acc1
-	mov	$acc0, 8*0($r_ptr)
-	cmovnz	%rax, $acc2
-	mov	$acc1, 8*1($r_ptr)
-	cmovz	%rdx, $acc3
-	mov	$acc2, 8*2($r_ptr)
-	mov	$acc3, 8*3($r_ptr)
-
-	pop	%r13
-	pop	%r12
-	ret
-.size	ecp_nistz256_from_mont,.-ecp_nistz256_from_mont
-___
-}
-{
 my ($val,$in_t,$index)=$win64?("%rcx","%rdx","%r8d"):("%rdi","%rsi","%edx");
 my ($ONE,$INDEX,$Ra,$Rb,$Rc,$Rd,$Re,$Rf)=map("%xmm$_",(0..7));
 my ($M0,$T0a,$T0b,$T0c,$T0d,$T0e,$T0f,$TMP0)=map("%xmm$_",(8..15));
diff --git a/src/crypto/ec/p256-x86_64.c b/src/crypto/ec/p256-x86_64.c
index 2400740..652d10c 100644
--- a/src/crypto/ec/p256-x86_64.c
+++ b/src/crypto/ec/p256-x86_64.c
@@ -515,13 +515,15 @@
   ecp_nistz256_mod_inverse_mont(z_inv3, point_z);
   ecp_nistz256_sqr_mont(z_inv2, z_inv3);
 
-  /* TODO(davidben): The two calls to |ecp_nistz256_from_mont| may be factored
-   * into one call now that other operations also reduce mod P. */
+  /* Instead of using |ecp_nistz256_from_mont| to convert the |x| coordinate
+   * and then calling |ecp_nistz256_from_mont| again to convert the |y|
+   * coordinate below, convert the common factor |z_inv2| once now, saving one
+   * reduction. */
+  ecp_nistz256_from_mont(z_inv2, z_inv2);
 
   if (x != NULL) {
     BN_ULONG x_aff[P256_LIMBS];
     ecp_nistz256_mul_mont(x_aff, z_inv2, point_x);
-    ecp_nistz256_from_mont(x_aff, x_aff);
     if (!bn_set_words(x, x_aff, P256_LIMBS)) {
       OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
       return 0;
@@ -532,7 +534,6 @@
     BN_ULONG y_aff[P256_LIMBS];
     ecp_nistz256_mul_mont(z_inv3, z_inv3, z_inv2);
     ecp_nistz256_mul_mont(y_aff, z_inv3, point_y);
-    ecp_nistz256_from_mont(y_aff, y_aff);
     if (!bn_set_words(y, y_aff, P256_LIMBS)) {
       OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
       return 0;
diff --git a/src/crypto/ec/p256-x86_64.h b/src/crypto/ec/p256-x86_64.h
index 9897699..0132348 100644
--- a/src/crypto/ec/p256-x86_64.h
+++ b/src/crypto/ec/p256-x86_64.h
@@ -51,8 +51,11 @@
 
 /* ecp_nistz256_from_mont sets |res| to |in|, converted from Montgomery domain
  * by multiplying with 1. */
-void ecp_nistz256_from_mont(BN_ULONG res[P256_LIMBS],
-                            const BN_ULONG in[P256_LIMBS]);
+static inline void ecp_nistz256_from_mont(BN_ULONG res[P256_LIMBS],
+                                          const BN_ULONG in[P256_LIMBS]) {
+  static const BN_ULONG ONE[P256_LIMBS] = { 1 };
+  ecp_nistz256_mul_mont(res, in, ONE);
+}
 
 
 /* P-256 point operations.
diff --git a/src/crypto/ec/wnaf.c b/src/crypto/ec/wnaf.c
index 1594354..67b7f34 100644
--- a/src/crypto/ec/wnaf.c
+++ b/src/crypto/ec/wnaf.c
@@ -242,7 +242,7 @@
   BN_CTX *new_ctx = NULL;
   const EC_POINT *generator = NULL;
   EC_POINT *tmp = NULL;
-  size_t total_num;
+  size_t total_num = 0;
   size_t i, j;
   int k;
   int r_is_inverted = 0;
@@ -251,7 +251,7 @@
   int8_t **wNAF = NULL; /* individual wNAFs */
   size_t *wNAF_len = NULL;
   size_t max_len = 0;
-  size_t num_val;
+  size_t num_val = 0;
   EC_POINT **val = NULL; /* precomputation */
   EC_POINT **v;
   EC_POINT ***val_sub = NULL; /* pointers to sub-arrays of 'val' */
@@ -284,15 +284,14 @@
   }
 
 
-  wsize = OPENSSL_malloc(total_num * sizeof wsize[0]);
-  wNAF_len = OPENSSL_malloc(total_num * sizeof wNAF_len[0]);
-  wNAF = OPENSSL_malloc((total_num + 1) *
-                        sizeof wNAF[0]); /* includes space for pivot */
-  val_sub = OPENSSL_malloc(total_num * sizeof val_sub[0]);
+  wsize = OPENSSL_malloc(total_num * sizeof(wsize[0]));
+  wNAF_len = OPENSSL_malloc(total_num * sizeof(wNAF_len[0]));
+  wNAF = OPENSSL_malloc(total_num * sizeof(wNAF[0]));
+  val_sub = OPENSSL_malloc(total_num * sizeof(val_sub[0]));
 
   /* Ensure wNAF is initialised in case we end up going to err. */
-  if (wNAF) {
-    wNAF[0] = NULL; /* preliminary pivot */
+  if (wNAF != NULL) {
+    OPENSSL_memset(wNAF, 0, total_num * sizeof(wNAF[0]));
   }
 
   if (!wsize || !wNAF_len || !wNAF || !val_sub) {
@@ -309,7 +308,6 @@
     bits = i < num ? BN_num_bits(scalars[i]) : BN_num_bits(g_scalar);
     wsize[i] = window_bits_for_scalar_size(bits);
     num_val += (size_t)1 << (wsize[i] - 1);
-    wNAF[i + 1] = NULL; /* make sure we always have a pivot */
     wNAF[i] =
         compute_wNAF((i < num ? scalars[i] : g_scalar), wsize[i], &wNAF_len[i]);
     if (wNAF[i] == NULL) {
@@ -322,12 +320,12 @@
 
   /* All points we precompute now go into a single array 'val'. 'val_sub[i]' is
    * a pointer to the subarray for the i-th point. */
-  val = OPENSSL_malloc((num_val + 1) * sizeof val[0]);
+  val = OPENSSL_malloc(num_val * sizeof(val[0]));
   if (val == NULL) {
     OPENSSL_PUT_ERROR(EC, ERR_R_MALLOC_FAILURE);
     goto err;
   }
-  val[num_val] = NULL; /* pivot element */
+  OPENSSL_memset(val, 0, num_val * sizeof(val[0]));
 
   /* allocate points for precomputation */
   v = val;
@@ -442,17 +440,15 @@
   OPENSSL_free(wsize);
   OPENSSL_free(wNAF_len);
   if (wNAF != NULL) {
-    int8_t **w;
-
-    for (w = wNAF; *w != NULL; w++) {
-      OPENSSL_free(*w);
+    for (i = 0; i < total_num; i++) {
+      OPENSSL_free(wNAF[i]);
     }
 
     OPENSSL_free(wNAF);
   }
   if (val != NULL) {
-    for (v = val; *v != NULL; v++) {
-      EC_POINT_clear_free(*v);
+    for (i = 0; i < num_val; i++) {
+      EC_POINT_clear_free(val[i]);
     }
 
     OPENSSL_free(val);
diff --git a/src/crypto/modes/asm/aesni-gcm-x86_64.pl b/src/crypto/modes/asm/aesni-gcm-x86_64.pl
index f777a6e..e329741 100644
--- a/src/crypto/modes/asm/aesni-gcm-x86_64.pl
+++ b/src/crypto/modes/asm/aesni-gcm-x86_64.pl
@@ -41,17 +41,24 @@
 ( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
 die "can't locate x86_64-xlate.pl";
 
-# This must be kept in sync with |$avx| in ghash-x86_64.pl; otherwise tags will
+# |$avx| in ghash-x86_64.pl must be set to at least 1; otherwise tags will
 # be computed incorrectly.
 #
 # In upstream, this is controlled by shelling out to the compiler to check
 # versions, but BoringSSL is intended to be used with pre-generated perlasm
 # output, so this isn't useful anyway.
-$avx = 0;
+#
+# The upstream code uses the condition |$avx>1| even though no AVX2
+# instructions are used, because it assumes MOVBE is supported by the assembler
+# if and only if AVX2 is also supported by the assembler; see
+# https://marc.info/?l=openssl-dev&m=146567589526984&w=2.
+$avx = 2;
 
 open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
 *STDOUT=*OUT;
 
+# See the comment above regarding why the condition is ($avx>1) when there are
+# no AVX2 instructions being used.
 if ($avx>1) {{{
 
 ($inp,$out,$len,$key,$ivp,$Xip)=("%rdi","%rsi","%rdx","%rcx","%r8","%r9");
diff --git a/src/crypto/modes/asm/ghash-x86.pl b/src/crypto/modes/asm/ghash-x86.pl
index db6eeae..182c29a 100644
--- a/src/crypto/modes/asm/ghash-x86.pl
+++ b/src/crypto/modes/asm/ghash-x86.pl
@@ -258,245 +258,17 @@
 	&mov	(&DWP($bias+56,"esp"),0xA9C0<<16);
 	&mov	(&DWP($bias+60,"esp"),0xB5E0<<16);
 }
-
-$suffix = $x86only ? "" : "_x86";
 
-&function_begin("gcm_gmult_4bit".$suffix);
-	&stack_push(16+4+1);			# +1 for stack alignment
-	&mov	($inp,&wparam(0));		# load Xi
-	&mov	($Htbl,&wparam(1));		# load Htable
-
-	&mov	($Zhh,&DWP(0,$inp));		# load Xi[16]
-	&mov	($Zhl,&DWP(4,$inp));
-	&mov	($Zlh,&DWP(8,$inp));
-	&mov	($Zll,&DWP(12,$inp));
-
-	&deposit_rem_4bit(16);
-
-	&mov	(&DWP(0,"esp"),$Zhh);		# copy Xi[16] on stack
-	&mov	(&DWP(4,"esp"),$Zhl);
-	&mov	(&DWP(8,"esp"),$Zlh);
-	&mov	(&DWP(12,"esp"),$Zll);
-	&shr	($Zll,20);
-	&and	($Zll,0xf0);
-
-	if ($unroll) {
-		&call	("_x86_gmult_4bit_inner");
-	} else {
-		&x86_loop(0);
-		&mov	($inp,&wparam(0));
-	}
-
-	&mov	(&DWP(12,$inp),$Zll);
-	&mov	(&DWP(8,$inp),$Zlh);
-	&mov	(&DWP(4,$inp),$Zhl);
-	&mov	(&DWP(0,$inp),$Zhh);
-	&stack_pop(16+4+1);
-&function_end("gcm_gmult_4bit".$suffix);
-
-&function_begin("gcm_ghash_4bit".$suffix);
-	&stack_push(16+4+1);			# +1 for 64-bit alignment
-	&mov	($Zll,&wparam(0));		# load Xi
-	&mov	($Htbl,&wparam(1));		# load Htable
-	&mov	($inp,&wparam(2));		# load in
-	&mov	("ecx",&wparam(3));		# load len
-	&add	("ecx",$inp);
-	&mov	(&wparam(3),"ecx");
-
-	&mov	($Zhh,&DWP(0,$Zll));		# load Xi[16]
-	&mov	($Zhl,&DWP(4,$Zll));
-	&mov	($Zlh,&DWP(8,$Zll));
-	&mov	($Zll,&DWP(12,$Zll));
-
-	&deposit_rem_4bit(16);
-
-    &set_label("x86_outer_loop",16);
-	&xor	($Zll,&DWP(12,$inp));		# xor with input
-	&xor	($Zlh,&DWP(8,$inp));
-	&xor	($Zhl,&DWP(4,$inp));
-	&xor	($Zhh,&DWP(0,$inp));
-	&mov	(&DWP(12,"esp"),$Zll);		# dump it on stack
-	&mov	(&DWP(8,"esp"),$Zlh);
-	&mov	(&DWP(4,"esp"),$Zhl);
-	&mov	(&DWP(0,"esp"),$Zhh);
-
-	&shr	($Zll,20);
-	&and	($Zll,0xf0);
-
-	if ($unroll) {
-		&call	("_x86_gmult_4bit_inner");
-	} else {
-		&x86_loop(0);
-		&mov	($inp,&wparam(2));
-	}
-	&lea	($inp,&DWP(16,$inp));
-	&cmp	($inp,&wparam(3));
-	&mov	(&wparam(2),$inp)	if (!$unroll);
-	&jb	(&label("x86_outer_loop"));
-
-	&mov	($inp,&wparam(0));	# load Xi
-	&mov	(&DWP(12,$inp),$Zll);
-	&mov	(&DWP(8,$inp),$Zlh);
-	&mov	(&DWP(4,$inp),$Zhl);
-	&mov	(&DWP(0,$inp),$Zhh);
-	&stack_pop(16+4+1);
-&function_end("gcm_ghash_4bit".$suffix);
-
 if (!$x86only) {{{
 
 &static_label("rem_4bit");
 
 if (!$sse2) {{	# pure-MMX "May" version...
 
-$S=12;		# shift factor for rem_4bit
+    # This code was removed since SSE2 is required for BoringSSL. The
+    # outer structure of the code was retained to minimize future merge
+    # conflicts.
 
-&function_begin_B("_mmx_gmult_4bit_inner");
-# MMX version performs 3.5 times better on P4 (see comment in non-MMX
-# routine for further details), 100% better on Opteron, ~70% better
-# on Core2 and PIII... In other words effort is considered to be well
-# spent... Since initial release the loop was unrolled in order to
-# "liberate" register previously used as loop counter. Instead it's
-# used to optimize critical path in 'Z.hi ^= rem_4bit[Z.lo&0xf]'.
-# The path involves move of Z.lo from MMX to integer register,
-# effective address calculation and finally merge of value to Z.hi.
-# Reference to rem_4bit is scheduled so late that I had to >>4
-# rem_4bit elements. This resulted in 20-45% procent improvement
-# on contemporary µ-archs.
-{
-    my $cnt;
-    my $rem_4bit = "eax";
-    my @rem = ($Zhh,$Zll);
-    my $nhi = $Zhl;
-    my $nlo = $Zlh;
-
-    my ($Zlo,$Zhi) = ("mm0","mm1");
-    my $tmp = "mm2";
-
-	&xor	($nlo,$nlo);	# avoid partial register stalls on PIII
-	&mov	($nhi,$Zll);
-	&mov	(&LB($nlo),&LB($nhi));
-	&shl	(&LB($nlo),4);
-	&and	($nhi,0xf0);
-	&movq	($Zlo,&QWP(8,$Htbl,$nlo));
-	&movq	($Zhi,&QWP(0,$Htbl,$nlo));
-	&movd	($rem[0],$Zlo);
-
-	for ($cnt=28;$cnt>=-2;$cnt--) {
-	    my $odd = $cnt&1;
-	    my $nix = $odd ? $nlo : $nhi;
-
-		&shl	(&LB($nlo),4)			if ($odd);
-		&psrlq	($Zlo,4);
-		&movq	($tmp,$Zhi);
-		&psrlq	($Zhi,4);
-		&pxor	($Zlo,&QWP(8,$Htbl,$nix));
-		&mov	(&LB($nlo),&BP($cnt/2,$inp))	if (!$odd && $cnt>=0);
-		&psllq	($tmp,60);
-		&and	($nhi,0xf0)			if ($odd);
-		&pxor	($Zhi,&QWP(0,$rem_4bit,$rem[1],8)) if ($cnt<28);
-		&and	($rem[0],0xf);
-		&pxor	($Zhi,&QWP(0,$Htbl,$nix));
-		&mov	($nhi,$nlo)			if (!$odd && $cnt>=0);
-		&movd	($rem[1],$Zlo);
-		&pxor	($Zlo,$tmp);
-
-		push	(@rem,shift(@rem));		# "rotate" registers
-	}
-
-	&mov	($inp,&DWP(4,$rem_4bit,$rem[1],8));	# last rem_4bit[rem]
-
-	&psrlq	($Zlo,32);	# lower part of Zlo is already there
-	&movd	($Zhl,$Zhi);
-	&psrlq	($Zhi,32);
-	&movd	($Zlh,$Zlo);
-	&movd	($Zhh,$Zhi);
-	&shl	($inp,4);	# compensate for rem_4bit[i] being >>4
-
-	&bswap	($Zll);
-	&bswap	($Zhl);
-	&bswap	($Zlh);
-	&xor	($Zhh,$inp);
-	&bswap	($Zhh);
-
-	&ret	();
-}
-&function_end_B("_mmx_gmult_4bit_inner");
-
-&function_begin("gcm_gmult_4bit_mmx");
-	&mov	($inp,&wparam(0));	# load Xi
-	&mov	($Htbl,&wparam(1));	# load Htable
-
-	&call	(&label("pic_point"));
-	&set_label("pic_point");
-	&blindpop("eax");
-	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
-
-	&movz	($Zll,&BP(15,$inp));
-
-	&call	("_mmx_gmult_4bit_inner");
-
-	&mov	($inp,&wparam(0));	# load Xi
-	&emms	();
-	&mov	(&DWP(12,$inp),$Zll);
-	&mov	(&DWP(4,$inp),$Zhl);
-	&mov	(&DWP(8,$inp),$Zlh);
-	&mov	(&DWP(0,$inp),$Zhh);
-&function_end("gcm_gmult_4bit_mmx");
-
-# Streamed version performs 20% better on P4, 7% on Opteron,
-# 10% on Core2 and PIII...
-&function_begin("gcm_ghash_4bit_mmx");
-	&mov	($Zhh,&wparam(0));	# load Xi
-	&mov	($Htbl,&wparam(1));	# load Htable
-	&mov	($inp,&wparam(2));	# load in
-	&mov	($Zlh,&wparam(3));	# load len
-
-	&call	(&label("pic_point"));
-	&set_label("pic_point");
-	&blindpop("eax");
-	&lea	("eax",&DWP(&label("rem_4bit")."-".&label("pic_point"),"eax"));
-
-	&add	($Zlh,$inp);
-	&mov	(&wparam(3),$Zlh);	# len to point at the end of input
-	&stack_push(4+1);		# +1 for stack alignment
-
-	&mov	($Zll,&DWP(12,$Zhh));	# load Xi[16]
-	&mov	($Zhl,&DWP(4,$Zhh));
-	&mov	($Zlh,&DWP(8,$Zhh));
-	&mov	($Zhh,&DWP(0,$Zhh));
-	&jmp	(&label("mmx_outer_loop"));
-
-    &set_label("mmx_outer_loop",16);
-	&xor	($Zll,&DWP(12,$inp));
-	&xor	($Zhl,&DWP(4,$inp));
-	&xor	($Zlh,&DWP(8,$inp));
-	&xor	($Zhh,&DWP(0,$inp));
-	&mov	(&wparam(2),$inp);
-	&mov	(&DWP(12,"esp"),$Zll);
-	&mov	(&DWP(4,"esp"),$Zhl);
-	&mov	(&DWP(8,"esp"),$Zlh);
-	&mov	(&DWP(0,"esp"),$Zhh);
-
-	&mov	($inp,"esp");
-	&shr	($Zll,24);
-
-	&call	("_mmx_gmult_4bit_inner");
-
-	&mov	($inp,&wparam(2));
-	&lea	($inp,&DWP(16,$inp));
-	&cmp	($inp,&wparam(3));
-	&jb	(&label("mmx_outer_loop"));
-
-	&mov	($inp,&wparam(0));	# load Xi
-	&emms	();
-	&mov	(&DWP(12,$inp),$Zll);
-	&mov	(&DWP(4,$inp),$Zhl);
-	&mov	(&DWP(8,$inp),$Zlh);
-	&mov	(&DWP(0,$inp),$Zhh);
-
-	&stack_pop(4+1);
-&function_end("gcm_ghash_4bit_mmx");
-
 }} else {{	# "June" MMX version...
 		# ... has slower "April" gcm_gmult_4bit_mmx with folded
 		# loop. This is done to conserve code size...
diff --git a/src/crypto/modes/asm/ghash-x86_64.pl b/src/crypto/modes/asm/ghash-x86_64.pl
index df8546c..d7471e2 100644
--- a/src/crypto/modes/asm/ghash-x86_64.pl
+++ b/src/crypto/modes/asm/ghash-x86_64.pl
@@ -90,13 +90,13 @@
 ( $xlate="${dir}../../perlasm/x86_64-xlate.pl" and -f $xlate) or
 die "can't locate x86_64-xlate.pl";
 
-# This must be kept in sync with |$avx| in aesni-gcm-x86_64.pl; otherwise tags
-# will be computed incorrectly.
+# See the notes about |$avx| in aesni-gcm-x86_64.pl; otherwise tags will be
+# computed incorrectly.
 #
 # In upstream, this is controlled by shelling out to the compiler to check
 # versions, but BoringSSL is intended to be used with pre-generated perlasm
 # output, so this isn't useful anyway.
-$avx = 0;
+$avx = 1;
 
 open OUT,"| \"$^X\" \"$xlate\" $flavour \"$output\"";
 *STDOUT=*OUT;
diff --git a/src/crypto/modes/gcm.c b/src/crypto/modes/gcm.c
index df68c40..1330ad6 100644
--- a/src/crypto/modes/gcm.c
+++ b/src/crypto/modes/gcm.c
@@ -255,19 +255,16 @@
 
 
 #if defined(GHASH_ASM)
+
 #if defined(OPENSSL_X86) || defined(OPENSSL_X86_64)
-#define GHASH_ASM_X86_OR_64
 #define GCM_FUNCREF_4BIT
 void gcm_init_clmul(u128 Htable[16], const uint64_t Xi[2]);
 void gcm_gmult_clmul(uint64_t Xi[2], const u128 Htable[16]);
 void gcm_ghash_clmul(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
                      size_t len);
 
-#if defined(OPENSSL_X86)
-#define gcm_init_avx gcm_init_clmul
-#define gcm_gmult_avx gcm_gmult_clmul
-#define gcm_ghash_avx gcm_ghash_clmul
-#else
+#if defined(OPENSSL_X86_64)
+#define GHASH_ASM_X86_64
 void gcm_init_avx(u128 Htable[16], const uint64_t Xi[2]);
 void gcm_gmult_avx(uint64_t Xi[2], const u128 Htable[16]);
 void gcm_ghash_avx(uint64_t Xi[2], const u128 Htable[16], const uint8_t *in,
@@ -289,11 +286,8 @@
 void gcm_gmult_4bit_mmx(uint64_t Xi[2], const u128 Htable[16]);
 void gcm_ghash_4bit_mmx(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
                         size_t len);
-
-void gcm_gmult_4bit_x86(uint64_t Xi[2], const u128 Htable[16]);
-void gcm_ghash_4bit_x86(uint64_t Xi[2], const u128 Htable[16], const uint8_t *inp,
-                        size_t len);
 #endif
+
 #elif defined(OPENSSL_ARM) || defined(OPENSSL_AARCH64)
 #include <openssl/arm_arch.h>
 #if __ARM_ARCH__ >= 7
@@ -357,7 +351,8 @@
 #endif
 
 void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
-                       u128 out_table[16], const uint8_t *gcm_key) {
+                       u128 *out_key, u128 out_table[16],
+                       const uint8_t *gcm_key) {
   union {
     uint64_t u[2];
     uint8_t c[16];
@@ -369,7 +364,9 @@
   H.u[0] = CRYPTO_bswap8(H.u[0]);
   H.u[1] = CRYPTO_bswap8(H.u[1]);
 
-#if defined(GHASH_ASM_X86_OR_64)
+  OPENSSL_memcpy(out_key, H.c, 16);
+
+#if defined(GHASH_ASM_X86_64)
   if (crypto_gcm_clmul_enabled()) {
     if (((OPENSSL_ia32cap_P[1] >> 22) & 0x41) == 0x41) { /* AVX+MOVBE */
       gcm_init_avx(out_table, H.u);
@@ -377,20 +374,18 @@
       *out_hash = gcm_ghash_avx;
       return;
     }
-
     gcm_init_clmul(out_table, H.u);
     *out_mult = gcm_gmult_clmul;
     *out_hash = gcm_ghash_clmul;
     return;
   }
-#if defined(GHASH_ASM_X86) /* x86 only */
-  if (OPENSSL_ia32cap_P[0] & (1 << 25)) { /* check SSE bit */
-    gcm_init_4bit(out_table, H.u);
-    *out_mult = gcm_gmult_4bit_mmx;
-    *out_hash = gcm_ghash_4bit_mmx;
+#elif defined(GHASH_ASM_X86)
+  if (crypto_gcm_clmul_enabled()) {
+    gcm_init_clmul(out_table, H.u);
+    *out_mult = gcm_gmult_clmul;
+    *out_hash = gcm_ghash_clmul;
     return;
   }
-#endif
 #elif defined(GHASH_ASM_ARM)
   if (pmull_capable()) {
     gcm_init_v8(out_table, H.u);
@@ -416,8 +411,8 @@
 
   gcm_init_4bit(out_table, H.u);
 #if defined(GHASH_ASM_X86)
-  *out_mult = gcm_gmult_4bit_x86;
-  *out_hash = gcm_ghash_4bit_x86;
+  *out_mult = gcm_gmult_4bit_mmx;
+  *out_hash = gcm_ghash_4bit_mmx;
 #else
   *out_mult = gcm_gmult_4bit;
   *out_hash = gcm_ghash_4bit;
@@ -433,7 +428,7 @@
   OPENSSL_memset(gcm_key, 0, sizeof(gcm_key));
   (*block)(gcm_key, gcm_key, aes_key);
 
-  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, ctx->Htable, gcm_key);
+  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, &ctx->H, ctx->Htable, gcm_key);
 }
 
 void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
diff --git a/src/crypto/modes/internal.h b/src/crypto/modes/internal.h
index 9b579fa..94072ec 100644
--- a/src/crypto/modes/internal.h
+++ b/src/crypto/modes/internal.h
@@ -150,6 +150,9 @@
     size_t t[16 / sizeof(size_t)];
   } Yi, EKi, EK0, len, Xi;
 
+  /* Note that the order of |Xi|, |H| and |Htable| is fixed by the MOVBE-based,
+   * x86-64, GHASH assembly. */
+  u128 H;
   u128 Htable[16];
   gmult_func gmult;
   ghash_func ghash;
@@ -211,7 +214,8 @@
  * |out_table| and sets |*out_mult| and |*out_hash| to (potentially hardware
  * accelerated) functions for performing operations in the GHASH field. */
 void CRYPTO_ghash_init(gmult_func *out_mult, ghash_func *out_hash,
-                       u128 out_table[16], const uint8_t *gcm_key);
+                       u128 *out_key, u128 out_table[16],
+                       const uint8_t *gcm_key);
 
 /* CRYPTO_gcm128_init initialises |ctx| to use |block| (typically AES) with
  * the given key. */
@@ -348,7 +352,10 @@
 } polyval_block;
 
 struct polyval_ctx {
+  /* Note that the order of |S|, |H| and |Htable| is fixed by the MOVBE-based,
+   * x86-64, GHASH assembly. */
   polyval_block S;
+  u128 H;
   u128 Htable[16];
   gmult_func gmult;
   ghash_func ghash;
diff --git a/src/crypto/modes/polyval.c b/src/crypto/modes/polyval.c
index 125b256..33d37eb 100644
--- a/src/crypto/modes/polyval.c
+++ b/src/crypto/modes/polyval.c
@@ -57,7 +57,7 @@
   OPENSSL_memcpy(H.c, key, 16);
   reverse_and_mulX_ghash(&H);
 
-  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, ctx->Htable, H.c);
+  CRYPTO_ghash_init(&ctx->gmult, &ctx->ghash, &ctx->H, ctx->Htable, H.c);
   OPENSSL_memset(&ctx->S, 0, sizeof(ctx->S));
 }
 
diff --git a/src/crypto/pkcs8/CMakeLists.txt b/src/crypto/pkcs8/CMakeLists.txt
index ffb3821..a2e52e1 100644
--- a/src/crypto/pkcs8/CMakeLists.txt
+++ b/src/crypto/pkcs8/CMakeLists.txt
@@ -7,7 +7,6 @@
 
   pkcs8.c
   p8_pkey.c
-  p5_pbe.c
   p5_pbev2.c
 )
 
diff --git a/src/crypto/pkcs8/internal.h b/src/crypto/pkcs8/internal.h
index 7995e78..9cebe29 100644
--- a/src/crypto/pkcs8/internal.h
+++ b/src/crypto/pkcs8/internal.h
@@ -63,17 +63,36 @@
 #endif
 
 
+#define PBE_UCS2_CONVERT_PASSWORD 0x1
+
+struct pbe_suite {
+  int pbe_nid;
+  const EVP_CIPHER *(*cipher_func)(void);
+  const EVP_MD *(*md_func)(void);
+  /* decrypt_init initialize |ctx| for decrypting. The password is specified by
+   * |pass_raw| and |pass_raw_len|. |param| contains the serialized parameters
+   * field of the AlgorithmIdentifier.
+   *
+   * It returns one on success and zero on error. */
+  int (*decrypt_init)(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
+                      const uint8_t *pass_raw, size_t pass_raw_len, CBS *param);
+  int flags;
+};
+
 #define PKCS5_DEFAULT_ITERATIONS 2048
 #define PKCS5_SALT_LEN 8
 
-/* PKCS5_v2_PBE_keyivgen intializes the supplied |ctx| for PBKDF v2, which must
- * be specified by |param|. The password is specified by |pass_raw| and
- * |pass_raw_len|. |cipher| and |md| are ignored.
- *
- * It returns one on success and zero on error. */
-int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
-                          size_t pass_raw_len, ASN1_TYPE *param,
-                          const EVP_CIPHER *cipher, const EVP_MD *md, int enc);
+int PKCS5_pbe2_decrypt_init(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
+                            const uint8_t *pass_raw, size_t pass_raw_len,
+                            CBS *param);
+
+/* PKCS5_pbe2_encrypt_init configures |ctx| for encrypting with PKCS #5 PBES2,
+ * as defined in RFC 2998, with the specified parameters. It writes the
+ * corresponding AlgorithmIdentifier to |out|. */
+int PKCS5_pbe2_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx,
+                            const EVP_CIPHER *cipher, unsigned iterations,
+                            const uint8_t *pass_raw, size_t pass_raw_len,
+                            const uint8_t *salt, size_t salt_len);
 
 
 #if defined(__cplusplus)
diff --git a/src/crypto/pkcs8/p5_pbe.c b/src/crypto/pkcs8/p5_pbe.c
deleted file mode 100644
index eee2e00..0000000
--- a/src/crypto/pkcs8/p5_pbe.c
+++ /dev/null
@@ -1,152 +0,0 @@
-/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL
- * project 1999.
- */
-/* ====================================================================
- * Copyright (c) 1999 The OpenSSL Project.  All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *
- * 3. All advertising materials mentioning features or use of this
- *    software must display the following acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
- *
- * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
- *    endorse or promote products derived from this software without
- *    prior written permission. For written permission, please contact
- *    licensing@OpenSSL.org.
- *
- * 5. Products derived from this software may not be called "OpenSSL"
- *    nor may "OpenSSL" appear in their names without prior written
- *    permission of the OpenSSL Project.
- *
- * 6. Redistributions of any form whatsoever must retain the following
- *    acknowledgment:
- *    "This product includes software developed by the OpenSSL Project
- *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
- *
- * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
- * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
- * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- * ====================================================================
- *
- * This product includes cryptographic software written by Eric Young
- * (eay@cryptsoft.com).  This product includes software written by Tim
- * Hudson (tjh@cryptsoft.com). */
-
-#include <string.h>
-
-#include <openssl/asn1t.h>
-#include <openssl/err.h>
-#include <openssl/obj.h>
-#include <openssl/pkcs8.h>
-#include <openssl/rand.h>
-#include <openssl/x509.h>
-
-#include "../internal.h"
-#include "internal.h"
-
-
-/* PKCS#5 password based encryption structure */
-
-ASN1_SEQUENCE(PBEPARAM) = {
-	ASN1_SIMPLE(PBEPARAM, salt, ASN1_OCTET_STRING),
-	ASN1_SIMPLE(PBEPARAM, iter, ASN1_INTEGER)
-} ASN1_SEQUENCE_END(PBEPARAM)
-
-IMPLEMENT_ASN1_FUNCTIONS(PBEPARAM)
-
-
-/* Set an algorithm identifier for a PKCS#5 PBE algorithm */
-
-int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
-				const unsigned char *salt, int saltlen)
-	{
-	PBEPARAM *pbe=NULL;
-	ASN1_STRING *pbe_str=NULL;
-	unsigned char *sstr;
-
-	pbe = PBEPARAM_new();
-	if (!pbe)
-		{
-		OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-		goto err;
-		}
-	if(iter <= 0)
-		iter = PKCS5_DEFAULT_ITERATIONS;
-	if (!ASN1_INTEGER_set(pbe->iter, iter))
-		{
-		OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-		goto err;
-		}
-	if (!saltlen)
-		saltlen = PKCS5_SALT_LEN;
-	if (!ASN1_STRING_set(pbe->salt, NULL, saltlen))
-		{
-		OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-		goto err;
-		}
-	sstr = ASN1_STRING_data(pbe->salt);
-	if (salt)
-		OPENSSL_memcpy(sstr, salt, saltlen);
-	else if (!RAND_bytes(sstr, saltlen))
-		goto err;
-
-	if(!ASN1_item_pack(pbe, ASN1_ITEM_rptr(PBEPARAM), &pbe_str))
-		{
-		OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-		goto err;
-		}
-
-	PBEPARAM_free(pbe);
-	pbe = NULL;
-
-	if (X509_ALGOR_set0(algor, OBJ_nid2obj(alg), V_ASN1_SEQUENCE, pbe_str))
-		return 1;
-
-err:
-	if (pbe != NULL)
-		PBEPARAM_free(pbe);
-	if (pbe_str != NULL)
-		ASN1_STRING_free(pbe_str);
-	return 0;
-	}
-
-/* Return an algorithm identifier for a PKCS#5 PBE algorithm */
-
-X509_ALGOR *PKCS5_pbe_set(int alg, int iter,
-				const unsigned char *salt, int saltlen)
-	{
-	X509_ALGOR *ret;
-	ret = X509_ALGOR_new();
-	if (!ret)
-		{
-		OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-		return NULL;
-		}
-
-	if (PKCS5_pbe_set0_algor(ret, alg, iter, salt, saltlen)) 
-		return ret;
-
-	X509_ALGOR_free(ret);
-	return NULL;
-	}
diff --git a/src/crypto/pkcs8/p5_pbev2.c b/src/crypto/pkcs8/p5_pbev2.c
index c16b83f..59e2067 100644
--- a/src/crypto/pkcs8/p5_pbev2.c
+++ b/src/crypto/pkcs8/p5_pbev2.c
@@ -53,390 +53,174 @@
  * (eay@cryptsoft.com).  This product includes software written by Tim
  * Hudson (tjh@cryptsoft.com). */
 
-#include <assert.h>
+#include <openssl/pkcs8.h>
+
 #include <limits.h>
 #include <string.h>
 
-#include <openssl/asn1t.h>
+#include <openssl/bytestring.h>
 #include <openssl/cipher.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
-#include <openssl/pkcs8.h>
 #include <openssl/rand.h>
-#include <openssl/x509.h>
 
 #include "internal.h"
 #include "../internal.h"
 
 
-/* PKCS#5 v2.0 password based encryption structures */
-
-ASN1_SEQUENCE(PBE2PARAM) = {
-	ASN1_SIMPLE(PBE2PARAM, keyfunc, X509_ALGOR),
-	ASN1_SIMPLE(PBE2PARAM, encryption, X509_ALGOR)
-} ASN1_SEQUENCE_END(PBE2PARAM)
-
-IMPLEMENT_ASN1_FUNCTIONS(PBE2PARAM)
-
-ASN1_SEQUENCE(PBKDF2PARAM) = {
-	ASN1_SIMPLE(PBKDF2PARAM, salt, ASN1_ANY),
-	ASN1_SIMPLE(PBKDF2PARAM, iter, ASN1_INTEGER),
-	ASN1_OPT(PBKDF2PARAM, keylength, ASN1_INTEGER),
-	ASN1_OPT(PBKDF2PARAM, prf, X509_ALGOR)
-} ASN1_SEQUENCE_END(PBKDF2PARAM)
-
-IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM)
-
-static int ASN1_TYPE_set_octetstring(ASN1_TYPE *a, unsigned char *data, int len)
-	{
-	ASN1_STRING *os;
-
-	if ((os=M_ASN1_OCTET_STRING_new()) == NULL) return(0);
-	if (!M_ASN1_OCTET_STRING_set(os,data,len))
-		{
-		M_ASN1_OCTET_STRING_free(os);
-		return 0;
-		}
-	ASN1_TYPE_set(a,V_ASN1_OCTET_STRING,os);
-	return(1);
-	}
-
-static int param_to_asn1(EVP_CIPHER_CTX *c, ASN1_TYPE *type)
-	{
-	unsigned iv_len;
-
-	iv_len = EVP_CIPHER_CTX_iv_length(c);
-	return ASN1_TYPE_set_octetstring(type, c->oiv, iv_len);
-	}
-
-/* Return an algorithm identifier for a PKCS#5 v2.0 PBE algorithm:
- * yes I know this is horrible!
- *
- * Extended version to allow application supplied PRF NID and IV. */
-
-X509_ALGOR *PKCS5_pbe2_set_iv(const EVP_CIPHER *cipher, int iter,
-				 unsigned char *salt, int saltlen,
-				 unsigned char *aiv, int prf_nid)
-{
-	X509_ALGOR *scheme = NULL, *kalg = NULL, *ret = NULL;
-	int alg_nid, keylen;
-	EVP_CIPHER_CTX ctx;
-	unsigned char iv[EVP_MAX_IV_LENGTH];
-	PBE2PARAM *pbe2 = NULL;
-	const ASN1_OBJECT *obj;
-
-	alg_nid = EVP_CIPHER_nid(cipher);
-	if(alg_nid == NID_undef) {
-		OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
-		goto err;
-	}
-	obj = OBJ_nid2obj(alg_nid);
-
-	if(!(pbe2 = PBE2PARAM_new())) goto merr;
-
-	/* Setup the AlgorithmIdentifier for the encryption scheme */
-	scheme = pbe2->encryption;
-
-	scheme->algorithm = (ASN1_OBJECT*) obj;
-	if(!(scheme->parameter = ASN1_TYPE_new())) goto merr;
-
-	/* Create random IV */
-	if (EVP_CIPHER_iv_length(cipher))
-		{
-		if (aiv)
-			OPENSSL_memcpy(iv, aiv, EVP_CIPHER_iv_length(cipher));
-		else if (!RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)))
-  			goto err;
-		}
-
-	EVP_CIPHER_CTX_init(&ctx);
-
-	/* Dummy cipherinit to just setup the IV, and PRF */
-	if (!EVP_CipherInit_ex(&ctx, cipher, NULL, NULL, iv, 0))
-		goto err;
-	if(param_to_asn1(&ctx, scheme->parameter) < 0) {
-		OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
-		EVP_CIPHER_CTX_cleanup(&ctx);
-		goto err;
-	}
-	/* If prf NID unspecified see if cipher has a preference.
-	 * An error is OK here: just means use default PRF.
-	 */
-	if ((prf_nid == -1) && 
-	EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_PBE_PRF_NID, 0, &prf_nid) <= 0)
-		{
-		ERR_clear_error();
-		prf_nid = NID_hmacWithSHA1;
-		}
-	EVP_CIPHER_CTX_cleanup(&ctx);
-
-	/* If its RC2 then we'd better setup the key length */
-
-	if(alg_nid == NID_rc2_cbc)
-		keylen = EVP_CIPHER_key_length(cipher);
-	else
-		keylen = -1;
-
-	/* Setup keyfunc */
-
-	X509_ALGOR_free(pbe2->keyfunc);
-
-	pbe2->keyfunc = PKCS5_pbkdf2_set(iter, salt, saltlen, prf_nid, keylen);
-
-	if (!pbe2->keyfunc)
-		goto merr;
-
-	/* Now set up top level AlgorithmIdentifier */
-
-	if(!(ret = X509_ALGOR_new())) goto merr;
-	if(!(ret->parameter = ASN1_TYPE_new())) goto merr;
-
-	ret->algorithm = (ASN1_OBJECT*) OBJ_nid2obj(NID_pbes2);
-
-	/* Encode PBE2PARAM into parameter */
-
-	if(!ASN1_item_pack(pbe2, ASN1_ITEM_rptr(PBE2PARAM),
-				 &ret->parameter->value.sequence)) goto merr;
-	ret->parameter->type = V_ASN1_SEQUENCE;
-
-	PBE2PARAM_free(pbe2);
-	pbe2 = NULL;
-
-	return ret;
-
-	merr:
-	OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-
-	err:
-	PBE2PARAM_free(pbe2);
-	/* Note 'scheme' is freed as part of pbe2 */
-	X509_ALGOR_free(kalg);
-	X509_ALGOR_free(ret);
-
-	return NULL;
-
-}
-
-X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
-				 unsigned char *salt, int saltlen)
-	{
-	return PKCS5_pbe2_set_iv(cipher, iter, salt, saltlen, NULL, -1);
-	}
-
-X509_ALGOR *PKCS5_pbkdf2_set(int iter, unsigned char *salt, int saltlen,
-				int prf_nid, int keylen)
-	{
-	X509_ALGOR *keyfunc = NULL;
-	PBKDF2PARAM *kdf = NULL;
-	ASN1_OCTET_STRING *osalt = NULL;
-
-	if(!(kdf = PBKDF2PARAM_new()))
-		goto merr;
-	if(!(osalt = M_ASN1_OCTET_STRING_new()))
-		goto merr;
-
-	kdf->salt->value.octet_string = osalt;
-	kdf->salt->type = V_ASN1_OCTET_STRING;
-
-	if (!saltlen)
-		saltlen = PKCS5_SALT_LEN;
-	if (!(osalt->data = OPENSSL_malloc (saltlen)))
-		goto merr;
-
-	osalt->length = saltlen;
-
-	if (salt)
-		OPENSSL_memcpy (osalt->data, salt, saltlen);
-	else if (!RAND_bytes(osalt->data, saltlen))
-		goto merr;
-
-	if(iter <= 0)
-		iter = PKCS5_DEFAULT_ITERATIONS;
-
-	if(!ASN1_INTEGER_set(kdf->iter, iter))
-		goto merr;
-
-	/* If have a key len set it up */
-
-	if(keylen > 0) 
-		{
-		if(!(kdf->keylength = M_ASN1_INTEGER_new()))
-			goto merr;
-		if(!ASN1_INTEGER_set (kdf->keylength, keylen))
-			goto merr;
-		}
-
-	/* prf can stay NULL if we are using hmacWithSHA1 */
-	if (prf_nid > 0 && prf_nid != NID_hmacWithSHA1)
-		{
-		kdf->prf = X509_ALGOR_new();
-		if (!kdf->prf)
-			goto merr;
-		X509_ALGOR_set0(kdf->prf, OBJ_nid2obj(prf_nid),
-					V_ASN1_NULL, NULL);
-		}
-
-	/* Finally setup the keyfunc structure */
-
-	keyfunc = X509_ALGOR_new();
-	if (!keyfunc)
-		goto merr;
-
-	keyfunc->algorithm = (ASN1_OBJECT*) OBJ_nid2obj(NID_id_pbkdf2);
-
-	/* Encode PBKDF2PARAM into parameter of pbe2 */
-
-	if(!(keyfunc->parameter = ASN1_TYPE_new()))
-		goto merr;
-
-	if(!ASN1_item_pack(kdf, ASN1_ITEM_rptr(PBKDF2PARAM),
-			 &keyfunc->parameter->value.sequence))
-		goto merr;
-	keyfunc->parameter->type = V_ASN1_SEQUENCE;
-
-	PBKDF2PARAM_free(kdf);
-	return keyfunc;
-
-	merr:
-	OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-	PBKDF2PARAM_free(kdf);
-	X509_ALGOR_free(keyfunc);
-	return NULL;
-	}
-
-static int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx,
-                                    const uint8_t *pass_raw,
-                                    size_t pass_raw_len, const ASN1_TYPE *param,
-                                    const ASN1_TYPE *iv, int enc) {
-  int rv = 0;
-  PBKDF2PARAM *pbkdf2param = NULL;
-
-  if (EVP_CIPHER_CTX_cipher(ctx) == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, CIPHER_R_NO_CIPHER_SET);
-    goto err;
+static int pkcs5_pbe2_cipher_init(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+                                  unsigned iterations, const uint8_t *pass_raw,
+                                  size_t pass_raw_len, const uint8_t *salt,
+                                  size_t salt_len, const uint8_t *iv,
+                                  size_t iv_len, int enc) {
+  if (iv_len != EVP_CIPHER_iv_length(cipher)) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
+    return 0;
   }
 
-  /* Decode parameters. */
-  if (param == NULL || param->type != V_ASN1_SEQUENCE) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    goto err;
-  }
-
-  const uint8_t *pbuf = param->value.sequence->data;
-  int plen = param->value.sequence->length;
-  pbkdf2param = d2i_PBKDF2PARAM(NULL, &pbuf, plen);
-  if (pbkdf2param == NULL || pbuf != param->value.sequence->data + plen) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    goto err;
-  }
-
-  /* Now check the parameters. */
   uint8_t key[EVP_MAX_KEY_LENGTH];
-  const size_t key_len = EVP_CIPHER_CTX_key_length(ctx);
-  assert(key_len <= sizeof(key));
-
-  if (pbkdf2param->keylength != NULL &&
-      ASN1_INTEGER_get(pbkdf2param->keylength) != (int) key_len) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH);
-    goto err;
-  }
-
-  if (pbkdf2param->prf != NULL &&
-      OBJ_obj2nid(pbkdf2param->prf->algorithm) != NID_hmacWithSHA1) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
-    goto err;
-  }
-
-  if (pbkdf2param->salt->type != V_ASN1_OCTET_STRING) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_SALT_TYPE);
-    goto err;
-  }
-
-  if (pbkdf2param->iter->type != V_ASN1_INTEGER) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
-    goto err;
-  }
-  long iterations = ASN1_INTEGER_get(pbkdf2param->iter);
-  if (iterations <= 0 ||
-      (sizeof(long) > sizeof(unsigned) && iterations > (long)UINT_MAX)) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
-    goto err;
-  }
-
-  if (iv->type != V_ASN1_OCTET_STRING || iv->value.octet_string == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
-    goto err;
-  }
-
-  const size_t iv_len = EVP_CIPHER_CTX_iv_length(ctx);
-  if ((size_t) iv->value.octet_string->length != iv_len) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS);
-    goto err;
-  }
-
-  if (!PKCS5_PBKDF2_HMAC_SHA1((const char *) pass_raw, pass_raw_len,
-                              pbkdf2param->salt->value.octet_string->data,
-                              pbkdf2param->salt->value.octet_string->length,
-                              iterations, key_len, key)) {
-    goto err;
-  }
-
-  rv = EVP_CipherInit_ex(ctx, NULL /* cipher */, NULL /* engine */, key,
-                         iv->value.octet_string->data, enc);
-
- err:
-  PBKDF2PARAM_free(pbkdf2param);
-  return rv;
+  int ret = PKCS5_PBKDF2_HMAC_SHA1((const char *)pass_raw, pass_raw_len, salt,
+                                   salt_len, iterations,
+                                   EVP_CIPHER_key_length(cipher), key) &&
+            EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, key, iv, enc);
+  OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
+  return ret;
 }
 
-int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
-                          size_t pass_raw_len, ASN1_TYPE *param,
-                          const EVP_CIPHER *unused, const EVP_MD *unused2,
-                          int enc) {
-  PBE2PARAM *pbe2param = NULL;
-  int rv = 0;
-
-  if (param == NULL ||
-      param->type != V_ASN1_SEQUENCE ||
-      param->value.sequence == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    goto err;
+int PKCS5_pbe2_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx,
+                            const EVP_CIPHER *cipher, unsigned iterations,
+                            const uint8_t *pass_raw, size_t pass_raw_len,
+                            const uint8_t *salt, size_t salt_len) {
+  int cipher_nid = EVP_CIPHER_nid(cipher);
+  if (cipher_nid == NID_undef) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_CIPHER_HAS_NO_OBJECT_IDENTIFIER);
+    return 0;
   }
 
-  const uint8_t *pbuf = param->value.sequence->data;
-  int plen = param->value.sequence->length;
-  pbe2param = d2i_PBE2PARAM(NULL, &pbuf, plen);
-  if (pbe2param == NULL || pbuf != param->value.sequence->data + plen) {
+  /* Generate a random IV. */
+  uint8_t iv[EVP_MAX_IV_LENGTH];
+  if (!RAND_bytes(iv, EVP_CIPHER_iv_length(cipher))) {
+    return 0;
+  }
+
+  /* See RFC 2898, appendix A. */
+  CBB algorithm, param, kdf, kdf_param, salt_cbb, cipher_cbb, iv_cbb;
+  if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&algorithm, NID_pbes2) ||
+      !CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&param, &kdf, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&kdf, NID_id_pbkdf2) ||
+      !CBB_add_asn1(&kdf, &kdf_param, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&kdf_param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_bytes(&salt_cbb, salt, salt_len) ||
+      !CBB_add_asn1_uint64(&kdf_param, iterations) ||
+      /* Specify a key length for RC2. */
+      (cipher_nid == NID_rc2_cbc &&
+       !CBB_add_asn1_uint64(&kdf_param, EVP_CIPHER_key_length(cipher))) ||
+      /* Omit the PRF. We use the default hmacWithSHA1. */
+      !CBB_add_asn1(&param, &cipher_cbb, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&cipher_cbb, cipher_nid) ||
+      /* RFC 2898 says RC2-CBC and RC5-CBC-Pad use a SEQUENCE with version and
+       * IV, but OpenSSL always uses an OCTET STRING IV, so we do the same. */
+      !CBB_add_asn1(&cipher_cbb, &iv_cbb, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_bytes(&iv_cbb, iv, EVP_CIPHER_iv_length(cipher)) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return pkcs5_pbe2_cipher_init(ctx, cipher, iterations, pass_raw, pass_raw_len,
+                                salt, salt_len, iv,
+                                EVP_CIPHER_iv_length(cipher), 1 /* encrypt */);
+}
+
+int PKCS5_pbe2_decrypt_init(const struct pbe_suite *suite, EVP_CIPHER_CTX *ctx,
+                            const uint8_t *pass_raw, size_t pass_raw_len,
+                            CBS *param) {
+  CBS pbe_param, kdf, kdf_obj, enc_scheme, enc_obj;
+  if (!CBS_get_asn1(param, &pbe_param, CBS_ASN1_SEQUENCE) ||
+      CBS_len(param) != 0 ||
+      !CBS_get_asn1(&pbe_param, &kdf, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&pbe_param, &enc_scheme, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&pbe_param) != 0 ||
+      !CBS_get_asn1(&kdf, &kdf_obj, CBS_ASN1_OBJECT) ||
+      !CBS_get_asn1(&enc_scheme, &enc_obj, CBS_ASN1_OBJECT)) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    goto err;
+    return 0;
   }
 
   /* Check that the key derivation function is PBKDF2. */
-  if (OBJ_obj2nid(pbe2param->keyfunc->algorithm) != NID_id_pbkdf2) {
+  if (OBJ_cbs2nid(&kdf_obj) != NID_id_pbkdf2) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION);
-    goto err;
+    return 0;
   }
 
   /* See if we recognise the encryption algorithm. */
-  const EVP_CIPHER *cipher =
-      EVP_get_cipherbynid(OBJ_obj2nid(pbe2param->encryption->algorithm));
+  const EVP_CIPHER *cipher = EVP_get_cipherbynid(OBJ_cbs2nid(&enc_obj));
   if (cipher == NULL) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER);
-    goto err;
+    return 0;
   }
 
-  /* Fixup cipher based on AlgorithmIdentifier. */
-  if (!EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, NULL /* key */,
-                         NULL /* iv */, enc)) {
-    goto err;
+  /* Parse the KDF parameters. */
+  CBS pbkdf2_params, salt;
+  uint64_t iterations;
+  if (!CBS_get_asn1(&kdf, &pbkdf2_params, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&kdf) != 0 ||
+      !CBS_get_asn1(&pbkdf2_params, &salt, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1_uint64(&pbkdf2_params, &iterations)) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    return 0;
   }
 
-  rv = PKCS5_v2_PBKDF2_keyivgen(ctx, pass_raw, pass_raw_len,
-                                pbe2param->keyfunc->parameter,
-                                pbe2param->encryption->parameter, enc);
+  if (iterations == 0 || iterations > UINT_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
+    return 0;
+  }
 
- err:
-  PBE2PARAM_free(pbe2param);
-  return rv;
+  /* The optional keyLength parameter, if present, must match the key length of
+   * the cipher. */
+  if (CBS_peek_asn1_tag(&pbkdf2_params, CBS_ASN1_INTEGER)) {
+    uint64_t key_len;
+    if (!CBS_get_asn1_uint64(&pbkdf2_params, &key_len)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+      return 0;
+    }
+
+    if (key_len != EVP_CIPHER_key_length(cipher)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH);
+      return 0;
+    }
+  }
+
+  if (CBS_len(&pbkdf2_params) != 0) {
+    CBS prf;
+    if (!CBS_get_asn1(&pbkdf2_params, &prf, CBS_ASN1_OBJECT) ||
+        CBS_len(&pbkdf2_params) != 0) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+      return 0;
+    }
+
+    /* We only support hmacWithSHA1. It is the DEFAULT, so DER requires it be
+     * omitted, but we match OpenSSL in tolerating it being present. */
+    if (OBJ_cbs2nid(&prf) != NID_hmacWithSHA1) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
+      return 0;
+    }
+  }
+
+  /* Parse the encryption scheme parameters. Note OpenSSL does not match the
+   * specification. Per RFC 2898, this should depend on the encryption scheme.
+   * In particular, RC2-CBC and RC5-CBC-Pad use a SEQUENCE with version and IV.
+   * We align with OpenSSL. */
+  CBS iv;
+  if (!CBS_get_asn1(&enc_scheme, &iv, CBS_ASN1_OCTETSTRING) ||
+      CBS_len(&enc_scheme) != 0) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF);
+    return 0;
+  }
+
+  return pkcs5_pbe2_cipher_init(ctx, cipher, (unsigned)iterations, pass_raw,
+                                pass_raw_len, CBS_data(&salt), CBS_len(&salt),
+                                CBS_data(&iv), CBS_len(&iv), 0 /* decrypt */);
 }
diff --git a/src/crypto/pkcs8/pkcs8.c b/src/crypto/pkcs8/pkcs8.c
index e965bc9..efad81d 100644
--- a/src/crypto/pkcs8/pkcs8.c
+++ b/src/crypto/pkcs8/pkcs8.c
@@ -68,6 +68,7 @@
 #include <openssl/hmac.h>
 #include <openssl/mem.h>
 #include <openssl/obj.h>
+#include <openssl/rand.h>
 #include <openssl/x509.h>
 
 #include "internal.h"
@@ -81,23 +82,21 @@
 
 static int ascii_to_ucs2(const char *ascii, size_t ascii_len,
                          uint8_t **out, size_t *out_len) {
-  uint8_t *unitmp;
-  size_t ulen, i;
-
-  ulen = ascii_len * 2 + 2;
-  if (ulen < ascii_len) {
+  size_t ulen = ascii_len * 2 + 2;
+  if (ascii_len * 2 < ascii_len || ulen < ascii_len * 2) {
     return 0;
   }
-  unitmp = OPENSSL_malloc(ulen);
+
+  uint8_t *unitmp = OPENSSL_malloc(ulen);
   if (unitmp == NULL) {
     return 0;
   }
-  for (i = 0; i < ulen - 2; i += 2) {
+  for (size_t i = 0; i < ulen - 2; i += 2) {
     unitmp[i] = 0;
     unitmp[i + 1] = ascii[i >> 1];
   }
 
-  /* Make result double null terminated */
+  /* Terminate the result with a UCS-2 NUL. */
   unitmp[ulen - 2] = 0;
   unitmp[ulen - 1] = 0;
   *out_len = ulen;
@@ -107,7 +106,7 @@
 
 static int pkcs12_key_gen_raw(const uint8_t *pass_raw, size_t pass_raw_len,
                               const uint8_t *salt, size_t salt_len,
-                              uint8_t id, int iterations,
+                              uint8_t id, unsigned iterations,
                               size_t out_len, uint8_t *out,
                               const EVP_MD *md) {
   /* See https://tools.ietf.org/html/rfc7292#appendix-B. Quoted parts of the
@@ -177,7 +176,7 @@
         !EVP_DigestFinal_ex(&ctx, A, &A_len)) {
       goto err;
     }
-    for (int iter = 1; iter < iterations; iter++) {
+    for (unsigned iter = 1; iter < iterations; iter++) {
       if (!EVP_DigestInit_ex(&ctx, md, NULL) ||
           !EVP_DigestUpdate(&ctx, A, A_len) ||
           !EVP_DigestFinal_ex(&ctx, A, &A_len)) {
@@ -223,86 +222,75 @@
   return ret;
 }
 
-static int pkcs12_pbe_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
-                               size_t pass_raw_len, ASN1_TYPE *param,
-                               const EVP_CIPHER *cipher, const EVP_MD *md,
-                               int is_encrypt) {
-  PBEPARAM *pbe;
-  int salt_len, iterations, ret;
-  uint8_t *salt;
-  const uint8_t *pbuf;
-  uint8_t key[EVP_MAX_KEY_LENGTH], iv[EVP_MAX_IV_LENGTH];
+static int pkcs12_pbe_cipher_init(const struct pbe_suite *suite,
+                                  EVP_CIPHER_CTX *ctx, unsigned iterations,
+                                  const uint8_t *pass_raw, size_t pass_raw_len,
+                                  const uint8_t *salt, size_t salt_len,
+                                  int is_encrypt) {
+  const EVP_CIPHER *cipher = suite->cipher_func();
+  const EVP_MD *md = suite->md_func();
 
-  /* Extract useful info from parameter */
-  if (param == NULL || param->type != V_ASN1_SEQUENCE ||
-      param->value.sequence == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    return 0;
-  }
-
-  pbuf = param->value.sequence->data;
-  pbe = d2i_PBEPARAM(NULL, &pbuf, param->value.sequence->length);
-  if (pbe == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-    return 0;
-  }
-
-  if (!pbe->iter) {
-    iterations = 1;
-  } else {
-    iterations = ASN1_INTEGER_get(pbe->iter);
-  }
-  salt = pbe->salt->data;
-  salt_len = pbe->salt->length;
-  if (!pkcs12_key_gen_raw(pass_raw, pass_raw_len, salt, salt_len, PKCS12_KEY_ID,
-                          iterations, EVP_CIPHER_key_length(cipher), key, md)) {
+  uint8_t key[EVP_MAX_KEY_LENGTH];
+  if (!pkcs12_key_gen_raw(pass_raw, pass_raw_len, salt,
+                          salt_len, PKCS12_KEY_ID, iterations,
+                          EVP_CIPHER_key_length(cipher), key, md)) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEY_GEN_ERROR);
-    PBEPARAM_free(pbe);
     return 0;
   }
-  if (!pkcs12_key_gen_raw(pass_raw, pass_raw_len, salt, salt_len, PKCS12_IV_ID,
-                          iterations, EVP_CIPHER_iv_length(cipher), iv, md)) {
+
+  uint8_t iv[EVP_MAX_IV_LENGTH];
+  if (!pkcs12_key_gen_raw(pass_raw, pass_raw_len, salt,
+                          salt_len, PKCS12_IV_ID, iterations,
+                          EVP_CIPHER_iv_length(cipher), iv, md)) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEY_GEN_ERROR);
-    PBEPARAM_free(pbe);
     return 0;
   }
-  PBEPARAM_free(pbe);
-  ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, is_encrypt);
+
+  int ret = EVP_CipherInit_ex(ctx, cipher, NULL, key, iv, is_encrypt);
   OPENSSL_cleanse(key, EVP_MAX_KEY_LENGTH);
   OPENSSL_cleanse(iv, EVP_MAX_IV_LENGTH);
   return ret;
 }
 
-typedef int (*keygen_func)(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
-                           size_t pass_raw_len, ASN1_TYPE *param,
-                           const EVP_CIPHER *cipher, const EVP_MD *md,
-                           int is_encrypt);
+static int pkcs12_pbe_decrypt_init(const struct pbe_suite *suite,
+                                   EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw,
+                                   size_t pass_raw_len, CBS *param) {
+  CBS pbe_param, salt;
+  uint64_t iterations;
+  if (!CBS_get_asn1(param, &pbe_param, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&pbe_param, &salt, CBS_ASN1_OCTETSTRING) ||
+      !CBS_get_asn1_uint64(&pbe_param, &iterations) ||
+      CBS_len(&pbe_param) != 0 ||
+      CBS_len(param) != 0) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    return 0;
+  }
 
-struct pbe_suite {
-  int pbe_nid;
-  const EVP_CIPHER* (*cipher_func)(void);
-  const EVP_MD* (*md_func)(void);
-  keygen_func keygen;
-  int flags;
-};
+  if (iterations == 0 || iterations > UINT_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT);
+    return 0;
+  }
 
-#define PBE_UCS2_CONVERT_PASSWORD 0x1
+  return pkcs12_pbe_cipher_init(suite, ctx, (unsigned)iterations, pass_raw,
+                                pass_raw_len, CBS_data(&salt), CBS_len(&salt),
+                                0 /* decrypt */);
+}
 
 static const struct pbe_suite kBuiltinPBE[] = {
     {
-     NID_pbe_WithSHA1And40BitRC2_CBC, EVP_rc2_40_cbc, EVP_sha1,
-     pkcs12_pbe_keyivgen, PBE_UCS2_CONVERT_PASSWORD
+        NID_pbe_WithSHA1And40BitRC2_CBC, EVP_rc2_40_cbc, EVP_sha1,
+        pkcs12_pbe_decrypt_init, PBE_UCS2_CONVERT_PASSWORD,
     },
     {
-     NID_pbe_WithSHA1And128BitRC4, EVP_rc4, EVP_sha1, pkcs12_pbe_keyivgen,
-     PBE_UCS2_CONVERT_PASSWORD
+        NID_pbe_WithSHA1And128BitRC4, EVP_rc4, EVP_sha1,
+        pkcs12_pbe_decrypt_init, PBE_UCS2_CONVERT_PASSWORD,
     },
     {
-     NID_pbe_WithSHA1And3_Key_TripleDES_CBC, EVP_des_ede3_cbc, EVP_sha1,
-     pkcs12_pbe_keyivgen, PBE_UCS2_CONVERT_PASSWORD
+        NID_pbe_WithSHA1And3_Key_TripleDES_CBC, EVP_des_ede3_cbc, EVP_sha1,
+        pkcs12_pbe_decrypt_init, PBE_UCS2_CONVERT_PASSWORD,
     },
     {
-      NID_pbes2, NULL, NULL,  PKCS5_v2_PBE_keyivgen, 0
+        NID_pbes2, NULL, NULL, PKCS5_pbe2_decrypt_init, 0,
     },
 };
 
@@ -359,130 +347,85 @@
   return 1;
 }
 
-static int pbe_cipher_init(ASN1_OBJECT *pbe_obj,
-                           const uint8_t *pass_raw, size_t pass_raw_len,
-                           ASN1_TYPE *param,
-                           EVP_CIPHER_CTX *ctx, int is_encrypt) {
-  const EVP_CIPHER *cipher;
-  const EVP_MD *md;
-
-  const struct pbe_suite *suite = get_pbe_suite(OBJ_obj2nid(pbe_obj));
+static int pkcs12_pbe_encrypt_init(CBB *out, EVP_CIPHER_CTX *ctx, int alg,
+                                   unsigned iterations, const uint8_t *pass_raw,
+                                   size_t pass_raw_len, const uint8_t *salt,
+                                   size_t salt_len) {
+  const struct pbe_suite *suite = get_pbe_suite(alg);
   if (suite == NULL) {
-    char obj_str[80];
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_ALGORITHM);
-    if (!pbe_obj) {
-      strncpy(obj_str, "NULL", sizeof(obj_str));
-    } else {
-      i2t_ASN1_OBJECT(obj_str, sizeof(obj_str), pbe_obj);
-    }
-    ERR_add_error_data(2, "TYPE=", obj_str);
     return 0;
   }
 
-  if (suite->cipher_func == NULL) {
-    cipher = NULL;
-  } else {
-    cipher = suite->cipher_func();
-    if (!cipher) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_CIPHER);
-      return 0;
-    }
-  }
-
-  if (suite->md_func == NULL) {
-    md = NULL;
-  } else {
-    md = suite->md_func();
-    if (!md) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_DIGEST);
-      return 0;
-    }
-  }
-
-  if (!suite->keygen(ctx, pass_raw, pass_raw_len, param, cipher, md,
-                     is_encrypt)) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEYGEN_FAILURE);
+  /* See RFC 2898, appendix A.3. */
+  CBB algorithm, param, salt_cbb;
+  if (!CBB_add_asn1(out, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&algorithm, alg) ||
+      !CBB_add_asn1(&algorithm, &param, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&param, &salt_cbb, CBS_ASN1_OCTETSTRING) ||
+      !CBB_add_bytes(&salt_cbb, salt, salt_len) ||
+      !CBB_add_asn1_uint64(&param, iterations) ||
+      !CBB_flush(out)) {
     return 0;
   }
 
-  return 1;
+  return pkcs12_pbe_cipher_init(suite, ctx, iterations, pass_raw, pass_raw_len,
+                                salt, salt_len, 1 /* encrypt */);
 }
 
-static int pbe_crypt(const X509_ALGOR *algor,
-                     const uint8_t *pass_raw, size_t pass_raw_len,
-                     const uint8_t *in, size_t in_len,
-                     uint8_t **out, size_t *out_len,
-                     int is_encrypt) {
-  uint8_t *buf;
-  int n, ret = 0;
+static int pbe_decrypt(uint8_t **out, size_t *out_len, CBS *algorithm,
+                       const uint8_t *pass_raw, size_t pass_raw_len,
+                       const uint8_t *in, size_t in_len) {
+  int ret = 0;
+  uint8_t *buf = NULL;;
   EVP_CIPHER_CTX ctx;
-  unsigned block_size;
-
   EVP_CIPHER_CTX_init(&ctx);
 
-  if (!pbe_cipher_init(algor->algorithm, pass_raw, pass_raw_len,
-                       algor->parameter, &ctx, is_encrypt)) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_CIPHER_ALGORITHM);
-    return 0;
-  }
-  block_size = EVP_CIPHER_CTX_block_size(&ctx);
-
-  if (in_len + block_size < in_len) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_TOO_LONG);
+  CBS obj;
+  if (!CBS_get_asn1(algorithm, &obj, CBS_ASN1_OBJECT)) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
     goto err;
   }
 
-  buf = OPENSSL_malloc(in_len + block_size);
+  const struct pbe_suite *suite = get_pbe_suite(OBJ_cbs2nid(&obj));
+  if (suite == NULL) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_ALGORITHM);
+    goto err;
+  }
+
+  if (!suite->decrypt_init(suite, &ctx, pass_raw, pass_raw_len, algorithm)) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_KEYGEN_FAILURE);
+    goto err;
+  }
+
+  buf = OPENSSL_malloc(in_len);
   if (buf == NULL) {
     OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
     goto err;
   }
 
-  if (!EVP_CipherUpdate(&ctx, buf, &n, in, in_len)) {
-    OPENSSL_free(buf);
-    OPENSSL_PUT_ERROR(PKCS8, ERR_R_EVP_LIB);
+  if (in_len > INT_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, ERR_R_OVERFLOW);
     goto err;
   }
-  *out_len = n;
 
-  if (!EVP_CipherFinal_ex(&ctx, buf + n, &n)) {
-    OPENSSL_free(buf);
-    OPENSSL_PUT_ERROR(PKCS8, ERR_R_EVP_LIB);
+  int n1, n2;
+  if (!EVP_DecryptUpdate(&ctx, buf, &n1, in, (int)in_len) ||
+      !EVP_DecryptFinal_ex(&ctx, buf + n1, &n2)) {
     goto err;
   }
-  *out_len += n;
+
   *out = buf;
+  *out_len = n1 + n2;
   ret = 1;
+  buf = NULL;
 
 err:
+  OPENSSL_free(buf);
   EVP_CIPHER_CTX_cleanup(&ctx);
   return ret;
 }
 
-static void *pkcs12_item_decrypt_d2i(X509_ALGOR *algor, const ASN1_ITEM *it,
-                                     const uint8_t *pass_raw,
-                                     size_t pass_raw_len,
-                                     ASN1_OCTET_STRING *oct) {
-  uint8_t *out;
-  const uint8_t *p;
-  void *ret;
-  size_t out_len;
-
-  if (!pbe_crypt(algor, pass_raw, pass_raw_len, oct->data, oct->length,
-                 &out, &out_len, 0 /* decrypt */)) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_CRYPT_ERROR);
-    return NULL;
-  }
-  p = out;
-  ret = ASN1_item_d2i(NULL, &p, out_len, it);
-  OPENSSL_cleanse(out, out_len);
-  if (!ret) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
-  }
-  OPENSSL_free(out);
-  return ret;
-}
-
 PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
                                    int pass_len) {
   uint8_t *pass_raw = NULL;
@@ -503,44 +446,57 @@
 
 PKCS8_PRIV_KEY_INFO *PKCS8_decrypt_pbe(X509_SIG *pkcs8, const uint8_t *pass_raw,
                                        size_t pass_raw_len) {
-  return pkcs12_item_decrypt_d2i(pkcs8->algor,
-                                 ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), pass_raw,
-                                 pass_raw_len, pkcs8->digest);
-}
+  PKCS8_PRIV_KEY_INFO *ret = NULL;
+  uint8_t *in = NULL, *out = NULL;
+  size_t out_len = 0;
 
-static ASN1_OCTET_STRING *pkcs12_item_i2d_encrypt(X509_ALGOR *algor,
-                                                  const ASN1_ITEM *it,
-                                                  const uint8_t *pass_raw,
-                                                  size_t pass_raw_len, void *obj) {
-  ASN1_OCTET_STRING *oct;
-  uint8_t *in = NULL;
-  int in_len;
-  size_t crypt_len;
+  /* Convert the legacy ASN.1 object to a byte string. */
+  int in_len = i2d_X509_SIG(pkcs8, &in);
+  if (in_len < 0) {
+    goto err;
+  }
 
-  oct = M_ASN1_OCTET_STRING_new();
-  if (oct == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
-    return NULL;
+  /* See RFC 5208, section 6. */
+  CBS cbs, epki, algorithm, ciphertext;
+  CBS_init(&cbs, in, in_len);
+  if (!CBS_get_asn1(&cbs, &epki, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&epki, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
+      CBS_len(&epki) != 0 ||
+      CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
   }
-  in_len = ASN1_item_i2d(obj, &in, it);
-  if (!in) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ENCODE_ERROR);
-    return NULL;
+
+  if (!pbe_decrypt(&out, &out_len, &algorithm, pass_raw, pass_raw_len,
+                   CBS_data(&ciphertext), CBS_len(&ciphertext))) {
+    goto err;
   }
-  if (!pbe_crypt(algor, pass_raw, pass_raw_len, in, in_len, &oct->data, &crypt_len,
-                 1 /* encrypt */)) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ENCRYPT_ERROR);
-    OPENSSL_free(in);
-    return NULL;
+
+  if (out_len > LONG_MAX) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    goto err;
   }
-  oct->length = crypt_len;
-  OPENSSL_cleanse(in, in_len);
+
+  /* Convert back to legacy ASN.1 objects. */
+  const uint8_t *ptr = out;
+  ret = d2i_PKCS8_PRIV_KEY_INFO(NULL, &ptr, (long)out_len);
+  OPENSSL_cleanse(out, out_len);
+  if (ret == NULL || ptr != out + out_len) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
+    PKCS8_PRIV_KEY_INFO_free(ret);
+    ret = NULL;
+  }
+
+err:
   OPENSSL_free(in);
-  return oct;
+  OPENSSL_cleanse(out, out_len);
+  OPENSSL_free(out);
+  return ret;
 }
 
 X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
-                        int pass_len, uint8_t *salt, size_t salt_len,
+                        int pass_len, const uint8_t *salt, size_t salt_len,
                         int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
   uint8_t *pass_raw = NULL;
   size_t pass_raw_len = 0;
@@ -560,42 +516,97 @@
 
 X509_SIG *PKCS8_encrypt_pbe(int pbe_nid, const EVP_CIPHER *cipher,
                             const uint8_t *pass_raw, size_t pass_raw_len,
-                            uint8_t *salt, size_t salt_len,
+                            const uint8_t *salt, size_t salt_len,
                             int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
-  X509_SIG *pkcs8 = NULL;
-  X509_ALGOR *pbe;
+  X509_SIG *ret = NULL;
+  uint8_t *plaintext = NULL, *salt_buf = NULL, *der = NULL;
+  int plaintext_len = -1;
+  size_t der_len;
+  CBB cbb;
+  CBB_zero(&cbb);
+  EVP_CIPHER_CTX ctx;
+  EVP_CIPHER_CTX_init(&ctx);
 
-  pkcs8 = X509_SIG_new();
-  if (pkcs8 == NULL) {
-    OPENSSL_PUT_ERROR(PKCS8, ERR_R_MALLOC_FAILURE);
+  /* Generate a random salt if necessary. */
+  if (salt == NULL) {
+    if (salt_len == 0) {
+      salt_len = PKCS5_SALT_LEN;
+    }
+
+    salt_buf = OPENSSL_malloc(salt_len);
+    if (salt_buf == NULL ||
+        !RAND_bytes(salt_buf, salt_len)) {
+      goto err;
+    }
+
+    salt = salt_buf;
+  }
+
+  if (iterations <= 0) {
+    iterations = PKCS5_DEFAULT_ITERATIONS;
+  }
+
+  /* Convert the input from the legacy ASN.1 format. */
+  plaintext_len = i2d_PKCS8_PRIV_KEY_INFO(p8inf, &plaintext);
+  if (plaintext_len < 0) {
     goto err;
   }
 
+  CBB epki;
+  if (!CBB_init(&cbb, 128) ||
+      !CBB_add_asn1(&cbb, &epki, CBS_ASN1_SEQUENCE)) {
+    goto err;
+  }
+
+  int alg_ok;
   if (pbe_nid == -1) {
-    pbe = PKCS5_pbe2_set(cipher, iterations, salt, salt_len);
+    alg_ok = PKCS5_pbe2_encrypt_init(&epki, &ctx, cipher, (unsigned)iterations,
+                                     pass_raw, pass_raw_len, salt, salt_len);
   } else {
-    pbe = PKCS5_pbe_set(pbe_nid, iterations, salt, salt_len);
+    alg_ok = pkcs12_pbe_encrypt_init(&epki, &ctx, pbe_nid, (unsigned)iterations,
+                                     pass_raw, pass_raw_len, salt, salt_len);
   }
-  if (!pbe) {
-    OPENSSL_PUT_ERROR(PKCS8, ERR_R_ASN1_LIB);
+  if (!alg_ok) {
     goto err;
   }
 
-  X509_ALGOR_free(pkcs8->algor);
-  pkcs8->algor = pbe;
-  M_ASN1_OCTET_STRING_free(pkcs8->digest);
-  pkcs8->digest = pkcs12_item_i2d_encrypt(
-      pbe, ASN1_ITEM_rptr(PKCS8_PRIV_KEY_INFO), pass_raw, pass_raw_len, p8inf);
-  if (!pkcs8->digest) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ENCRYPT_ERROR);
+  size_t max_out = (size_t)plaintext_len + EVP_CIPHER_CTX_block_size(&ctx);
+  if (max_out < (size_t)plaintext_len) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_TOO_LONG);
     goto err;
   }
 
-  return pkcs8;
+  CBB ciphertext;
+  uint8_t *out;
+  int n1, n2;
+  if (!CBB_add_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
+      !CBB_reserve(&ciphertext, &out, max_out) ||
+      !EVP_CipherUpdate(&ctx, out, &n1, plaintext, plaintext_len) ||
+      !EVP_CipherFinal_ex(&ctx, out + n1, &n2) ||
+      !CBB_did_write(&ciphertext, n1 + n2) ||
+      !CBB_finish(&cbb, &der, &der_len)) {
+    goto err;
+  }
+
+  /* Convert back to legacy ASN.1 objects. */
+  const uint8_t *ptr = der;
+  ret = d2i_X509_SIG(NULL, &ptr, der_len);
+  if (ret == NULL || ptr != der + der_len) {
+    OPENSSL_PUT_ERROR(PKCS8, ERR_R_INTERNAL_ERROR);
+    X509_SIG_free(ret);
+    ret = NULL;
+  }
 
 err:
-  X509_SIG_free(pkcs8);
-  return NULL;
+  if (plaintext_len > 0) {
+    OPENSSL_cleanse(plaintext, plaintext_len);
+  }
+  OPENSSL_free(plaintext);
+  OPENSSL_free(salt_buf);
+  OPENSSL_free(der);
+  CBB_cleanup(&cbb);
+  EVP_CIPHER_CTX_cleanup(&ctx);
+  return ret;
 }
 
 EVP_PKEY *EVP_PKCS82PKEY(PKCS8_PRIV_KEY_INFO *p8) {
@@ -655,32 +666,21 @@
   size_t password_len;
 };
 
-static int PKCS12_handle_content_info(CBS *content_info, unsigned depth,
-                                      struct pkcs12_context *ctx);
-
-/* PKCS12_handle_content_infos parses a series of PKCS#7 ContentInfos in a
- * SEQUENCE. */
-static int PKCS12_handle_content_infos(CBS *content_infos,
-                                       unsigned depth,
-                                       struct pkcs12_context *ctx) {
+/* PKCS12_handle_sequence parses a BER-encoded SEQUENCE of elements in a PKCS#12
+ * structure. */
+static int PKCS12_handle_sequence(
+    CBS *sequence, struct pkcs12_context *ctx,
+    int (*handle_element)(CBS *cbs, struct pkcs12_context *ctx)) {
   uint8_t *der_bytes = NULL;
   size_t der_len;
   CBS in;
   int ret = 0;
 
-  /* Generally we only expect depths 0 (the top level, with a
-   * pkcs7-encryptedData and a pkcs7-data) and depth 1 (the various PKCS#12
-   * bags). */
-  if (depth > 3) {
-    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_PKCS12_TOO_DEEPLY_NESTED);
-    return 0;
-  }
-
   /* Although a BER->DER conversion is done at the beginning of |PKCS12_parse|,
    * the ASN.1 data gets wrapped in OCTETSTRINGs and/or encrypted and the
    * conversion cannot see through those wrappings. So each time we step
    * through one we need to convert to DER again. */
-  if (!CBS_asn1_ber_to_der(content_infos, &der_bytes, &der_len)) {
+  if (!CBS_asn1_ber_to_der(sequence, &der_bytes, &der_len)) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
     return 0;
   }
@@ -688,30 +688,28 @@
   if (der_bytes != NULL) {
     CBS_init(&in, der_bytes, der_len);
   } else {
-    CBS_init(&in, CBS_data(content_infos), CBS_len(content_infos));
+    CBS_init(&in, CBS_data(sequence), CBS_len(sequence));
   }
 
-  if (!CBS_get_asn1(&in, &in, CBS_ASN1_SEQUENCE)) {
+  CBS child;
+  if (!CBS_get_asn1(&in, &child, CBS_ASN1_SEQUENCE) ||
+      CBS_len(&in) != 0) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
     goto err;
   }
 
-  while (CBS_len(&in) > 0) {
-    CBS content_info;
-    if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE)) {
+  while (CBS_len(&child) > 0) {
+    CBS element;
+    if (!CBS_get_asn1(&child, &element, CBS_ASN1_SEQUENCE)) {
       OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
       goto err;
     }
 
-    if (!PKCS12_handle_content_info(&content_info, depth + 1, ctx)) {
+    if (!handle_element(&element, ctx)) {
       goto err;
     }
   }
 
-  /* NSS includes additional data after the SEQUENCE, but it's an (unwrapped)
-   * copy of the same encrypted private key (with the same IV and
-   * ciphertext)! */
-
   ret = 1;
 
 err:
@@ -719,17 +717,116 @@
   return ret;
 }
 
+/* PKCS12_handle_safe_bag parses a single SafeBag element in a PKCS#12
+ * structure. */
+static int PKCS12_handle_safe_bag(CBS *safe_bag, struct pkcs12_context *ctx) {
+  CBS bag_id, wrapped_value;
+  if (!CBS_get_asn1(safe_bag, &bag_id, CBS_ASN1_OBJECT) ||
+      !CBS_get_asn1(safe_bag, &wrapped_value,
+                        CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)
+      /* Ignore the bagAttributes field. */) {
+    OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+    return 0;
+  }
+
+  int nid = OBJ_cbs2nid(&bag_id);
+  if (nid == NID_pkcs8ShroudedKeyBag) {
+    /* See RFC 7292, section 4.2.2. */
+    if (*ctx->out_key) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_MULTIPLE_PRIVATE_KEYS_IN_PKCS12);
+      return 0;
+    }
+
+    if (CBS_len(&wrapped_value) > LONG_MAX) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      return 0;
+    }
+
+    /* |encrypted| isn't actually an X.509 signature, but it has the same
+     * structure as one and so |X509_SIG| is reused to store it. */
+    const uint8_t *inp = CBS_data(&wrapped_value);
+    X509_SIG *encrypted =
+        d2i_X509_SIG(NULL, &inp, (long)CBS_len(&wrapped_value));
+    if (encrypted == NULL) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      return 0;
+    }
+    if (inp != CBS_data(&wrapped_value) + CBS_len(&wrapped_value)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      X509_SIG_free(encrypted);
+      return 0;
+    }
+
+    PKCS8_PRIV_KEY_INFO *pki =
+        PKCS8_decrypt_pbe(encrypted, ctx->password, ctx->password_len);
+    X509_SIG_free(encrypted);
+    if (pki == NULL) {
+      return 0;
+    }
+
+    *ctx->out_key = EVP_PKCS82PKEY(pki);
+    PKCS8_PRIV_KEY_INFO_free(pki);
+    return ctx->out_key != NULL;
+  }
+
+  if (nid == NID_certBag) {
+    /* See RFC 7292, section 4.2.3. */
+    CBS cert_bag, cert_type, wrapped_cert, cert;
+    if (!CBS_get_asn1(&wrapped_value, &cert_bag, CBS_ASN1_SEQUENCE) ||
+        !CBS_get_asn1(&cert_bag, &cert_type, CBS_ASN1_OBJECT) ||
+        !CBS_get_asn1(&cert_bag, &wrapped_cert,
+                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+        !CBS_get_asn1(&wrapped_cert, &cert, CBS_ASN1_OCTETSTRING)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      return 0;
+    }
+
+    if (OBJ_cbs2nid(&cert_type) != NID_x509Certificate) {
+      return 1;
+    }
+
+    if (CBS_len(&cert) > LONG_MAX) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      return 0;
+    }
+
+    const uint8_t *inp = CBS_data(&cert);
+    X509 *x509 = d2i_X509(NULL, &inp, (long)CBS_len(&cert));
+    if (!x509) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      return 0;
+    }
+
+    if (inp != CBS_data(&cert) + CBS_len(&cert)) {
+      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+      X509_free(x509);
+      return 0;
+    }
+
+    if (0 == sk_X509_push(ctx->out_certs, x509)) {
+      X509_free(x509);
+      return 0;
+    }
+
+    return 1;
+  }
+
+  /* Unknown element type - ignore it. */
+  return 1;
+}
+
 /* PKCS12_handle_content_info parses a single PKCS#7 ContentInfo element in a
  * PKCS#12 structure. */
-static int PKCS12_handle_content_info(CBS *content_info, unsigned depth,
+static int PKCS12_handle_content_info(CBS *content_info,
                                       struct pkcs12_context *ctx) {
-  CBS content_type, wrapped_contents, contents, content_infos;
+  CBS content_type, wrapped_contents, contents;
   int nid, ret = 0;
   uint8_t *storage = NULL;
 
   if (!CBS_get_asn1(content_info, &content_type, CBS_ASN1_OBJECT) ||
       !CBS_get_asn1(content_info, &wrapped_contents,
-                        CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
+                        CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+      CBS_len(content_info) != 0) {
     OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
     goto err;
   }
@@ -742,8 +839,6 @@
      * encrypted certificate bag and it's generally encrypted with 40-bit
      * RC2-CBC. */
     CBS version_bytes, eci, contents_type, ai, encrypted_contents;
-    X509_ALGOR *algor = NULL;
-    const uint8_t *inp;
     uint8_t *out;
     size_t out_len;
 
@@ -755,7 +850,7 @@
         !CBS_get_asn1(&eci, &contents_type, CBS_ASN1_OBJECT) ||
         /* AlgorithmIdentifier, see
          * https://tools.ietf.org/html/rfc5280#section-4.1.1.2 */
-        !CBS_get_asn1_element(&eci, &ai, CBS_ASN1_SEQUENCE) ||
+        !CBS_get_asn1(&eci, &ai, CBS_ASN1_SEQUENCE) ||
         !CBS_get_asn1_implicit_string(
             &eci, &encrypted_contents, &storage,
             CBS_ASN1_CONTEXT_SPECIFIC | 0, CBS_ASN1_OCTETSTRING)) {
@@ -763,122 +858,32 @@
       goto err;
     }
 
-    if (OBJ_cbs2nid(&contents_type) != NID_pkcs7_data ||
-        CBS_len(&ai) > LONG_MAX) {
+    if (OBJ_cbs2nid(&contents_type) != NID_pkcs7_data) {
       OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
       goto err;
     }
 
-    inp = CBS_data(&ai);
-    algor = d2i_X509_ALGOR(NULL, &inp, (long)CBS_len(&ai));
-    if (algor == NULL) {
-      goto err;
-    }
-    if (inp != CBS_data(&ai) + CBS_len(&ai)) {
-      X509_ALGOR_free(algor);
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
+    if (!pbe_decrypt(&out, &out_len, &ai, ctx->password, ctx->password_len,
+                     CBS_data(&encrypted_contents),
+                     CBS_len(&encrypted_contents))) {
       goto err;
     }
 
-    if (!pbe_crypt(algor, ctx->password, ctx->password_len,
-                   CBS_data(&encrypted_contents), CBS_len(&encrypted_contents),
-                   &out, &out_len, 0 /* decrypt */)) {
-      X509_ALGOR_free(algor);
-      goto err;
-    }
-    X509_ALGOR_free(algor);
-
-    CBS_init(&content_infos, out, out_len);
-    ret = PKCS12_handle_content_infos(&content_infos, depth + 1, ctx);
+    CBS safe_contents;
+    CBS_init(&safe_contents, out, out_len);
+    ret = PKCS12_handle_sequence(&safe_contents, ctx, PKCS12_handle_safe_bag);
     OPENSSL_free(out);
   } else if (nid == NID_pkcs7_data) {
     CBS octet_string_contents;
 
     if (!CBS_get_asn1(&wrapped_contents, &octet_string_contents,
-                          CBS_ASN1_OCTETSTRING)) {
+                      CBS_ASN1_OCTETSTRING)) {
       OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
       goto err;
     }
 
-    ret = PKCS12_handle_content_infos(&octet_string_contents, depth + 1, ctx);
-  } else if (nid == NID_pkcs8ShroudedKeyBag) {
-    /* See ftp://ftp.rsasecurity.com/pub/pkcs/pkcs-12/pkcs-12v1.pdf, section
-     * 4.2.2. */
-    const uint8_t *inp = CBS_data(&wrapped_contents);
-    PKCS8_PRIV_KEY_INFO *pki = NULL;
-    X509_SIG *encrypted = NULL;
-
-    if (*ctx->out_key) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_MULTIPLE_PRIVATE_KEYS_IN_PKCS12);
-      goto err;
-    }
-
-    if (CBS_len(&wrapped_contents) > LONG_MAX) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-      goto err;
-    }
-
-    /* encrypted isn't actually an X.509 signature, but it has the same
-     * structure as one and so |X509_SIG| is reused to store it. */
-    encrypted = d2i_X509_SIG(NULL, &inp, (long)CBS_len(&wrapped_contents));
-    if (encrypted == NULL) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-      goto err;
-    }
-    if (inp != CBS_data(&wrapped_contents) + CBS_len(&wrapped_contents)) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-      X509_SIG_free(encrypted);
-      goto err;
-    }
-
-    pki = PKCS8_decrypt_pbe(encrypted, ctx->password, ctx->password_len);
-    X509_SIG_free(encrypted);
-    if (pki == NULL) {
-      goto err;
-    }
-
-    *ctx->out_key = EVP_PKCS82PKEY(pki);
-    PKCS8_PRIV_KEY_INFO_free(pki);
-
-    if (ctx->out_key == NULL) {
-      goto err;
-    }
-    ret = 1;
-  } else if (nid == NID_certBag) {
-    CBS cert_bag, cert_type, wrapped_cert, cert;
-
-    if (!CBS_get_asn1(&wrapped_contents, &cert_bag, CBS_ASN1_SEQUENCE) ||
-        !CBS_get_asn1(&cert_bag, &cert_type, CBS_ASN1_OBJECT) ||
-        !CBS_get_asn1(&cert_bag, &wrapped_cert,
-                      CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
-        !CBS_get_asn1(&wrapped_cert, &cert, CBS_ASN1_OCTETSTRING)) {
-      OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-      goto err;
-    }
-
-    if (OBJ_cbs2nid(&cert_type) == NID_x509Certificate) {
-      if (CBS_len(&cert) > LONG_MAX) {
-        OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-        goto err;
-      }
-      const uint8_t *inp = CBS_data(&cert);
-      X509 *x509 = d2i_X509(NULL, &inp, (long)CBS_len(&cert));
-      if (!x509) {
-        OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-        goto err;
-      }
-      if (inp != CBS_data(&cert) + CBS_len(&cert)) {
-        OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
-        X509_free(x509);
-        goto err;
-      }
-
-      if (0 == sk_X509_push(ctx->out_certs, x509)) {
-        X509_free(x509);
-        goto err;
-      }
-    }
-    ret = 1;
+    ret = PKCS12_handle_sequence(&octet_string_contents, ctx,
+                                 PKCS12_handle_safe_bag);
   } else {
     /* Unknown element type - ignore it. */
     ret = 1;
@@ -995,7 +1000,7 @@
     iterations = 1;
     if (CBS_len(&mac_data) > 0) {
       if (!CBS_get_asn1_uint64(&mac_data, &iterations) ||
-          iterations > INT_MAX) {
+          iterations > UINT_MAX) {
         OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
         goto err;
       }
@@ -1026,7 +1031,7 @@
   }
 
   /* authsafes contains a series of PKCS#7 ContentInfos. */
-  if (!PKCS12_handle_content_infos(&authsafes, 0, &ctx)) {
+  if (!PKCS12_handle_sequence(&authsafes, &ctx, PKCS12_handle_content_info)) {
     goto err;
   }
 
@@ -1054,7 +1059,8 @@
   size_t ber_len;
 };
 
-PKCS12* d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes, size_t ber_len) {
+PKCS12 *d2i_PKCS12(PKCS12 **out_p12, const uint8_t **ber_bytes,
+                   size_t ber_len) {
   PKCS12 *p12;
 
   p12 = OPENSSL_malloc(sizeof(PKCS12));
diff --git a/src/crypto/pkcs8/pkcs8_test.cc b/src/crypto/pkcs8/pkcs8_test.cc
index cbb2043..1196f9f 100644
--- a/src/crypto/pkcs8/pkcs8_test.cc
+++ b/src/crypto/pkcs8/pkcs8_test.cc
@@ -21,6 +21,8 @@
 #include <openssl/pkcs8.h>
 #include <openssl/x509.h>
 
+#include "../internal.h"
+
 
 /* kDER is a PKCS#8 encrypted private key. It was generated with:
  *
@@ -60,7 +62,89 @@
   0xd6, 0x2d,
 };
 
-static bool test(const uint8_t *der, size_t der_len) {
+/* kNullPassword is a PKCS#8 encrypted private key using the null password. */
+static const uint8_t kNullPassword[] = {
+    0x30, 0x81, 0xb0, 0x30, 0x1b, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0d, 0x04, 0x08, 0xb2, 0xfe, 0x68,
+    0xc2, 0xea, 0x0f, 0x10, 0x9c, 0x02, 0x01, 0x01, 0x04, 0x81, 0x90, 0xe2,
+    0xf6, 0x1c, 0xca, 0xad, 0x64, 0x30, 0xbf, 0x88, 0x04, 0x35, 0xe5, 0x0f,
+    0x11, 0x49, 0x06, 0x01, 0x14, 0x33, 0x80, 0xa2, 0x78, 0x44, 0x5b, 0xaa,
+    0x0d, 0xd7, 0x00, 0x36, 0x9d, 0x91, 0x97, 0x37, 0x20, 0x7b, 0x27, 0xc1,
+    0xa0, 0xa2, 0x73, 0x06, 0x15, 0xdf, 0xc8, 0x13, 0x9b, 0xc9, 0x8c, 0x9c,
+    0xce, 0x00, 0xd0, 0xc8, 0x42, 0xc1, 0xda, 0x2b, 0x07, 0x2b, 0x12, 0xa3,
+    0xce, 0x10, 0x39, 0x7a, 0xf1, 0x55, 0x69, 0x8d, 0xa5, 0xc4, 0x2a, 0x00,
+    0x0d, 0x94, 0xc6, 0xde, 0x6a, 0x3d, 0xb7, 0xe5, 0x6d, 0x59, 0x3e, 0x09,
+    0xb5, 0xe3, 0x3e, 0xfc, 0x50, 0x56, 0xe9, 0x50, 0x42, 0x7c, 0xe7, 0xf0,
+    0x19, 0xbd, 0x31, 0xa7, 0x85, 0x47, 0xb3, 0xe9, 0xb3, 0x50, 0x3c, 0xc9,
+    0x32, 0x37, 0x1a, 0x93, 0x78, 0x48, 0x78, 0x82, 0xde, 0xad, 0x5c, 0xf2,
+    0xcf, 0xf2, 0xbb, 0x2c, 0x44, 0x05, 0x7f, 0x4a, 0xf9, 0xb1, 0x2b, 0xdd,
+    0x49, 0xf6, 0x7e, 0xd0, 0x42, 0xaa, 0x14, 0x3c, 0x24, 0x77, 0xb4,
+};
+
+/* kNullPasswordNSS is a PKCS#8 encrypted private key using the null password
+ * and generated by NSS. */
+static const uint8_t kNullPasswordNSS[] = {
+    0x30, 0x81, 0xb8, 0x30, 0x23, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+    0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x15, 0x04, 0x10, 0x3f, 0xac, 0xe9,
+    0x38, 0xdb, 0x40, 0x6b, 0x26, 0x89, 0x09, 0x73, 0x18, 0x8d, 0x7f, 0x1c,
+    0x82, 0x02, 0x01, 0x01, 0x04, 0x81, 0x90, 0x5e, 0x5e, 0x11, 0xef, 0xbb,
+    0x7c, 0x4d, 0xec, 0xc0, 0xdc, 0xc7, 0x23, 0xd2, 0xc4, 0x77, 0xbc, 0xf4,
+    0x5d, 0x59, 0x4c, 0x07, 0xc2, 0x8a, 0x26, 0xfa, 0x25, 0x1c, 0xaa, 0x42,
+    0xed, 0xd0, 0xed, 0xbb, 0x5c, 0xe9, 0x13, 0x07, 0xaa, 0xdd, 0x52, 0x3c,
+    0x65, 0x25, 0xbf, 0x94, 0x02, 0xaf, 0xd6, 0x97, 0xe9, 0x33, 0x00, 0x76,
+    0x64, 0x4a, 0x73, 0xab, 0xfb, 0x99, 0x6e, 0x83, 0x12, 0x05, 0x86, 0x72,
+    0x6c, 0xd5, 0xa4, 0xcf, 0xb1, 0xd5, 0x4d, 0x54, 0x87, 0x8b, 0x4b, 0x95,
+    0x1d, 0xcd, 0xf3, 0xfe, 0xa8, 0xda, 0xe0, 0xb6, 0x72, 0x13, 0x3f, 0x2e,
+    0x66, 0xe0, 0xb9, 0x2e, 0xfa, 0x69, 0x40, 0xbe, 0xd7, 0x67, 0x6e, 0x53,
+    0x2b, 0x3f, 0x53, 0xe5, 0x39, 0x54, 0x77, 0xe1, 0x1d, 0xe6, 0x81, 0x92,
+    0x58, 0x82, 0x14, 0xfb, 0x47, 0x85, 0x3c, 0xc3, 0xdf, 0xdd, 0xcc, 0x79,
+    0x9f, 0x41, 0x83, 0x72, 0xf2, 0x0a, 0xe9, 0xe1, 0x2c, 0x12, 0xb0, 0xb0,
+    0x0a, 0xb2, 0x1d, 0xca, 0x15, 0xb2, 0xca,
+};
+
+/* kEmptyPasswordOpenSSL is a PKCS#8 encrypted private key using the empty
+ * password and generated by OpenSSL. */
+static const uint8_t kEmptyPasswordOpenSSL[] = {
+    0x30, 0x82, 0x01, 0xa1, 0x30, 0x1b, 0x06, 0x0a, 0x2a, 0x86, 0x48, 0x86,
+    0xf7, 0x0d, 0x01, 0x0c, 0x01, 0x03, 0x30, 0x0d, 0x04, 0x08, 0x86, 0xaa,
+    0xd7, 0xdf, 0x3b, 0x91, 0x97, 0x60, 0x02, 0x01, 0x01, 0x04, 0x82, 0x01,
+    0x80, 0xcb, 0x2a, 0x14, 0xaa, 0x4f, 0x38, 0x4c, 0xe1, 0x49, 0x00, 0xe2,
+    0x1a, 0x3a, 0x75, 0x87, 0x7e, 0x3d, 0xea, 0x4d, 0x53, 0xd4, 0x46, 0x47,
+    0x23, 0x8f, 0xa1, 0x72, 0x51, 0x92, 0x86, 0x8b, 0xeb, 0x53, 0xe6, 0x6a,
+    0x0a, 0x6b, 0xb6, 0xa0, 0xdc, 0x0f, 0xdc, 0x20, 0xc3, 0x45, 0x85, 0xf1,
+    0x95, 0x90, 0x5c, 0xf4, 0xfa, 0xee, 0x47, 0xaf, 0x35, 0xd0, 0xd0, 0xd3,
+    0x14, 0xde, 0x0d, 0xca, 0x1b, 0xd3, 0xbb, 0x20, 0xec, 0x9d, 0x6a, 0xd4,
+    0xc1, 0xce, 0x60, 0x81, 0xab, 0x0c, 0x72, 0x10, 0xfa, 0x28, 0x3c, 0xac,
+    0x87, 0x7b, 0x82, 0x85, 0x00, 0xb8, 0x58, 0x9c, 0x07, 0xc4, 0x7d, 0xa9,
+    0xc5, 0x94, 0x95, 0xf7, 0x23, 0x93, 0x3f, 0xed, 0xef, 0x92, 0x55, 0x25,
+    0x74, 0xbb, 0xd3, 0xd1, 0x67, 0x3b, 0x3d, 0x5a, 0xfe, 0x84, 0xf8, 0x97,
+    0x7d, 0x7c, 0x01, 0xc7, 0xd7, 0x0d, 0xf8, 0xc3, 0x6d, 0xd6, 0xf1, 0xaa,
+    0x9d, 0x1f, 0x69, 0x97, 0x45, 0x06, 0xc4, 0x1c, 0x95, 0x3c, 0xe0, 0xef,
+    0x11, 0xb2, 0xb3, 0x72, 0x91, 0x9e, 0x7d, 0x0f, 0x7f, 0xc8, 0xf6, 0x64,
+    0x49, 0x5e, 0x3c, 0x53, 0x37, 0x79, 0x03, 0x1c, 0x3f, 0x29, 0x6c, 0x6b,
+    0xea, 0x4c, 0x35, 0x9b, 0x6d, 0x1b, 0x59, 0x43, 0x4c, 0x14, 0x47, 0x2a,
+    0x36, 0x39, 0x2a, 0xd8, 0x96, 0x90, 0xdc, 0xfc, 0xd2, 0xdd, 0x23, 0x0e,
+    0x2c, 0xb3, 0x83, 0xf9, 0xf2, 0xe3, 0xe6, 0x99, 0x53, 0x57, 0x33, 0xc5,
+    0x5f, 0xf9, 0xfd, 0x56, 0x0b, 0x32, 0xd4, 0xf3, 0x9d, 0x5b, 0x34, 0xe5,
+    0x94, 0xbf, 0xb6, 0xc0, 0xce, 0xe1, 0x73, 0x5c, 0x02, 0x7a, 0x4c, 0xed,
+    0xde, 0x23, 0x38, 0x89, 0x9f, 0xcd, 0x51, 0xf3, 0x90, 0x80, 0xd3, 0x4b,
+    0x83, 0xd3, 0xee, 0xf2, 0x9e, 0x35, 0x91, 0xa5, 0xa3, 0xc0, 0x5c, 0xce,
+    0xdb, 0xaa, 0x70, 0x1e, 0x1d, 0xc1, 0x44, 0xea, 0x3b, 0xa7, 0x5a, 0x11,
+    0xd1, 0xf3, 0xf3, 0xd0, 0xf4, 0x5a, 0xc4, 0x99, 0xaf, 0x8d, 0xe2, 0xbc,
+    0xa2, 0xb9, 0x3d, 0x86, 0x5e, 0xba, 0xa0, 0xdf, 0x78, 0x81, 0x7c, 0x54,
+    0x31, 0xe3, 0x98, 0xb5, 0x46, 0xcb, 0x4d, 0x26, 0x4b, 0xf8, 0xac, 0x3a,
+    0x54, 0x1b, 0x77, 0x5a, 0x18, 0xa5, 0x43, 0x0e, 0x14, 0xde, 0x7b, 0xb7,
+    0x4e, 0x45, 0x99, 0x03, 0xd1, 0x3d, 0x18, 0xb2, 0x36, 0x00, 0x48, 0x07,
+    0x72, 0xbb, 0x4f, 0x21, 0x25, 0x3e, 0xda, 0x25, 0x24, 0x5b, 0xc8, 0xa0,
+    0x28, 0xd5, 0x9b, 0x96, 0x87, 0x07, 0x77, 0x84, 0xff, 0xd7, 0xac, 0x71,
+    0xf6, 0x61, 0x63, 0x0b, 0xfb, 0x42, 0xfd, 0x52, 0xf4, 0xc4, 0x35, 0x0c,
+    0xc2, 0xc1, 0x55, 0x22, 0x42, 0x2f, 0x13, 0x7d, 0x93, 0x27, 0xc8, 0x11,
+    0x35, 0xc5, 0xe3, 0xc5, 0xaa, 0x15, 0x3c, 0xac, 0x30, 0xbc, 0x45, 0x16,
+    0xed,
+};
+
+static bool TestDecrypt(const uint8_t *der, size_t der_len,
+                        const char *password) {
   const uint8_t *data = der;
   bssl::UniquePtr<X509_SIG> sig(d2i_X509_SIG(NULL, &data, der_len));
   if (sig.get() == NULL || data != der + der_len) {
@@ -68,8 +152,8 @@
     return false;
   }
 
-  static const char kPassword[] = "testing";
-  bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> keypair(PKCS8_decrypt(sig.get(), kPassword, -1));
+  bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> keypair(
+      PKCS8_decrypt(sig.get(), password, -1));
   if (!keypair) {
     fprintf(stderr, "PKCS8_decrypt failed.\n");
     ERR_print_errors_fp(stderr);
@@ -79,8 +163,92 @@
   return true;
 }
 
+static bool TestRoundTrip(int pbe_nid, const EVP_CIPHER *cipher,
+                          const char *password, const uint8_t *salt,
+                          size_t salt_len, int iterations) {
+  static const uint8_t kSampleKey[] = {
+      0x30, 0x81, 0x87, 0x02, 0x01, 0x00, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
+      0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
+      0x03, 0x01, 0x07, 0x04, 0x6d, 0x30, 0x6b, 0x02, 0x01, 0x01, 0x04, 0x20,
+      0x8a, 0x87, 0x2f, 0xb6, 0x28, 0x93, 0xc4, 0xd1, 0xff, 0xc5, 0xb9, 0xf0,
+      0xf9, 0x17, 0x58, 0x06, 0x9f, 0x83, 0x52, 0xe0, 0x8f, 0xa0, 0x5a, 0x49,
+      0xf8, 0xdb, 0x92, 0x6c, 0xb5, 0x72, 0x87, 0x25, 0xa1, 0x44, 0x03, 0x42,
+      0x00, 0x04, 0x2c, 0x15, 0x0f, 0x42, 0x9c, 0xe7, 0x0f, 0x21, 0x6c, 0x25,
+      0x2c, 0xf5, 0xe0, 0x62, 0xce, 0x1f, 0x63, 0x9c, 0xd5, 0xd1, 0x65, 0xc7,
+      0xf8, 0x94, 0x24, 0x07, 0x2c, 0x27, 0x19, 0x7d, 0x78, 0xb3, 0x3b, 0x92,
+      0x0e, 0x95, 0xcd, 0xb6, 0x64, 0xe9, 0x90, 0xdc, 0xf0, 0xcf, 0xea, 0x0d,
+      0x94, 0xe2, 0xa8, 0xe6, 0xaf, 0x9d, 0x0e, 0x58, 0x05, 0x6e, 0x65, 0x31,
+      0x04, 0x92, 0x5b, 0x9f, 0xe6, 0xc9,
+  };
+
+  const uint8_t *ptr = kSampleKey;
+  bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> key(
+      d2i_PKCS8_PRIV_KEY_INFO(nullptr, &ptr, sizeof(kSampleKey)));
+  if (!key || ptr != kSampleKey + sizeof(kSampleKey)) {
+    return false;
+  }
+
+  bssl::UniquePtr<X509_SIG> encrypted(PKCS8_encrypt(
+      pbe_nid, cipher, password, -1, salt, salt_len, iterations, key.get()));
+  if (!encrypted) {
+    fprintf(stderr, "Failed to encrypt private key.\n");
+    return false;
+  }
+
+  bssl::UniquePtr<PKCS8_PRIV_KEY_INFO> key2(
+      PKCS8_decrypt(encrypted.get(), password, -1));
+  if (!key2) {
+    fprintf(stderr, "Failed to decrypt private key.\n");
+    return false;
+  }
+
+  uint8_t *encoded = nullptr;
+  int len = i2d_PKCS8_PRIV_KEY_INFO(key2.get(), &encoded);
+  bssl::UniquePtr<uint8_t> free_encoded(encoded);
+  if (len < 0 ||
+      static_cast<size_t>(len) != sizeof(kSampleKey) ||
+      OPENSSL_memcmp(encoded, kSampleKey, sizeof(kSampleKey)) != 0) {
+    fprintf(stderr, "Decrypted private key did not round-trip.");
+    return false;
+  }
+
+  return true;
+}
+
 int main(int argc, char **argv) {
-  if (!test(kDER, sizeof(kDER))) {
+  CRYPTO_library_init();
+
+  if (!TestDecrypt(kDER, sizeof(kDER), "testing") ||
+      !TestDecrypt(kNullPassword, sizeof(kNullPassword), NULL) ||
+      !TestDecrypt(kNullPasswordNSS, sizeof(kNullPasswordNSS), NULL) ||
+      !TestDecrypt(kEmptyPasswordOpenSSL, sizeof(kEmptyPasswordOpenSSL), "") ||
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
+                     "password", nullptr, 0, 10) ||
+      // Vary the salt
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
+                     "password", nullptr, 4, 10) ||
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
+                     "password", (const uint8_t *)"salt", 4, 10) ||
+      // Vary the iteration count.
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr,
+                     "password", nullptr, 0, 1) ||
+      // Vary the password.
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr, "",
+                     nullptr, 0, 1) ||
+      !TestRoundTrip(NID_pbe_WithSHA1And3_Key_TripleDES_CBC, nullptr, nullptr,
+                     nullptr, 0, 1) ||
+      // Vary the PBE suite.
+      !TestRoundTrip(NID_pbe_WithSHA1And40BitRC2_CBC, nullptr, "password",
+                     nullptr, 0, 10) ||
+      !TestRoundTrip(NID_pbe_WithSHA1And128BitRC4, nullptr, "password", nullptr,
+                     0, 10) ||
+      // Test PBES2.
+      !TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 0, 10) ||
+      !TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 4, 10) ||
+      !TestRoundTrip(-1, EVP_aes_128_cbc(), "password", (const uint8_t *)"salt",
+                     4, 10) ||
+      !TestRoundTrip(-1, EVP_aes_128_cbc(), "password", nullptr, 0, 1) ||
+      !TestRoundTrip(-1, EVP_rc2_cbc(), "password", nullptr, 0, 10)) {
     return 1;
   }
 
diff --git a/src/crypto/rand/CMakeLists.txt b/src/crypto/rand/CMakeLists.txt
index c66d2ee..f7c11f1 100644
--- a/src/crypto/rand/CMakeLists.txt
+++ b/src/crypto/rand/CMakeLists.txt
@@ -14,6 +14,7 @@
   OBJECT
 
   deterministic.c
+  fuchsia.c
   rand.c
   urandom.c
   windows.c
diff --git a/src/crypto/rand/fuchsia.c b/src/crypto/rand/fuchsia.c
new file mode 100644
index 0000000..2e138d0
--- /dev/null
+++ b/src/crypto/rand/fuchsia.c
@@ -0,0 +1,43 @@
+/* Copyright (c) 2017, Google Inc.
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
+
+#include <openssl/rand.h>
+
+#if defined(OPENSSL_FUCHSIA) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+
+#include <limits.h>
+#include <stdlib.h>
+
+#include <magenta/syscalls.h>
+
+#include "internal.h"
+
+void CRYPTO_sysrand(uint8_t *out, size_t requested) {
+  while (requested > 0) {
+    size_t output_bytes_this_pass = MX_CPRNG_DRAW_MAX_LEN;
+    if (requested < output_bytes_this_pass) {
+      output_bytes_this_pass = requested;
+    }
+    size_t bytes_drawn;
+    mx_status_t status =
+        mx_cprng_draw(out, output_bytes_this_pass, &bytes_drawn);
+    if (status != NO_ERROR) {
+      abort();
+    }
+    requested -= bytes_drawn;
+    out += bytes_drawn;
+  }
+}
+
+#endif /* OPENSSL_FUCHSIA && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
diff --git a/src/crypto/rand/urandom.c b/src/crypto/rand/urandom.c
index 2233203..3ccd940 100644
--- a/src/crypto/rand/urandom.c
+++ b/src/crypto/rand/urandom.c
@@ -16,7 +16,8 @@
 
 #include <openssl/rand.h>
 
-#if !defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_FUCHSIA) && \
+    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
 
 #include <assert.h>
 #include <errno.h>
@@ -328,4 +329,5 @@
   }
 }
 
-#endif  /* !OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
+#endif /* !OPENSSL_WINDOWS && !defined(OPENSSL_FUCHSIA) && \
+          !BORINGSSL_UNSAFE_DETERMINISTIC_MODE */
diff --git a/src/crypto/rsa/rsa_impl.c b/src/crypto/rsa/rsa_impl.c
index 3834be5..8e0aa9c 100644
--- a/src/crypto/rsa/rsa_impl.c
+++ b/src/crypto/rsa/rsa_impl.c
@@ -769,8 +769,6 @@
 int rsa_default_multi_prime_keygen(RSA *rsa, int bits, int num_primes,
                                    BIGNUM *e_value, BN_GENCB *cb) {
   BIGNUM *r0 = NULL, *r1 = NULL, *r2 = NULL, *r3 = NULL, *tmp;
-  BIGNUM local_r0, local_p;
-  BIGNUM *pr0, *p;
   int prime_bits, ok = -1, n = 0, i, j;
   BN_CTX *ctx = NULL;
   STACK_OF(RSA_additional_prime) *additional_primes = NULL;
@@ -999,9 +997,7 @@
       goto err;
     }
   }
-  pr0 = &local_r0;
-  BN_with_flags(pr0, r0, BN_FLG_CONSTTIME);
-  if (!BN_mod_inverse(rsa->d, rsa->e, pr0, ctx)) {
+  if (!BN_mod_inverse(rsa->d, rsa->e, r0, ctx)) {
     goto err; /* d */
   }
 
@@ -1019,10 +1015,9 @@
    * from constant-time, |bn_mod_inverse_secret_prime| uses the same modular
    * exponentation logic as in RSA private key operations and, if the RSAZ-1024
    * code is enabled, will be optimized for common RSA prime sizes. */
-  p = &local_p;
-  BN_with_flags(p, rsa->p, BN_FLG_CONSTTIME);
   if (!BN_MONT_CTX_set_locked(&rsa->mont_p, &rsa->lock, rsa->p, ctx) ||
-      !bn_mod_inverse_secret_prime(rsa->iqmp, rsa->q, p, ctx, rsa->mont_p)) {
+      !bn_mod_inverse_secret_prime(rsa->iqmp, rsa->q, rsa->p, ctx,
+                                   rsa->mont_p)) {
     goto err;
   }
 
diff --git a/src/crypto/x509/t_x509.c b/src/crypto/x509/t_x509.c
index d5d48ba..d4f6bba 100644
--- a/src/crypto/x509/t_x509.c
+++ b/src/crypto/x509/t_x509.c
@@ -54,6 +54,7 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <ctype.h>
 #include <openssl/asn1.h>
 #include <openssl/bio.h>
 #include <openssl/digest.h>
@@ -415,45 +416,84 @@
     return (0);
 }
 
-int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm)
-{
-    const char *v;
-    int gmt = 0;
-    int i;
-    int y = 0, M = 0, d = 0, h = 0, m = 0, s = 0;
+// consume_two_digits is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, has two leading digits, updates |*out| with
+// their value, updates |v| and |len|, and returns one. Otherwise, returns
+// zero.
+static int consume_two_digits(int* out, const char **v, int *len) {
+  if (*len < 2|| !isdigit((*v)[0]) || !isdigit((*v)[1])) {
+    return 0;
+  }
+  *out = ((*v)[0] - '0') * 10 + ((*v)[1] - '0');
+  *len -= 2;
+  *v += 2;
+  return 1;
+}
 
-    i = tm->length;
-    v = (const char *)tm->data;
+// consume_zulu_timezone is a helper function for ASN1_UTCTIME_print. If |*v|,
+// assumed to be |*len| bytes long, starts with "Z" then it updates |*v| and
+// |*len| and returns one. Otherwise returns zero.
+static int consume_zulu_timezone(const char **v, int *len) {
+  if (*len == 0 || (*v)[0] != 'Z') {
+    return 0;
+  }
 
-    if (i < 10)
-        goto err;
-    if (v[i - 1] == 'Z')
-        gmt = 1;
-    for (i = 0; i < 10; i++)
-        if ((v[i] > '9') || (v[i] < '0'))
-            goto err;
-    y = (v[0] - '0') * 10 + (v[1] - '0');
-    if (y < 50)
-        y += 100;
-    M = (v[2] - '0') * 10 + (v[3] - '0');
-    if ((M > 12) || (M < 1))
-        goto err;
-    d = (v[4] - '0') * 10 + (v[5] - '0');
-    h = (v[6] - '0') * 10 + (v[7] - '0');
-    m = (v[8] - '0') * 10 + (v[9] - '0');
-    if (tm->length >= 12 &&
-        (v[10] >= '0') && (v[10] <= '9') && (v[11] >= '0') && (v[11] <= '9'))
-        s = (v[10] - '0') * 10 + (v[11] - '0');
+  *len -= 1;
+  *v += 1;
+  return 1;
+}
 
-    if (BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s",
-                   mon[M - 1], d, h, m, s, y + 1900,
-                   (gmt) ? " GMT" : "") <= 0)
-        return (0);
-    else
-        return (1);
- err:
-    BIO_write(bp, "Bad time value", 14);
-    return (0);
+int ASN1_UTCTIME_print(BIO *bp, const ASN1_UTCTIME *tm) {
+  const char *v = (const char *)tm->data;
+  int len = tm->length;
+  int Y = 0, M = 0, D = 0, h = 0, m = 0, s = 0;
+
+  // YYMMDDhhmm are required to be present.
+  if (!consume_two_digits(&Y, &v, &len) ||
+      !consume_two_digits(&M, &v, &len) ||
+      !consume_two_digits(&D, &v, &len) ||
+      !consume_two_digits(&h, &v, &len) ||
+      !consume_two_digits(&m, &v, &len)) {
+    goto err;
+  }
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires seconds
+  // to be present, but historically this code has forgiven its absence.
+  consume_two_digits(&s, &v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, specifies this
+  // interpretation of the year.
+  if (Y < 50) {
+    Y += 2000;
+  } else {
+    Y += 1900;
+  }
+  if (M > 12 || M == 0) {
+    goto err;
+  }
+  if (D > 31 || D == 0) {
+    goto err;
+  }
+  if (h > 23 || m > 59 || s > 60) {
+    goto err;
+  }
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, requires the "Z"
+  // to be present, but historically this code has forgiven its absence.
+  const int is_gmt = consume_zulu_timezone(&v, &len);
+
+  // https://tools.ietf.org/html/rfc5280, section 4.1.2.5.1, does not permit
+  // the specification of timezones using the +hhmm / -hhmm syntax, which is
+  // the only other thing that might legitimately be found at the end.
+  if (len) {
+    goto err;
+  }
+
+  return BIO_printf(bp, "%s %2d %02d:%02d:%02d %d%s", mon[M - 1], D, h, m, s, Y,
+                    is_gmt ? " GMT" : "") > 0;
+
+err:
+  BIO_write(bp, "Bad time value", 14);
+  return 0;
 }
 
 int X509_NAME_print(BIO *bp, X509_NAME *name, int obase)
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 3629c13..4b80af8 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -17,6 +17,7 @@
 #include <assert.h>
 #include <string.h>
 
+#include <openssl/bio.h>
 #include <openssl/bytestring.h>
 #include <openssl/crypto.h>
 #include <openssl/digest.h>
@@ -1011,6 +1012,73 @@
   return true;
 }
 
+static bool TestPrintUTCTIME() {
+  static const struct {
+    const char *val, *want;
+  } asn1_utctime_tests[] = {
+    {"", "Bad time value"},
+
+    // Correct RFC 5280 form. Test years < 2000 and > 2000.
+    {"090303125425Z", "Mar  3 12:54:25 2009 GMT"},
+    {"900303125425Z", "Mar  3 12:54:25 1990 GMT"},
+    {"000303125425Z", "Mar  3 12:54:25 2000 GMT"},
+
+    // Correct form, bad values.
+    {"000000000000Z", "Bad time value"},
+    {"999999999999Z", "Bad time value"},
+
+    // Missing components. Not legal RFC 5280, but permitted.
+    {"090303125425", "Mar  3 12:54:25 2009"},
+    {"9003031254", "Mar  3 12:54:00 1990"},
+    {"9003031254Z", "Mar  3 12:54:00 1990 GMT"},
+
+    // GENERALIZEDTIME confused for UTCTIME.
+    {"20090303125425Z", "Bad time value"},
+
+    // Legal ASN.1, but not legal RFC 5280.
+    {"9003031254+0800", "Bad time value"},
+    {"9003031254-0800", "Bad time value"},
+
+    // Trailing garbage.
+    {"9003031254Z ", "Bad time value"},
+  };
+
+  for (auto t : asn1_utctime_tests) {
+    bssl::UniquePtr<ASN1_UTCTIME> tm(ASN1_UTCTIME_new());
+    bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+
+    // Use this instead of ASN1_UTCTIME_set() because some callers get
+    // type-confused and pass ASN1_GENERALIZEDTIME to ASN1_UTCTIME_print().
+    // ASN1_UTCTIME_set_string() is stricter, and would reject the inputs in
+    // question.
+    if (!ASN1_STRING_set(tm.get(), t.val, strlen(t.val))) {
+      fprintf(stderr, "ASN1_STRING_set\n");
+      return false;
+    }
+    const int ok = ASN1_UTCTIME_print(bio.get(), tm.get());
+
+    const uint8_t *contents;
+    size_t len;
+    if (!BIO_mem_contents(bio.get(), &contents, &len)) {
+      fprintf(stderr, "BIO_mem_contents\n");
+      return false;
+    }
+
+    if (ok != (strcmp(t.want, "Bad time value") != 0)) {
+      fprintf(stderr, "ASN1_UTCTIME_print(%s): bad return value\n", t.val);
+      return false;
+    }
+    if (len != strlen(t.want) || memcmp(contents, t.want, len)) {
+      fprintf(stderr, "ASN1_UTCTIME_print(%s): got %.*s, want %s\n", t.val,
+              static_cast<int>(len),
+              reinterpret_cast<const char *>(contents), t.want);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 int main() {
   CRYPTO_library_init();
 
@@ -1023,7 +1091,8 @@
       !TestFromBufferTrailingData() ||
       !TestFromBufferModified() ||
       !TestFromBufferReused() ||
-      !TestFailedParseFromBuffer()) {
+      !TestFailedParseFromBuffer() ||
+      !TestPrintUTCTIME()) {
     return 1;
   }
 
diff --git a/src/include/openssl/aead.h b/src/include/openssl/aead.h
index eaa2b8f..35e0f13 100644
--- a/src/include/openssl/aead.h
+++ b/src/include/openssl/aead.h
@@ -64,7 +64,7 @@
  *
  * The "seal" and "open" operations are atomic - an entire message must be
  * encrypted or decrypted in a single call. Large messages may have to be split
- * up in order to accomodate this. When doing so, be mindful of the need not to
+ * up in order to accommodate this. When doing so, be mindful of the need not to
  * repeat nonces and the possibility that an attacker could duplicate, reorder
  * or drop message chunks. For example, using a single key for a given (large)
  * message and sealing chunks with nonces counting from zero would be secure as
diff --git a/src/include/openssl/base.h b/src/include/openssl/base.h
index bdb590a..3f47521 100644
--- a/src/include/openssl/base.h
+++ b/src/include/openssl/base.h
@@ -118,6 +118,10 @@
 #define OPENSSL_LINUX
 #endif
 
+#if defined(__Fuchsia__)
+#define OPENSSL_FUCHSIA
+#endif
+
 #if defined(TRUSTY)
 #define OPENSSL_TRUSTY
 #define OPENSSL_NO_THREADS
@@ -229,9 +233,6 @@
 typedef struct Netscape_certificate_sequence NETSCAPE_CERT_SEQUENCE;
 typedef struct Netscape_spkac_st NETSCAPE_SPKAC;
 typedef struct Netscape_spki_st NETSCAPE_SPKI;
-typedef struct PBE2PARAM_st PBE2PARAM;
-typedef struct PBEPARAM_st PBEPARAM;
-typedef struct PBKDF2PARAM_st PBKDF2PARAM;
 typedef struct RIPEMD160state_st RIPEMD160_CTX;
 typedef struct X509_POLICY_CACHE_st X509_POLICY_CACHE;
 typedef struct X509_POLICY_LEVEL_st X509_POLICY_LEVEL;
diff --git a/src/include/openssl/bio.h b/src/include/openssl/bio.h
index 847cb44..110629e 100644
--- a/src/include/openssl/bio.h
+++ b/src/include/openssl/bio.h
@@ -226,7 +226,7 @@
                             long larg, long return_value);
 
 /* BIO_callback_ctrl allows the callback function to be manipulated. The |cmd|
- * arg will generally be |BIO_CTRL_SET_CALLBACK| but arbitary command values
+ * arg will generally be |BIO_CTRL_SET_CALLBACK| but arbitrary command values
  * can be interpreted by the |BIO|. */
 OPENSSL_EXPORT long BIO_callback_ctrl(BIO *bio, int cmd, bio_info_cb fp);
 
@@ -469,7 +469,7 @@
 
 /* BIO_set_fp sets the |FILE| for |bio|. If |close_flag| is |BIO_CLOSE| then
  * |fclose| will be called on |file| when |bio| is closed. It returns one on
- * sucess and zero otherwise. */
+ * success and zero otherwise. */
 OPENSSL_EXPORT int BIO_set_fp(BIO *bio, FILE *file, int close_flag);
 
 /* BIO_read_filename opens |filename| for reading and sets the result as the
@@ -656,7 +656,8 @@
 #define BIO_CTRL_GET_CALLBACK	15  /* opt - set callback function */
 #define BIO_CTRL_SET_FILENAME	30	/* BIO_s_file special */
 
-/* BIO_CTRL_DUP is never used, but exists to allow code to compile more easily. */
+/* BIO_CTRL_DUP is never used, but exists to allow code to compile more
+ * easily. */
 #define BIO_CTRL_DUP	12
 
 
diff --git a/src/include/openssl/bn.h b/src/include/openssl/bn.h
index 18beba4..77f6196 100644
--- a/src/include/openssl/bn.h
+++ b/src/include/openssl/bn.h
@@ -134,7 +134,7 @@
 #endif
 
 
-/* BN provides support for working with arbitary sized integers. For example,
+/* BN provides support for working with arbitrary sized integers. For example,
  * although the largest integer supported by the compiler might be 64 bits, BN
  * will allow you to work with numbers until you run out of memory. */
 
@@ -194,13 +194,6 @@
 /* BN_value_one returns a static BIGNUM with value 1. */
 OPENSSL_EXPORT const BIGNUM *BN_value_one(void);
 
-/* BN_with_flags initialises a stack allocated |BIGNUM| with pointers to the
- * contents of |in| but with |flags| ORed into the flags field.
- *
- * Note: the two BIGNUMs share state and so |out| should /not/ be passed to
- * |BN_free|. */
-OPENSSL_EXPORT void BN_with_flags(BIGNUM *out, const BIGNUM *in, int flags);
-
 
 /* Basic functions. */
 
@@ -233,12 +226,6 @@
 /* BN_is_negative returns one if |bn| is negative and zero otherwise. */
 OPENSSL_EXPORT int BN_is_negative(const BIGNUM *bn);
 
-/* BN_get_flags returns |bn->flags| & |flags|. */
-OPENSSL_EXPORT int BN_get_flags(const BIGNUM *bn, int flags);
-
-/* BN_set_flags sets |flags| on |bn|. */
-OPENSSL_EXPORT void BN_set_flags(BIGNUM *bn, int flags);
-
 
 /* Conversion functions. */
 
@@ -358,7 +345,7 @@
 /* BIGNUM pools.
  *
  * Certain BIGNUM operations need to use many temporary variables and
- * allocating and freeing them can be quite slow. Thus such opertions typically
+ * allocating and freeing them can be quite slow. Thus such operations typically
  * take a |BN_CTX| parameter, which contains a pool of |BIGNUMs|. The |ctx|
  * argument to a public function may be NULL, in which case a local |BN_CTX|
  * will be created just for the lifetime of that call.
@@ -657,7 +644,7 @@
  * |BN_GENCB| structure.
  *
  * The callback receives the address of that |BN_GENCB| structure as its last
- * argument and the user is free to put an arbitary pointer in |arg|. The other
+ * argument and the user is free to put an arbitrary pointer in |arg|. The other
  * arguments are set as follows:
  *   event=BN_GENCB_GENERATED, n=i:   after generating the i'th possible prime
  *                                    number.
@@ -762,11 +749,10 @@
 /* BN_mod_inverse sets |out| equal to |a|^-1, mod |n|. If |out| is NULL, a
  * fresh BIGNUM is allocated. It returns the result or NULL on error.
  *
- * If either of |a| or |n| have |BN_FLG_CONSTTIME| set then the operation is
- * performed using an algorithm that avoids some branches but which isn't
- * constant-time. This function shouldn't be used for secret values, even
- * with |BN_FLG_CONSTTIME|; use |BN_mod_inverse_blinded| instead. Or, if
- * |n| is guaranteed to be prime, use
+ * If |n| is even then the operation is performed using an algorithm that avoids
+ * some branches but which isn't constant-time. This function shouldn't be used
+ * for secret values; use |BN_mod_inverse_blinded| instead. Or, if |n| is
+ * guaranteed to be prime, use
  * |BN_mod_exp_mont_consttime(out, a, m_minus_2, m, ctx, m_mont)|, taking
  * advantage of Fermat's Little Theorem. */
 OPENSSL_EXPORT BIGNUM *BN_mod_inverse(BIGNUM *out, const BIGNUM *a,
@@ -775,11 +761,9 @@
 /* BN_mod_inverse_blinded sets |out| equal to |a|^-1, mod |n|, where |n| is the
  * Montgomery modulus for |mont|. |a| must be non-negative and must be less
  * than |n|. |n| must be greater than 1. |a| is blinded (masked by a random
- * value) to protect it against side-channel attacks. |BN_mod_inverse_blinded|
- * may or may not ignore the |BN_FLG_CONSTTIME| flag on any/all of its inputs.
- * It returns one on success or zero on failure. On failure, if the failure was
- * caused by |a| having no inverse mod |n| then |*out_no_inverse| will be set
- * to one; otherwise it will be set to zero. */
+ * value) to protect it against side-channel attacks. On failure, if the failure
+ * was caused by |a| having no inverse mod |n| then |*out_no_inverse| will be
+ * set to one; otherwise it will be set to zero. */
 int BN_mod_inverse_blinded(BIGNUM *out, int *out_no_inverse, const BIGNUM *a,
                            const BN_MONT_CTX *mont, BN_CTX *ctx);
 
@@ -860,9 +844,9 @@
                           BN_CTX *ctx);
 
 /* BN_mod_exp sets |r| equal to |a|^{|p|} mod |m|. It does so with the best
- * algorithm for the values provided and can run in constant time if
- * |BN_FLG_CONSTTIME| is set for |p|. It returns one on success or zero
- * otherwise. */
+ * algorithm for the values provided. It returns one on success or zero
+ * otherwise. The |BN_mod_exp_mont_consttime| variant must be used if the
+ * exponent is secret. */
 OPENSSL_EXPORT int BN_mod_exp(BIGNUM *r, const BIGNUM *a, const BIGNUM *p,
                               const BIGNUM *m, BN_CTX *ctx);
 
@@ -930,10 +914,11 @@
 
 #define BN_FLG_MALLOCED 0x01
 #define BN_FLG_STATIC_DATA 0x02
-/* Avoid leaking exponent information through timing. |BN_mod_exp_mont| will
- * call |BN_mod_exp_mont_consttime| and |BN_mod_inverse| will call
- * |BN_mod_inverse_no_branch|. */
-#define BN_FLG_CONSTTIME 0x04
+/* |BN_FLG_CONSTTIME| has been removed and intentionally omitted so code relying
+ * on it will not compile. Consumers outside BoringSSL should use the
+ * higher-level cryptographic algorithms exposed by other modules. Consumers
+ * within the library should call the appropriate timing-sensitive algorithm
+ * directly. */
 
 
 #if defined(__cplusplus)
diff --git a/src/include/openssl/cipher.h b/src/include/openssl/cipher.h
index a8585d7..2ee74ef 100644
--- a/src/include/openssl/cipher.h
+++ b/src/include/openssl/cipher.h
@@ -191,7 +191,7 @@
  * |*out_len| to the number of bytes written. If padding is enabled (the
  * default) then padding is removed from the final block.
  *
- * WARNING: it is unsafe to call this function with unauthenticted
+ * WARNING: it is unsafe to call this function with unauthenticated
  * ciphertext if padding is enabled. */
 OPENSSL_EXPORT int EVP_DecryptFinal_ex(EVP_CIPHER_CTX *ctx, unsigned char *out,
                                        int *out_len);
@@ -282,12 +282,13 @@
 /* EVP_CIPHER_CTX_set_key_length sets the key length for |ctx|. This is only
  * valid for ciphers that can take a variable length key. It returns one on
  * success and zero on error. */
-OPENSSL_EXPORT int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *ctx, unsigned key_len);
+OPENSSL_EXPORT int EVP_CIPHER_CTX_set_key_length(EVP_CIPHER_CTX *ctx,
+                                                 unsigned key_len);
 
 
 /* Cipher accessors. */
 
-/* EVP_CIPHER_nid returns a NID identifing |cipher|. (For example,
+/* EVP_CIPHER_nid returns a NID identifying |cipher|. (For example,
  * |NID_aes_128_gcm|.) */
 OPENSSL_EXPORT int EVP_CIPHER_nid(const EVP_CIPHER *cipher);
 
diff --git a/src/include/openssl/conf.h b/src/include/openssl/conf.h
index 0a09e24..8b82fd4 100644
--- a/src/include/openssl/conf.h
+++ b/src/include/openssl/conf.h
@@ -77,7 +77,7 @@
  *   [section_name]
  *   key2=value2
  *
- * Config files are representated by a |CONF|. */
+ * Config files are represented by a |CONF|. */
 
 struct conf_value_st {
   char *section;
diff --git a/src/include/openssl/digest.h b/src/include/openssl/digest.h
index ec62993..caf5861 100644
--- a/src/include/openssl/digest.h
+++ b/src/include/openssl/digest.h
@@ -143,7 +143,8 @@
  * at least this much space. */
 #define EVP_MAX_MD_SIZE 64 /* SHA-512 is the longest so far. */
 
-/* EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in bytes. */
+/* EVP_MAX_MD_BLOCK_SIZE is the largest digest block size supported, in
+ * bytes. */
 #define EVP_MAX_MD_BLOCK_SIZE 128 /* SHA-512 is the longest so far. */
 
 /* EVP_DigestFinal_ex finishes the digest in |ctx| and writes the output to
@@ -175,7 +176,7 @@
  * These functions allow code to learn details about an abstract hash
  * function. */
 
-/* EVP_MD_type returns a NID identifing |md|. (For example, |NID_sha256|.) */
+/* EVP_MD_type returns a NID identifying |md|. (For example, |NID_sha256|.) */
 OPENSSL_EXPORT int EVP_MD_type(const EVP_MD *md);
 
 /* EVP_MD_flags returns the flags for |md|, which is a set of |EVP_MD_FLAG_*|
diff --git a/src/include/openssl/dsa.h b/src/include/openssl/dsa.h
index d6c3204..2988877 100644
--- a/src/include/openssl/dsa.h
+++ b/src/include/openssl/dsa.h
@@ -71,7 +71,7 @@
 #endif
 
 
-/* DSA contains functions for signing and verifing with the Digital Signature
+/* DSA contains functions for signing and verifying with the Digital Signature
  * Algorithm. */
 
 
@@ -351,10 +351,10 @@
  * Use |DSA_parse_private_key| instead. */
 OPENSSL_EXPORT DSA *d2i_DSAPrivateKey(DSA **out, const uint8_t **inp, long len);
 
-/* i2d_DSAPrivateKey marshals a private key from |in| to an ASN.1, DER structure.
- * If |outp| is not NULL then the result is written to |*outp| and |*outp| is
- * advanced just past the output. It returns the number of bytes in the result,
- * whether written or not, or a negative value on error.
+/* i2d_DSAPrivateKey marshals a private key from |in| to an ASN.1, DER
+ * structure. If |outp| is not NULL then the result is written to |*outp| and
+ * |*outp| is advanced just past the output. It returns the number of bytes in
+ * the result, whether written or not, or a negative value on error.
  *
  * Use |DSA_marshal_private_key| instead. */
 OPENSSL_EXPORT int i2d_DSAPrivateKey(const DSA *in, uint8_t **outp);
diff --git a/src/include/openssl/ec.h b/src/include/openssl/ec.h
index e780347..a1cd5c7 100644
--- a/src/include/openssl/ec.h
+++ b/src/include/openssl/ec.h
@@ -272,8 +272,8 @@
 OPENSSL_EXPORT int EC_POINT_dbl(const EC_GROUP *group, EC_POINT *r,
                                 const EC_POINT *a, BN_CTX *ctx);
 
-/* EC_POINT_invert sets |a| equal to minus |a|. It returns one on success and zero
- * otherwise. If |ctx| is not NULL, it may be used. */
+/* EC_POINT_invert sets |a| equal to minus |a|. It returns one on success and
+ * zero otherwise. If |ctx| is not NULL, it may be used. */
 OPENSSL_EXPORT int EC_POINT_invert(const EC_GROUP *group, EC_POINT *a,
                                    BN_CTX *ctx);
 
diff --git a/src/include/openssl/ecdsa.h b/src/include/openssl/ecdsa.h
index f6e9982..3890744 100644
--- a/src/include/openssl/ecdsa.h
+++ b/src/include/openssl/ecdsa.h
@@ -66,7 +66,7 @@
  * Algorithm over elliptic curves. */
 
 
-/* Signing and verifing. */
+/* Signing and verifying. */
 
 /* ECDSA_sign signs |digest_len| bytes from |digest| with |key| and writes the
  * resulting signature to |sig|, which must have |ECDSA_size(key)| bytes of
@@ -80,7 +80,7 @@
 /* ECDSA_verify verifies that |sig_len| bytes from |sig| constitute a valid
  * signature by |key| of |digest|. (The |type| argument should be zero.) It
  * returns one on success or zero if the signature is invalid or an error
- * occured. */
+ * occurred. */
 OPENSSL_EXPORT int ECDSA_verify(int type, const uint8_t *digest,
                                 size_t digest_len, const uint8_t *sig,
                                 size_t sig_len, EC_KEY *key);
diff --git a/src/include/openssl/err.h b/src/include/openssl/err.h
index f6efa12..a747b30 100644
--- a/src/include/openssl/err.h
+++ b/src/include/openssl/err.h
@@ -120,7 +120,7 @@
 
 /* Error queue handling functions.
  *
- * Errors in OpenSSL are generally signalled by the return value of a function.
+ * Errors in OpenSSL are generally signaled by the return value of a function.
  * When a function fails it may add an entry to a per-thread error queue,
  * which is managed by the functions in this header.
  *
@@ -306,7 +306,7 @@
   ERR_put_error(ERR_LIB_SYS, 0, 0, __FILE__, __LINE__);
 
 /* ERR_put_error adds an error to the error queue, dropping the least recent
- * error if neccessary for space reasons. */
+ * error if necessary for space reasons. */
 OPENSSL_EXPORT void ERR_put_error(int library, int unused, int reason,
                                   const char *file, unsigned line);
 
@@ -331,14 +331,14 @@
 OPENSSL_EXPORT int ERR_pop_to_mark(void);
 
 struct err_error_st {
-  /* file contains the filename where the error occured. */
+  /* file contains the filename where the error occurred. */
   const char *file;
   /* data contains optional data. It must be freed with |OPENSSL_free| if
    * |flags&ERR_FLAG_MALLOCED|. */
   char *data;
   /* packed contains the error library and reason, as packed by ERR_PACK. */
   uint32_t packed;
-  /* line contains the line number where the error occured. */
+  /* line contains the line number where the error occurred. */
   uint16_t line;
   /* flags contains a bitwise-OR of ERR_FLAG_* values. */
   uint8_t flags;
diff --git a/src/include/openssl/evp.h b/src/include/openssl/evp.h
index e8deb10..7debbc5 100644
--- a/src/include/openssl/evp.h
+++ b/src/include/openssl/evp.h
@@ -174,7 +174,7 @@
 OPENSSL_EXPORT int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
 
 /* EVP_PKEY_set_type sets the type of |pkey| to |type|, which should be one of
- * the |EVP_PKEY_*| values. It returns one if sucessful or zero otherwise. If
+ * the |EVP_PKEY_*| values. It returns one if successful or zero otherwise. If
  * |pkey| is NULL, it simply reports whether the type is known. */
 OPENSSL_EXPORT int EVP_PKEY_set_type(EVP_PKEY *pkey, int type);
 
@@ -378,9 +378,10 @@
 /* PKCS5_PBKDF2_HMAC_SHA1 is the same as PKCS5_PBKDF2_HMAC, but with |digest|
  * fixed to |EVP_sha1|. */
 OPENSSL_EXPORT int PKCS5_PBKDF2_HMAC_SHA1(const char *password,
-                                          size_t password_len, const uint8_t *salt,
-                                          size_t salt_len, unsigned iterations,
-                                          size_t key_len, uint8_t *out_key);
+                                          size_t password_len,
+                                          const uint8_t *salt, size_t salt_len,
+                                          unsigned iterations, size_t key_len,
+                                          uint8_t *out_key);
 
 
 /* Public key contexts.
@@ -435,8 +436,8 @@
  * It returns one on success or zero on error. */
 OPENSSL_EXPORT int EVP_PKEY_verify_init(EVP_PKEY_CTX *ctx);
 
-/* EVP_PKEY_verify verifies that |sig_len| bytes from |sig| are a valid signature
- * for |data|.
+/* EVP_PKEY_verify verifies that |sig_len| bytes from |sig| are a valid
+ * signature for |data|.
  *
  * It returns one on success or zero on error. */
 OPENSSL_EXPORT int EVP_PKEY_verify(EVP_PKEY_CTX *ctx, const uint8_t *sig,
diff --git a/src/include/openssl/obj.h b/src/include/openssl/obj.h
index 8819593..63cf866 100644
--- a/src/include/openssl/obj.h
+++ b/src/include/openssl/obj.h
@@ -135,7 +135,7 @@
 
 /* Dealing with textual representations of object identifiers. */
 
-/* OBJ_txt2obj returns an ASN1_OBJECT for the textual respresentation in |s|.
+/* OBJ_txt2obj returns an ASN1_OBJECT for the textual representation in |s|.
  * If |dont_search_names| is zero, then |s| will be matched against the long
  * and short names of a known objects to find a match. Otherwise |s| must
  * contain an ASCII string with a dotted sequence of numbers. The resulting
diff --git a/src/include/openssl/pkcs8.h b/src/include/openssl/pkcs8.h
index e04a4f3..141ed8d 100644
--- a/src/include/openssl/pkcs8.h
+++ b/src/include/openssl/pkcs8.h
@@ -88,7 +88,7 @@
                                            const EVP_CIPHER *cipher,
                                            const uint8_t *pass_raw,
                                            size_t pass_raw_len,
-                                           uint8_t *salt, size_t salt_len,
+                                           const uint8_t *salt, size_t salt_len,
                                            int iterations,
                                            PKCS8_PRIV_KEY_INFO *p8inf);
 
@@ -123,7 +123,7 @@
  * unchanged.  */
 OPENSSL_EXPORT X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher,
                                        const char *pass, int pass_len,
-                                       uint8_t *salt, size_t salt_len,
+                                       const uint8_t *salt, size_t salt_len,
                                        int iterations,
                                        PKCS8_PRIV_KEY_INFO *p8inf);
 
diff --git a/src/include/openssl/pool.h b/src/include/openssl/pool.h
index a6dee9f..dc5c938 100644
--- a/src/include/openssl/pool.h
+++ b/src/include/openssl/pool.h
@@ -24,9 +24,9 @@
 
 /* Buffers and buffer pools.
  *
- * |CRYPTO_BUFFER|s are simply reference-counted blobs. A |CRYPTO_BUFFER_POOL| is
- * an intern table for |CRYPTO_BUFFER|s. This allows for a single copy of a given
- * blob to be kept in memory and referenced from multiple places. */
+ * |CRYPTO_BUFFER|s are simply reference-counted blobs. A |CRYPTO_BUFFER_POOL|
+ * is an intern table for |CRYPTO_BUFFER|s. This allows for a single copy of a
+ * given blob to be kept in memory and referenced from multiple places. */
 
 
 /* CRYPTO_BUFFER_POOL_new returns a freshly allocated |CRYPTO_BUFFER_POOL| or
diff --git a/src/include/openssl/rsa.h b/src/include/openssl/rsa.h
index 19be3ba..bad3fad 100644
--- a/src/include/openssl/rsa.h
+++ b/src/include/openssl/rsa.h
@@ -517,7 +517,7 @@
                                         int sLen);
 
 /* RSA_padding_add_PKCS1_OAEP acts like |RSA_padding_add_PKCS1_OAEP_mgf1| but
- * the |md| and |mgf1md| paramaters of the latter are implicitly set to NULL,
+ * the |md| and |mgf1md| parameters of the latter are implicitly set to NULL,
  * which means SHA-1. */
 OPENSSL_EXPORT int RSA_padding_add_PKCS1_OAEP(uint8_t *to, unsigned to_len,
                                               const uint8_t *from,
diff --git a/src/include/openssl/ssl.h b/src/include/openssl/ssl.h
index c230f8c..70646b0 100644
--- a/src/include/openssl/ssl.h
+++ b/src/include/openssl/ssl.h
@@ -904,14 +904,14 @@
                                                           const uint8_t *list,
                                                           size_t list_len);
 
-/* SSL_CTX_set_ocsp_response sets the OCSP reponse that is sent to clients
+/* SSL_CTX_set_ocsp_response sets the OCSP response that is sent to clients
  * which request it. It returns one on success and zero on error. The caller
  * retains ownership of |response|. */
 OPENSSL_EXPORT int SSL_CTX_set_ocsp_response(SSL_CTX *ctx,
                                              const uint8_t *response,
                                              size_t response_len);
 
-/* SSL_set_ocsp_response sets the OCSP reponse that is sent to clients which
+/* SSL_set_ocsp_response sets the OCSP response that is sent to clients which
  * request it. It returns one on success and zero on error. The caller retains
  * ownership of |response|. */
 OPENSSL_EXPORT int SSL_set_ocsp_response(SSL *ssl,
@@ -1152,7 +1152,7 @@
  * mode). */
 OPENSSL_EXPORT int SSL_CIPHER_is_AES(const SSL_CIPHER *cipher);
 
-/* SSL_CIPHER_has_MD5_HMAC returns one if |cipher| uses HMAC-MD5. */
+/* SSL_CIPHER_has_MD5_HMAC returns zero. */
 OPENSSL_EXPORT int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *cipher);
 
 /* SSL_CIPHER_has_SHA1_HMAC returns one if |cipher| uses HMAC-SHA1. */
@@ -1161,6 +1161,9 @@
 /* SSL_CIPHER_has_SHA256_HMAC returns one if |cipher| uses HMAC-SHA256. */
 OPENSSL_EXPORT int SSL_CIPHER_has_SHA256_HMAC(const SSL_CIPHER *cipher);
 
+/* SSL_CIPHER_is_AEAD returns one if |cipher| uses an AEAD cipher. */
+OPENSSL_EXPORT int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher);
+
 /* SSL_CIPHER_is_AESGCM returns one if |cipher| uses AES-GCM. */
 OPENSSL_EXPORT int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher);
 
@@ -1195,6 +1198,10 @@
 /* SSL_CIPHER_is_ECDHE returns one if |cipher| uses ECDHE. */
 OPENSSL_EXPORT int SSL_CIPHER_is_ECDHE(const SSL_CIPHER *cipher);
 
+/* SSL_CIPHER_is_static_RSA returns one if |cipher| uses the static RSA key
+ * exchange. */
+OPENSSL_EXPORT int SSL_CIPHER_is_static_RSA(const SSL_CIPHER *cipher);
+
 /* SSL_CIPHER_get_min_version returns the minimum protocol version required
  * for |cipher|. */
 OPENSSL_EXPORT uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher);
@@ -1276,7 +1283,7 @@
  *   whose bulk cipher use the corresponding encryption scheme. Note that
  *   |AES|, |AES128|, and |AES256| match both CBC and GCM ciphers.
  *
- *   |MD5|, |SHA1|, |SHA256|, and |SHA384| match legacy cipher suites using the
+ *   |SHA1|, |SHA256|, and |SHA384| match legacy cipher suites using the
  *   corresponding hash function in their MAC. AEADs are matched by none of
  *   these.
  *
@@ -1428,8 +1435,8 @@
 OPENSSL_EXPORT int SSL_get_tls_unique(const SSL *ssl, uint8_t *out,
                                       size_t *out_len, size_t max_out);
 
-/* SSL_get_extms_support returns one if the Extended Master Secret
- * extension was negotiated. Otherwise, it returns zero. */
+/* SSL_get_extms_support returns one if the Extended Master Secret extension or
+ * TLS 1.3 was negotiated. Otherwise, it returns zero. */
 OPENSSL_EXPORT int SSL_get_extms_support(const SSL *ssl);
 
 /* SSL_get_current_cipher returns the cipher used in the current outgoing
@@ -1444,7 +1451,7 @@
 OPENSSL_EXPORT int SSL_session_reused(const SSL *ssl);
 
 /* SSL_get_secure_renegotiation_support returns one if the peer supports secure
- * renegotiation (RFC 5746) and zero otherwise. */
+ * renegotiation (RFC 5746) or TLS 1.3. Otherwise, it returns zero. */
 OPENSSL_EXPORT int SSL_get_secure_renegotiation_support(const SSL *ssl);
 
 /* SSL_export_keying_material exports a value derived from the master secret, as
@@ -1589,8 +1596,8 @@
  * was established at. For example, "TLSv1.2" or "SSLv3". */
 OPENSSL_EXPORT const char *SSL_SESSION_get_version(const SSL_SESSION *session);
 
-/* SSL_SESSION_get_id returns a pointer to a buffer containg |session|'s session
- * ID and sets |*out_len| to its length. */
+/* SSL_SESSION_get_id returns a pointer to a buffer containing |session|'s
+ * session ID and sets |*out_len| to its length. */
 OPENSSL_EXPORT const uint8_t *SSL_SESSION_get_id(const SSL_SESSION *session,
                                                  unsigned *out_len);
 
@@ -1601,7 +1608,7 @@
 /* SSL_SESSION_get_timeout returns the lifetime of |session| in seconds. */
 OPENSSL_EXPORT long SSL_SESSION_get_timeout(const SSL_SESSION *session);
 
-/* SSL_SESSION_get0_peer return's the peer leaf certificate stored in
+/* SSL_SESSION_get0_peer returns the peer leaf certificate stored in
  * |session|.
  *
  * TODO(davidben): This should return a const X509 *. */
@@ -1651,7 +1658,7 @@
  *
  * Note that offering or accepting a session short-circuits most parameter
  * negotiation. Resuming sessions across different configurations may result in
- * surprising behavor. So, for instance, a client implementing a version
+ * surprising behavior. So, for instance, a client implementing a version
  * fallback should shard its session cache by maximum protocol version. */
 
 /* SSL_SESS_CACHE_OFF disables all session caching. */
@@ -2368,7 +2375,7 @@
 #define SSL_TLSEXT_ERR_NOACK 3
 
 
-/* Application-layer protocol negotation.
+/* Application-layer protocol negotiation.
  *
  * The ALPN extension (RFC 7301) allows negotiating different application-layer
  * protocols over a single port. This is used, for example, to negotiate
@@ -2483,7 +2490,7 @@
  * or have a default application level protocol.
  *
  * 2) If the server supports NPN, but advertises an empty list then the
- * client selects the first protcol in its list, but indicates via the
+ * client selects the first protocol in its list, but indicates via the
  * API that this fallback case was enacted.
  *
  * 3) Otherwise, the client finds the first protocol in the server's list
@@ -2910,12 +2917,12 @@
 OPENSSL_EXPORT int SSL_renegotiate_pending(SSL *ssl);
 
 /* SSL_total_renegotiations returns the total number of renegotiation handshakes
- * peformed by |ssl|. This includes the pending renegotiation, if any. */
+ * performed by |ssl|. This includes the pending renegotiation, if any. */
 OPENSSL_EXPORT int SSL_total_renegotiations(const SSL *ssl);
 
 /* SSL_CTX_set_early_data_enabled sets whether early data is allowed to be used
  * with resumptions using |ctx|. WARNING: This is experimental and may cause
- * interop failures until fully implemented. */
+ * interoperability failures until fully implemented. */
 OPENSSL_EXPORT void SSL_CTX_set_early_data_enabled(SSL_CTX *ctx, int enabled);
 
 /* SSL_MAX_CERT_LIST_DEFAULT is the default maximum length, in bytes, of a peer
@@ -3034,14 +3041,14 @@
 #define SSL_CB_HANDSHAKE_DONE 0x20
 
 /* SSL_CTX_set_info_callback configures a callback to be run when various
- * events occur during a connection's lifetime. The |type| argumentj determines
+ * events occur during a connection's lifetime. The |type| argument determines
  * the type of event and the meaning of the |value| argument. Callbacks must
  * ignore unexpected |type| values.
  *
  * |SSL_CB_READ_ALERT| is signaled for each alert received, warning or fatal.
  * The |value| argument is a 16-bit value where the alert level (either
- * |SSL3_AL_WARNING| or |SSL3_AL_FATAL|) is in the most-significant eight bits and
- * the alert type (one of |SSL_AD_*|) is in the least-significant eight.
+ * |SSL3_AL_WARNING| or |SSL3_AL_FATAL|) is in the most-significant eight bits
+ * and the alert type (one of |SSL_AD_*|) is in the least-significant eight.
  *
  * |SSL_CB_WRITE_ALERT| is signaled for each alert sent. The |value| argument
  * is constructed as with |SSL_CB_READ_ALERT|.
@@ -3456,7 +3463,7 @@
 OPENSSL_EXPORT const char *SSL_get_version(const SSL *ssl);
 
 /* SSL_get_cipher_list returns the name of the |n|th cipher in the output of
- * |SSL_get_ciphers| or NULL if out of range. Use |SSL_get_ciphers| insteads. */
+ * |SSL_get_ciphers| or NULL if out of range. Use |SSL_get_ciphers| instead. */
 OPENSSL_EXPORT const char *SSL_get_cipher_list(const SSL *ssl, int n);
 
 /* SSL_CTX_set_client_cert_cb sets a callback which is called on the client if
@@ -4066,9 +4073,7 @@
    * be negotiated and zero otherwise. */
   unsigned short_header_enabled:1;
 
-  /* extra_certs is a dummy value included for compatibility.
-   * TODO(agl): remove once node.js no longer references this. */
-  STACK_OF(X509)* extra_certs;
+  /* TODO(agl): remove once node.js no longer references this. */
   int freelist_max_len;
 };
 
@@ -4240,9 +4245,6 @@
    * session space. Only effective on the server side. */
   unsigned retain_only_sha256_of_client_certs:1;
 
-  /* TODO(agl): remove once node.js not longer references this. */
-  int tlsext_status_type;
-
   /* session_timeout is the default lifetime in seconds of the session
    * created in this connection. */
   long session_timeout;
diff --git a/src/include/openssl/tls1.h b/src/include/openssl/tls1.h
index 7d9f2ac..4b3e1aa 100644
--- a/src/include/openssl/tls1.h
+++ b/src/include/openssl/tls1.h
@@ -604,8 +604,8 @@
 #define TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 \
   "ECDHE-PSK-CHACHA20-POLY1305"
 
-/* TODO(davidben): Remove this. Historically, the TXT names for CHACHA20_POLY1305
- * were missing 'SHA256'. */
+/* TODO(davidben): Remove this. Historically, the TXT names for
+ * CHACHA20_POLY1305 were missing 'SHA256'. */
 #define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 \
   TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 
diff --git a/src/include/openssl/x509.h b/src/include/openssl/x509.h
index ffd0fbc..88455dd 100644
--- a/src/include/openssl/x509.h
+++ b/src/include/openssl/x509.h
@@ -509,28 +509,6 @@
 	} CBC_PARAM;
 */
 
-/* Password based encryption structure */
-
-struct PBEPARAM_st {
-ASN1_OCTET_STRING *salt;
-ASN1_INTEGER *iter;
-} /* PBEPARAM */;
-
-/* Password based encryption V2 structures */
-
-struct PBE2PARAM_st {
-X509_ALGOR *keyfunc;
-X509_ALGOR *encryption;
-} /* PBE2PARAM */;
-
-struct PBKDF2PARAM_st {
-ASN1_TYPE *salt;	/* Usually OCTET STRING but could be anything */
-ASN1_INTEGER *iter;
-ASN1_INTEGER *keylength;
-X509_ALGOR *prf;
-} /* PBKDF2PARAM */;
-
-
 /* PKCS#8 private key info structure */
 
 struct pkcs8_priv_key_info_st
@@ -1090,24 +1068,6 @@
 				     ASN1_INTEGER *serial);
 OPENSSL_EXPORT X509 *X509_find_by_subject(STACK_OF(X509) *sk,X509_NAME *name);
 
-DECLARE_ASN1_FUNCTIONS(PBEPARAM)
-DECLARE_ASN1_FUNCTIONS(PBE2PARAM)
-DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM)
-
-OPENSSL_EXPORT int PKCS5_pbe_set0_algor(X509_ALGOR *algor, int alg, int iter,
-				const unsigned char *salt, int saltlen);
-
-OPENSSL_EXPORT X509_ALGOR *PKCS5_pbe_set(int alg, int iter,
-				const unsigned char *salt, int saltlen);
-OPENSSL_EXPORT X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter,
-					 unsigned char *salt, int saltlen);
-OPENSSL_EXPORT X509_ALGOR *PKCS5_pbe2_set_iv(const EVP_CIPHER *cipher, int iter,
-				 unsigned char *salt, int saltlen,
-				 unsigned char *aiv, int prf_nid);
-
-OPENSSL_EXPORT X509_ALGOR *PKCS5_pbkdf2_set(int iter, unsigned char *salt, int saltlen,
-				int prf_nid, int keylen);
-
 /* PKCS#8 utilities */
 
 DECLARE_ASN1_FUNCTIONS(PKCS8_PRIV_KEY_INFO)
diff --git a/src/ssl/internal.h b/src/ssl/internal.h
index 919f5aa..eb47785 100644
--- a/src/ssl/internal.h
+++ b/src/ssl/internal.h
@@ -195,12 +195,11 @@
 #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
 
 /* Bits for |algorithm_mac| (symmetric authentication). */
-#define SSL_MD5 0x00000001L
-#define SSL_SHA1 0x00000002L
-#define SSL_SHA256 0x00000004L
-#define SSL_SHA384 0x00000008L
+#define SSL_SHA1 0x00000001L
+#define SSL_SHA256 0x00000002L
+#define SSL_SHA384 0x00000004L
 /* SSL_AEAD is set for all AEADs. */
-#define SSL_AEAD 0x00000010L
+#define SSL_AEAD 0x00000008L
 
 /* Bits for |algorithm_prf| (handshake digest). */
 #define SSL_HANDSHAKE_MAC_DEFAULT 0x1
diff --git a/src/ssl/ssl_cipher.c b/src/ssl/ssl_cipher.c
index 20b075e..7ca79ab 100644
--- a/src/ssl/ssl_cipher.c
+++ b/src/ssl/ssl_cipher.c
@@ -678,7 +678,6 @@
      0},
 
     /* MAC aliases */
-    {"MD5", ~0u, ~0u, ~0u, SSL_MD5, 0},
     {"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
     {"SHA", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
     {"SHA256", ~0u, ~0u, ~0u, SSL_SHA256, 0},
@@ -1473,7 +1472,7 @@
 }
 
 int SSL_CIPHER_has_MD5_HMAC(const SSL_CIPHER *cipher) {
-  return (cipher->algorithm_mac & SSL_MD5) != 0;
+  return 0;
 }
 
 int SSL_CIPHER_has_SHA1_HMAC(const SSL_CIPHER *cipher) {
@@ -1484,6 +1483,10 @@
   return (cipher->algorithm_mac & SSL_SHA256) != 0;
 }
 
+int SSL_CIPHER_is_AEAD(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mac & SSL_AEAD) != 0;
+}
+
 int SSL_CIPHER_is_AESGCM(const SSL_CIPHER *cipher) {
   return (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) != 0;
 }
@@ -1526,6 +1529,10 @@
   return (cipher->algorithm_mkey & SSL_kECDHE) != 0;
 }
 
+int SSL_CIPHER_is_static_RSA(const SSL_CIPHER *cipher) {
+  return (cipher->algorithm_mkey & SSL_kRSA) != 0;
+}
+
 uint16_t SSL_CIPHER_get_min_version(const SSL_CIPHER *cipher) {
   if (cipher->algorithm_mkey == SSL_kGENERIC ||
       cipher->algorithm_auth == SSL_aGENERIC) {
@@ -1627,15 +1634,10 @@
 static const char *ssl_cipher_get_prf_name(const SSL_CIPHER *cipher) {
   switch (cipher->algorithm_prf) {
     case SSL_HANDSHAKE_MAC_DEFAULT:
-      /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which is
-       * only ever MD5 or SHA-1. */
-      switch (cipher->algorithm_mac) {
-        case SSL_MD5:
-          return "MD5";
-        case SSL_SHA1:
-          return "SHA";
-      }
-      break;
+      /* Before TLS 1.2, the PRF component is the hash used in the HMAC, which
+       * is SHA-1 for all supported ciphers. */
+      assert(cipher->algorithm_mac == SSL_SHA1);
+      return "SHA";
     case SSL_HANDSHAKE_MAC_SHA256:
       return "SHA256";
     case SSL_HANDSHAKE_MAC_SHA384:
@@ -1824,10 +1826,6 @@
   }
 
   switch (alg_mac) {
-    case SSL_MD5:
-      mac = "MD5";
-      break;
-
     case SSL_SHA1:
       mac = "SHA1";
       break;
@@ -1917,19 +1915,9 @@
       return 0;
   }
 
-  size_t mac_len;
-  switch (cipher->algorithm_mac) {
-    case SSL_MD5:
-      mac_len = MD5_DIGEST_LENGTH;
-      break;
-    case SSL_SHA1:
-      mac_len = SHA_DIGEST_LENGTH;
-      break;
-    default:
-      return 0;
-  }
-
-  size_t ret = 1 + mac_len;
+  /* All supported TLS 1.0 ciphers use SHA-1. */
+  assert(cipher->algorithm_mac == SSL_SHA1);
+  size_t ret = 1 + SHA_DIGEST_LENGTH;
   ret += block_size - (ret % block_size);
   return ret;
 }
diff --git a/src/ssl/ssl_lib.c b/src/ssl/ssl_lib.c
index a60bf81..e0ab803 100644
--- a/src/ssl/ssl_lib.c
+++ b/src/ssl/ssl_lib.c
@@ -1506,7 +1506,11 @@
 }
 
 int SSL_get_secure_renegotiation_support(const SSL *ssl) {
-  return ssl->s3->send_connection_binding;
+  if (!ssl->s3->have_version) {
+    return 0;
+  }
+  return ssl3_protocol_version(ssl) >= TLS1_3_VERSION ||
+         ssl->s3->send_connection_binding;
 }
 
 LHASH_OF(SSL_SESSION) *SSL_CTX_sessions(SSL_CTX *ctx) { return ctx->sessions; }
diff --git a/src/ssl/t1_lib.c b/src/ssl/t1_lib.c
index 0681919..dc377c4 100644
--- a/src/ssl/t1_lib.c
+++ b/src/ssl/t1_lib.c
@@ -1136,10 +1136,6 @@
  *
  * https://tools.ietf.org/html/rfc6066#section-8 */
 
-static void ext_ocsp_init(SSL_HANDSHAKE *hs) {
-  hs->ssl->tlsext_status_type = -1;
-}
-
 static int ext_ocsp_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
   SSL *const ssl = hs->ssl;
   if (!ssl->ocsp_stapling_enabled) {
@@ -1156,7 +1152,6 @@
     return 0;
   }
 
-  ssl->tlsext_status_type = TLSEXT_STATUSTYPE_ocsp;
   return 1;
 }
 
@@ -2633,7 +2628,7 @@
   },
   {
     TLSEXT_TYPE_status_request,
-    ext_ocsp_init,
+    NULL,
     ext_ocsp_add_clienthello,
     ext_ocsp_parse_serverhello,
     ext_ocsp_parse_clienthello,
diff --git a/src/ssl/test/bssl_shim.cc b/src/ssl/test/bssl_shim.cc
index a98ff43..bbbebe7 100644
--- a/src/ssl/test/bssl_shim.cc
+++ b/src/ssl/test/bssl_shim.cc
@@ -1305,11 +1305,22 @@
     }
   }
 
-  if (config->expect_extended_master_secret) {
-    if (!SSL_get_extms_support(ssl)) {
-      fprintf(stderr, "No EMS for connection when expected");
-      return false;
-    }
+  if (config->expect_extended_master_secret && !SSL_get_extms_support(ssl)) {
+    fprintf(stderr, "No EMS for connection when expected\n");
+    return false;
+  }
+
+  if (config->expect_secure_renegotiation &&
+      !SSL_get_secure_renegotiation_support(ssl)) {
+    fprintf(stderr, "No secure renegotiation for connection when expected\n");
+    return false;
+  }
+
+  if (config->expect_no_secure_renegotiation &&
+      SSL_get_secure_renegotiation_support(ssl)) {
+    fprintf(stderr,
+            "Secure renegotiation unexpectedly negotiated for connection\n");
+    return false;
   }
 
   if (!config->expected_ocsp_response.empty()) {
@@ -1622,6 +1633,9 @@
   if (is_resume && config->retain_only_sha256_client_cert_resume) {
     SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
   }
+  if (config->max_send_fragment > 0) {
+    SSL_set_max_send_fragment(ssl.get(), config->max_send_fragment);
+  }
 
   int sock = Connect(config->port);
   if (sock == -1) {
@@ -1785,12 +1799,15 @@
     }
     if (!config->shim_shuts_down) {
       for (;;) {
-        static const size_t kBufLen = 16384;
-        std::unique_ptr<uint8_t[]> buf(new uint8_t[kBufLen]);
-
         // Read only 512 bytes at a time in TLS to ensure records may be
         // returned in multiple reads.
-        int n = DoRead(ssl.get(), buf.get(), config->is_dtls ? kBufLen : 512);
+        size_t read_size = config->is_dtls ? 16384 : 512;
+        if (config->read_size > 0) {
+          read_size = config->read_size;
+        }
+        std::unique_ptr<uint8_t[]> buf(new uint8_t[read_size]);
+
+        int n = DoRead(ssl.get(), buf.get(), read_size);
         int err = SSL_get_error(ssl.get(), n);
         if (err == SSL_ERROR_ZERO_RETURN ||
             (n == 0 && err == SSL_ERROR_SYSCALL)) {
diff --git a/src/ssl/test/runner/common.go b/src/ssl/test/runner/common.go
index 96bc39a..3afd73f 100644
--- a/src/ssl/test/runner/common.go
+++ b/src/ssl/test/runner/common.go
@@ -1253,6 +1253,10 @@
 	// formats to send in ClientHello or ServerHello. If set to a non-nil
 	// empty slice, no extension will be sent.
 	SendSupportedPointFormats []byte
+
+	// MaxReceivePlaintext, if non-zero, is the maximum plaintext record
+	// length accepted from the peer.
+	MaxReceivePlaintext int
 }
 
 func (c *Config) serverInit() {
diff --git a/src/ssl/test/runner/conn.go b/src/ssl/test/runner/conn.go
index ee342fa..3cbd496 100644
--- a/src/ssl/test/runner/conn.go
+++ b/src/ssl/test/runner/conn.go
@@ -876,7 +876,11 @@
 		return err
 	}
 	data := b.data[b.off:]
-	if len(data) > maxPlaintext {
+	max := maxPlaintext
+	if c.config.Bugs.MaxReceivePlaintext != 0 {
+		max = c.config.Bugs.MaxReceivePlaintext
+	}
+	if len(data) > max {
 		err := c.sendAlert(alertRecordOverflow)
 		c.in.freeBlock(b)
 		return c.in.setErrorLocked(err)
diff --git a/src/ssl/test/runner/runner.go b/src/ssl/test/runner/runner.go
index ba78fce..3a884bc 100644
--- a/src/ssl/test/runner/runner.go
+++ b/src/ssl/test/runner/runner.go
@@ -2401,6 +2401,42 @@
 				},
 			},
 		},
+		{
+			// Test the server so there is a large certificate as
+			// well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment",
+			config: Config{
+				Bugs: ProtocolBugs{
+					MaxReceivePlaintext: 512,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+		},
+		{
+			// Test the server so there is a large certificate as
+			// well as application data.
+			testType: serverTest,
+			name:     "MaxSendFragment-TooLarge",
+			config: Config{
+				Bugs: ProtocolBugs{
+					// Ensure that some of the records are
+					// 512.
+					MaxReceivePlaintext: 511,
+				},
+			},
+			messageLen: 1024,
+			flags: []string{
+				"-max-send-fragment", "512",
+				"-read-size", "1024",
+			},
+			shouldFail:         true,
+			expectedLocalError: "local error: record overflow",
+		},
 	}
 	testCases = append(testCases, basicTests...)
 
@@ -5246,6 +5282,7 @@
 				RequireRenegotiationInfo: true,
 			},
 		},
+		flags: []string{"-expect-secure-renegotiation"},
 	})
 	testCases = append(testCases, testCase{
 		testType: serverTest,
@@ -5258,6 +5295,7 @@
 				RequireRenegotiationInfo: true,
 			},
 		},
+		flags: []string{"-expect-secure-renegotiation"},
 	})
 
 	// Test that illegal extensions in TLS 1.3 are rejected by the client if
@@ -6015,6 +6053,7 @@
 		flags: []string{
 			"-renegotiate-freely",
 			"-expect-total-renegotiations", "1",
+			"-expect-secure-renegotiation",
 		},
 	})
 	testCases = append(testCases, testCase{
@@ -6081,6 +6120,7 @@
 		flags: []string{
 			"-renegotiate-freely",
 			"-expect-total-renegotiations", "1",
+			"-expect-no-secure-renegotiation",
 		},
 	})
 
@@ -6347,6 +6387,22 @@
 		shouldFail:    true,
 		expectedError: ":UNEXPECTED_MESSAGE:",
 	})
+
+	// The renegotiation_info extension is not sent in TLS 1.3, but TLS 1.3
+	// always reads as supporting it, regardless of whether it was
+	// negotiated.
+	testCases = append(testCases, testCase{
+		name: "AlwaysReportRenegotiationInfo-TLS13",
+		config: Config{
+			MaxVersion: VersionTLS13,
+			Bugs: ProtocolBugs{
+				NoRenegotiationInfo: true,
+			},
+		},
+		flags: []string{
+			"-expect-secure-renegotiation",
+		},
+	})
 }
 
 func addDTLSReplayTests() {
diff --git a/src/ssl/test/test_config.cc b/src/ssl/test/test_config.cc
index a06b5e5..5bc9544 100644
--- a/src/ssl/test/test_config.cc
+++ b/src/ssl/test/test_config.cc
@@ -119,6 +119,10 @@
     &TestConfig::expect_sha256_client_cert_resume },
   { "-enable-short-header", &TestConfig::enable_short_header },
   { "-read-with-unfinished-write", &TestConfig::read_with_unfinished_write },
+  { "-expect-secure-renegotiation",
+    &TestConfig::expect_secure_renegotiation },
+  { "-expect-no-secure-renegotiation",
+    &TestConfig::expect_no_secure_renegotiation },
 };
 
 const Flag<std::string> kStringFlags[] = {
@@ -172,6 +176,8 @@
   { "-expect-cipher-aes", &TestConfig::expect_cipher_aes },
   { "-expect-cipher-no-aes", &TestConfig::expect_cipher_no_aes },
   { "-resumption-delay", &TestConfig::resumption_delay },
+  { "-max-send-fragment", &TestConfig::max_send_fragment },
+  { "-read-size", &TestConfig::read_size },
 };
 
 const Flag<std::vector<int>> kIntVectorFlags[] = {
diff --git a/src/ssl/test/test_config.h b/src/ssl/test/test_config.h
index 1307d56..7cf0a6f 100644
--- a/src/ssl/test/test_config.h
+++ b/src/ssl/test/test_config.h
@@ -127,6 +127,10 @@
   bool expect_sha256_client_cert_resume = false;
   bool enable_short_header = false;
   bool read_with_unfinished_write = false;
+  bool expect_secure_renegotiation = false;
+  bool expect_no_secure_renegotiation = false;
+  int max_send_fragment = 0;
+  int read_size = 0;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);
diff --git a/src/tool/client.cc b/src/tool/client.cc
index dd3f846..c832ab5 100644
--- a/src/tool/client.cc
+++ b/src/tool/client.cc
@@ -19,7 +19,9 @@
 #if !defined(OPENSSL_WINDOWS)
 #include <sys/select.h>
 #else
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
 #include <winsock2.h>
+OPENSSL_MSVC_PRAGMA(warning(pop))
 #endif
 
 #include <openssl/err.h>
diff --git a/src/util/doc.go b/src/util/doc.go
index 681b834..987794c 100644
--- a/src/util/doc.go
+++ b/src/util/doc.go
@@ -139,7 +139,7 @@
 }
 
 func extractDecl(lines []string, lineNo int) (decl string, rest []string, restLineNo int, err error) {
-	if len(lines) == 0 {
+	if len(lines) == 0 || len(lines[0]) == 0 {
 		return "", lines, lineNo, nil
 	}