external/boringssl: Sync to 2d98d49cf712ca7dc6f4b23b9c5f5542385d8dbe.

This includes the following changes:

https://boringssl.googlesource.com/boringssl/+log/5ede28c8a422801ace3ba5e466ba963005351145..2d98d49cf712ca7dc6f4b23b9c5f5542385d8dbe

Test: BoringSSL CTS Presubmits
Change-Id: I21750d81b070d7e4741e205f5c2ccd8d541b36d1
diff --git a/src/crypto/CMakeLists.txt b/src/crypto/CMakeLists.txt
index 78b835c..ee9626a 100644
--- a/src/crypto/CMakeLists.txt
+++ b/src/crypto/CMakeLists.txt
@@ -467,6 +467,7 @@
   refcount_test.cc
   rsa_extra/rsa_test.cc
   self_test.cc
+  stack/stack_test.cc
   test/file_test_gtest.cc
   thread_test.cc
   x509/x509_test.cc
diff --git a/src/crypto/asn1/a_d2i_fp.c b/src/crypto/asn1/a_d2i_fp.c
index 3da6df9..fd423e2 100644
--- a/src/crypto/asn1/a_d2i_fp.c
+++ b/src/crypto/asn1/a_d2i_fp.c
@@ -58,240 +58,36 @@
 
 #include <limits.h>
 
-#include <openssl/buf.h>
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb);
-
-#ifndef NO_OLD_ASN1
-# ifndef OPENSSL_NO_FP_API
-
-void *ASN1_d2i_fp(void *(*xnew) (void), d2i_of_void *d2i, FILE *in, void **x)
-{
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
-    }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_d2i_bio(xnew, d2i, b, x);
-    BIO_free(b);
-    return (ret);
-}
-# endif
-
-void *ASN1_d2i_bio(void *(*xnew) (void), d2i_of_void *d2i, BIO *in, void **x)
-{
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (unsigned char *)b->data;
-    ret = d2i(x, &p, len);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
-}
-
-#endif
 
 void *ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in, void *x)
 {
-    BUF_MEM *b = NULL;
-    const unsigned char *p;
-    void *ret = NULL;
-    int len;
-
-    len = asn1_d2i_read_bio(in, &b);
-    if (len < 0)
-        goto err;
-
-    p = (const unsigned char *)b->data;
-    ret = ASN1_item_d2i(x, &p, len, it);
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return (ret);
+    uint8_t *data;
+    size_t len;
+    // Historically, this function did not impose a limit in OpenSSL and is used
+    // to read CRLs, so we leave this without an external bound.
+    if (!BIO_read_asn1(in, &data, &len, INT_MAX)) {
+        return NULL;
+    }
+    const uint8_t *ptr = data;
+    void *ret = ASN1_item_d2i(x, &ptr, len, it);
+    OPENSSL_free(data);
+    return ret;
 }
 
 #ifndef OPENSSL_NO_FP_API
 void *ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in, void *x)
 {
-    BIO *b;
-    char *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(in, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (NULL);
+        return NULL;
     }
-    BIO_set_fp(b, in, BIO_NOCLOSE);
-    ret = ASN1_item_d2i_bio(it, b, x);
+    void *ret = ASN1_item_d2i_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
-
-typedef struct asn1_const_ctx_st
-    {
-    const unsigned char *p;/* work char pointer */
-    int eos;    /* end of sequence read for indefinite encoding */
-    int error;  /* error code to use when returning an error */
-    int inf;    /* constructed if 0x20, indefinite is 0x21 */
-    int tag;    /* tag from last 'get object' */
-    int xclass; /* class from last 'get object' */
-    long slen;  /* length of last 'get object' */
-    const unsigned char *max; /* largest value of p allowed */
-    const unsigned char *q;/* temporary variable */
-    const unsigned char **pp;/* variable */
-    int line;   /* used in error processing */
-    } ASN1_const_CTX;
-
-#define HEADER_SIZE   8
-#define ASN1_CHUNK_INITIAL_SIZE (16 * 1024)
-static int asn1_d2i_read_bio(BIO *in, BUF_MEM **pb)
-{
-    BUF_MEM *b;
-    unsigned char *p;
-    int i;
-    ASN1_const_CTX c;
-    size_t want = HEADER_SIZE;
-    int eos = 0;
-    size_t off = 0;
-    size_t len = 0;
-
-    b = BUF_MEM_new();
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return -1;
-    }
-
-    ERR_clear_error();
-    for (;;) {
-        if (want >= (len - off)) {
-            want -= (len - off);
-
-            if (len + want < len || !BUF_MEM_grow_clean(b, len + want)) {
-                OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                goto err;
-            }
-            i = BIO_read(in, &(b->data[len]), want);
-            if ((i < 0) && ((len - off) == 0)) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                goto err;
-            }
-            if (i > 0) {
-                if (len + i < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                len += i;
-            }
-        }
-        /* else data already loaded */
-
-        p = (unsigned char *)&(b->data[off]);
-        c.p = p;
-        c.inf = ASN1_get_object(&(c.p), &(c.slen), &(c.tag), &(c.xclass),
-                                len - off);
-        if (c.inf & 0x80) {
-            uint32_t e;
-
-            e = ERR_GET_REASON(ERR_peek_error());
-            if (e != ASN1_R_TOO_LONG)
-                goto err;
-            else
-                ERR_clear_error(); /* clear error */
-        }
-        i = c.p - p;            /* header length */
-        off += i;               /* end of data */
-
-        if (c.inf & 1) {
-            /* no data body so go round again */
-            eos++;
-            if (eos < 0) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_HEADER_TOO_LONG);
-                goto err;
-            }
-            want = HEADER_SIZE;
-        } else if (eos && (c.slen == 0) && (c.tag == V_ASN1_EOC)) {
-            /* eos value, so go back and read another header */
-            eos--;
-            if (eos <= 0)
-                break;
-            else
-                want = HEADER_SIZE;
-        } else {
-            /* suck in c.slen bytes of data */
-            want = c.slen;
-            if (want > (len - off)) {
-                size_t chunk_max = ASN1_CHUNK_INITIAL_SIZE;
-                want -= (len - off);
-                if (want > INT_MAX /* BIO_read takes an int length */  ||
-                    len + want < len) {
-                    OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                    goto err;
-                }
-                while (want > 0) {
-                    /*
-                     * Read content in chunks of increasing size
-                     * so we can return an error for EOF without
-                     * having to allocate the entire content length
-                     * in one go.
-                     */
-                    size_t chunk = want > chunk_max ? chunk_max : want;
-
-                    if (!BUF_MEM_grow_clean(b, len + chunk)) {
-                        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-                        goto err;
-                    }
-                    want -= chunk;
-                    while (chunk > 0) {
-                        i = BIO_read(in, &(b->data[len]), chunk);
-                        if (i <= 0) {
-                            OPENSSL_PUT_ERROR(ASN1, ASN1_R_NOT_ENOUGH_DATA);
-                            goto err;
-                        }
-                        /*
-                         * This can't overflow because |len+want| didn't
-                         * overflow.
-                         */
-                        len += i;
-                        chunk -= i;
-                    }
-                    if (chunk_max < INT_MAX/2)
-                        chunk_max *= 2;
-                }
-            }
-            if (off + c.slen < off) {
-                OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-                goto err;
-            }
-            off += c.slen;
-            if (eos <= 0) {
-                break;
-            } else
-                want = HEADER_SIZE;
-        }
-    }
-
-    if (off > INT_MAX) {
-        OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
-        goto err;
-    }
-
-    *pb = b;
-    return off;
- err:
-    if (b != NULL)
-        BUF_MEM_free(b);
-    return -1;
-}
diff --git a/src/crypto/asn1/a_dup.c b/src/crypto/asn1/a_dup.c
index 57394f5..9ede851 100644
--- a/src/crypto/asn1/a_dup.c
+++ b/src/crypto/asn1/a_dup.c
@@ -59,30 +59,6 @@
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x)
-{
-    unsigned char *b, *p;
-    const unsigned char *p2;
-    int i;
-    char *ret;
-
-    if (x == NULL)
-        return (NULL);
-
-    i = i2d(x, NULL);
-    b = OPENSSL_malloc(i + 10);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (NULL);
-    }
-    p = b;
-    i = i2d(x, &p);
-    p2 = b;
-    ret = d2i(NULL, &p2, i);
-    OPENSSL_free(b);
-    return (ret);
-}
-
 /*
  * ASN1_ITEM version of dup: this follows the model above except we don't
  * need to allocate the buffer. At some point this could be rewritten to
diff --git a/src/crypto/asn1/a_i2d_fp.c b/src/crypto/asn1/a_i2d_fp.c
index 7b76d0c..db0d812 100644
--- a/src/crypto/asn1/a_i2d_fp.c
+++ b/src/crypto/asn1/a_i2d_fp.c
@@ -56,95 +56,33 @@
 
 #include <openssl/asn1.h>
 
+#include <openssl/bio.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
 
-int ASN1_i2d_fp(i2d_of_void *i2d, FILE *out, void *x)
-{
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
-    }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_i2d_bio(i2d, b, x);
-    BIO_free(b);
-    return (ret);
-}
-
-int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, void *x)
-{
-    char *b;
-    unsigned char *p;
-    int i, j = 0, n, ret = 1;
-
-    n = i2d(x, NULL);
-    if (n <= 0)
-        return 0;
-
-    b = (char *)OPENSSL_malloc(n);
-    if (b == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-
-    p = (unsigned char *)b;
-    i2d(x, &p);
-
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
-    OPENSSL_free(b);
-    return (ret);
-}
 
 int ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out, void *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(out, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, out, BIO_NOCLOSE);
-    ret = ASN1_item_i2d_bio(it, b, x);
+    int ret = ASN1_item_i2d_bio(it, b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out, void *x)
 {
     unsigned char *b = NULL;
-    int i, j = 0, n, ret = 1;
-
-    n = ASN1_item_i2d(x, &b, it);
+    int n = ASN1_item_i2d(x, &b, it);
     if (b == NULL) {
         OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
+        return 0;
     }
 
-    for (;;) {
-        i = BIO_write(out, &(b[j]), n);
-        if (i == n)
-            break;
-        if (i <= 0) {
-            ret = 0;
-            break;
-        }
-        j += i;
-        n -= i;
-    }
+    int ret = BIO_write_all(out, b, n);
     OPENSSL_free(b);
-    return (ret);
+    return ret;
 }
diff --git a/src/crypto/bio/bio.c b/src/crypto/bio/bio.c
index 3e788b8..881c14e 100644
--- a/src/crypto/bio/bio.c
+++ b/src/crypto/bio/bio.c
@@ -177,6 +177,19 @@
   return ret;
 }
 
+int BIO_write_all(BIO *bio, const void *data, size_t len) {
+  const uint8_t *data_u8 = data;
+  while (len > 0) {
+    int ret = BIO_write(bio, data_u8, len > INT_MAX ? INT_MAX : (int)len);
+    if (ret <= 0) {
+      return 0;
+    }
+    data_u8 += ret;
+    len -= ret;
+  }
+  return 1;
+}
+
 int BIO_puts(BIO *bio, const char *in) {
   return BIO_write(bio, in, strlen(in));
 }
diff --git a/src/crypto/bio/file.c b/src/crypto/bio/file.c
index 6a0b9a9..a177763 100644
--- a/src/crypto/bio/file.c
+++ b/src/crypto/bio/file.c
@@ -107,13 +107,12 @@
     return NULL;
   }
 
-  ret = BIO_new(BIO_s_file());
+  ret = BIO_new_fp(file, BIO_CLOSE);
   if (ret == NULL) {
     fclose(file);
     return NULL;
   }
 
-  BIO_set_fp(ret, file, BIO_CLOSE);
   return ret;
 }
 
diff --git a/src/crypto/bn_extra/convert.c b/src/crypto/bn_extra/convert.c
index c70ff8b..9a1a69e 100644
--- a/src/crypto/bn_extra/convert.c
+++ b/src/crypto/bn_extra/convert.c
@@ -367,17 +367,13 @@
 }
 
 int BN_print_fp(FILE *fp, const BIGNUM *a) {
-  BIO *b;
-  int ret;
-
-  b = BIO_new(BIO_s_file());
+  BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
   if (b == NULL) {
     return 0;
   }
-  BIO_set_fp(b, fp, BIO_NOCLOSE);
-  ret = BN_print(b, a);
-  BIO_free(b);
 
+  int ret = BN_print(b, a);
+  BIO_free(b);
   return ret;
 }
 
diff --git a/src/crypto/conf/conf.c b/src/crypto/conf/conf.c
index b1982f8..4c27ddf 100644
--- a/src/crypto/conf/conf.c
+++ b/src/crypto/conf/conf.c
@@ -62,6 +62,7 @@
 #include <openssl/bio.h>
 #include <openssl/buf.h>
 #include <openssl/err.h>
+#include <openssl/lhash.h>
 #include <openssl/mem.h>
 
 #include "conf_def.h"
@@ -69,6 +70,10 @@
 #include "../internal.h"
 
 
+struct conf_st {
+  LHASH_OF(CONF_VALUE) *data;
+};
+
 // The maximum length we can grow a value to after variable expansion. 64k
 // should be more than enough for all reasonable uses.
 #define MAX_CONF_VALUE_LENGTH 65536
diff --git a/src/crypto/fipsmodule/CMakeLists.txt b/src/crypto/fipsmodule/CMakeLists.txt
index 9868dd8..e6c8cc6 100644
--- a/src/crypto/fipsmodule/CMakeLists.txt
+++ b/src/crypto/fipsmodule/CMakeLists.txt
@@ -162,12 +162,12 @@
   set_target_properties(bcm_hashunset PROPERTIES POSITION_INDEPENDENT_CODE ON)
   set_target_properties(bcm_hashunset PROPERTIES LINKER_LANGUAGE C)
 
-  go_executable(inject-hash
-	        boringssl.googlesource.com/boringssl/util/fipstools/inject-hash)
+  go_executable(inject_hash
+	        boringssl.googlesource.com/boringssl/util/fipstools/inject_hash)
   add_custom_command(
     OUTPUT bcm.o
-    COMMAND ./inject-hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
-    DEPENDS bcm_hashunset inject-hash
+    COMMAND ./inject_hash -o bcm.o -in-archive $<TARGET_FILE:bcm_hashunset>
+    DEPENDS bcm_hashunset inject_hash
     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   )
 
diff --git a/src/crypto/fipsmodule/FIPS.md b/src/crypto/fipsmodule/FIPS.md
index a691d18..426b38e 100644
--- a/src/crypto/fipsmodule/FIPS.md
+++ b/src/crypto/fipsmodule/FIPS.md
@@ -103,7 +103,7 @@
 
 In order to actually implement the integrity test, a constructor function within the module calculates an HMAC from `module_start` to `module_end` using a fixed, all-zero key. It compares the result with the known-good value added (by the script) to the unhashed portion of the text segment. If they don't match, it calls `exit` in an infinite loop.
 
-Initially the known-good value will be incorrect. Another script (`inject-hash.go`) calculates the correct value from the assembled object and injects it back into the object.
+Initially the known-good value will be incorrect. Another script (`inject_hash.go`) calculates the correct value from the assembled object and injects it back into the object.
 
 ![build process](/crypto/fipsmodule/intcheck2.png)
 
diff --git a/src/crypto/fipsmodule/aes/mode_wrappers.c b/src/crypto/fipsmodule/aes/mode_wrappers.c
index 5c50c85..0140c70 100644
--- a/src/crypto/fipsmodule/aes/mode_wrappers.c
+++ b/src/crypto/fipsmodule/aes/mode_wrappers.c
@@ -57,8 +57,7 @@
 void AES_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
                         const AES_KEY *key, uint8_t ivec[AES_BLOCK_SIZE],
                         uint8_t ecount_buf[AES_BLOCK_SIZE], unsigned int *num) {
-  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ctr128_encrypt(in, out, len, key, ivec, ecount_buf, num, AES_encrypt);
 }
 
 void AES_ecb_encrypt(const uint8_t *in, uint8_t *out, const AES_KEY *key,
@@ -90,9 +89,9 @@
   aes_nohw_cbc_encrypt(in, out, len, key, ivec, enc);
 #else
   if (enc) {
-    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, (block128_f)AES_encrypt);
+    CRYPTO_cbc128_encrypt(in, out, len, key, ivec, AES_encrypt);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, (block128_f)AES_decrypt);
+    CRYPTO_cbc128_decrypt(in, out, len, key, ivec, AES_decrypt);
   }
 #endif
 }
@@ -100,8 +99,7 @@
 void AES_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t length,
                         const AES_KEY *key, uint8_t *ivec, int *num) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u,
-                        (block128_f)AES_encrypt);
+  CRYPTO_ofb128_encrypt(in, out, length, key, ivec, &num_u, AES_encrypt);
   *num = (int)num_u;
 }
 
@@ -109,7 +107,6 @@
                         const AES_KEY *key, uint8_t *ivec, int *num,
                         int enc) {
   unsigned num_u = (unsigned)(*num);
-  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc,
-                        (block128_f)AES_encrypt);
+  CRYPTO_cfb128_encrypt(in, out, length, key, ivec, &num_u, enc, AES_encrypt);
   *num = (int)num_u;
 }
diff --git a/src/crypto/fipsmodule/bcm.c b/src/crypto/fipsmodule/bcm.c
index 6b21f06..e15ecb8 100644
--- a/src/crypto/fipsmodule/bcm.c
+++ b/src/crypto/fipsmodule/bcm.c
@@ -124,6 +124,7 @@
   if (!HMAC(EVP_sha512(), kHMACKey, sizeof(kHMACKey), start, end - start,
             result, &result_len) ||
       result_len != sizeof(result)) {
+    fprintf(stderr, "HMAC failed.\n");
     goto err;
   }
 
diff --git a/src/crypto/fipsmodule/bn/div.c b/src/crypto/fipsmodule/bn/div.c
index 57485bd..27b591c 100644
--- a/src/crypto/fipsmodule/bn/div.c
+++ b/src/crypto/fipsmodule/bn/div.c
@@ -64,7 +64,7 @@
 #include "internal.h"
 
 
-#if !defined(BN_ULLONG)
+#if !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 // bn_div_words divides a double-width |h|,|l| by |d| and returns the result,
 // which must fit in a |BN_ULONG|.
 static BN_ULONG bn_div_words(BN_ULONG h, BN_ULONG l, BN_ULONG d) {
@@ -135,7 +135,7 @@
   ret |= q;
   return ret;
 }
-#endif  // !defined(BN_ULLONG)
+#endif  // !defined(BN_CAN_DIVIDE_ULLONG) && !defined(BN_CAN_USE_INLINE_ASM)
 
 static inline void bn_div_rem_words(BN_ULONG *quotient_out, BN_ULONG *rem_out,
                                     BN_ULONG n0, BN_ULONG n1, BN_ULONG d0) {
@@ -155,20 +155,18 @@
   //
   // These issues aren't specific to x86 and x86_64, so it might be worthwhile
   // to add more assembly language implementations.
-#if !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86) && \
-    (defined(__GNUC__) || defined(__clang__))
+#if defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86)
   __asm__ volatile("divl %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
-#elif !defined(OPENSSL_NO_ASM) && defined(OPENSSL_X86_64) && \
-    (defined(__GNUC__) || defined(__clang__))
+#elif defined(BN_CAN_USE_INLINE_ASM) && defined(OPENSSL_X86_64)
   __asm__ volatile("divq %4"
                    : "=a"(*quotient_out), "=d"(*rem_out)
                    : "a"(n1), "d"(n0), "rm"(d0)
                    : "cc");
 #else
-#if defined(BN_ULLONG)
+#if defined(BN_CAN_DIVIDE_ULLONG)
   BN_ULLONG n = (((BN_ULLONG)n0) << BN_BITS2) | n1;
   *quotient_out = (BN_ULONG)(n / d0);
 #else
diff --git a/src/crypto/fipsmodule/bn/internal.h b/src/crypto/fipsmodule/bn/internal.h
index 9796831..fb8d11f 100644
--- a/src/crypto/fipsmodule/bn/internal.h
+++ b/src/crypto/fipsmodule/bn/internal.h
@@ -185,6 +185,10 @@
 #error "Must define either OPENSSL_32_BIT or OPENSSL_64_BIT"
 #endif
 
+#if !defined(OPENSSL_NO_ASM) && (defined(__GNUC__) || defined(__clang__))
+#define BN_CAN_USE_INLINE_ASM
+#endif
+
 // |BN_mod_exp_mont_consttime| is based on the assumption that the L1 data
 // cache line width of the target processor is at least the following value.
 #define MOD_EXP_CTIME_MIN_CACHE_LINE_WIDTH 64
diff --git a/src/crypto/fipsmodule/cipher/e_aes.c b/src/crypto/fipsmodule/cipher/e_aes.c
index 734a517..0ced193 100644
--- a/src/crypto/fipsmodule/cipher/e_aes.c
+++ b/src/crypto/fipsmodule/cipher/e_aes.c
@@ -198,49 +198,45 @@
   if ((mode == EVP_CIPH_ECB_MODE || mode == EVP_CIPH_CBC_MODE) && !enc) {
     if (hwaes_capable()) {
       ret = aes_hw_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)aes_hw_decrypt;
+      dat->block = aes_hw_decrypt;
       dat->stream.cbc = NULL;
       if (mode == EVP_CIPH_CBC_MODE) {
-        dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+        dat->stream.cbc = aes_hw_cbc_encrypt;
       }
     } else if (bsaes_capable() && mode == EVP_CIPH_CBC_MODE) {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc = (cbc128_f)bsaes_cbc_encrypt;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = bsaes_cbc_encrypt;
     } else if (vpaes_capable()) {
       ret = vpaes_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)vpaes_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+      dat->block = vpaes_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
     } else {
       ret = AES_set_decrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-      dat->block = (block128_f)AES_decrypt;
-      dat->stream.cbc =
-          mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+      dat->block = AES_decrypt;
+      dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
     }
   } else if (hwaes_capable()) {
     ret = aes_hw_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)aes_hw_encrypt;
+    dat->block = aes_hw_encrypt;
     dat->stream.cbc = NULL;
     if (mode == EVP_CIPH_CBC_MODE) {
-      dat->stream.cbc = (cbc128_f)aes_hw_cbc_encrypt;
+      dat->stream.cbc = aes_hw_cbc_encrypt;
     } else if (mode == EVP_CIPH_CTR_MODE) {
-      dat->stream.ctr = (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+      dat->stream.ctr = aes_hw_ctr32_encrypt_blocks;
     }
   } else if (bsaes_capable() && mode == EVP_CIPH_CTR_MODE) {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.ctr = (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    dat->block = AES_encrypt;
+    dat->stream.ctr = bsaes_ctr32_encrypt_blocks;
   } else if (vpaes_capable()) {
     ret = vpaes_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)vpaes_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)vpaes_cbc_encrypt : NULL;
+    dat->block = vpaes_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? vpaes_cbc_encrypt : NULL;
   } else {
     ret = AES_set_encrypt_key(key, ctx->key_len * 8, &dat->ks.ks);
-    dat->block = (block128_f)AES_encrypt;
-    dat->stream.cbc =
-        mode == EVP_CIPH_CBC_MODE ? (cbc128_f)AES_cbc_encrypt : NULL;
+    dat->block = AES_encrypt;
+    dat->stream.cbc = mode == EVP_CIPH_CBC_MODE ? AES_cbc_encrypt : NULL;
   }
 
   if (ret < 0) {
@@ -256,11 +252,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.cbc) {
-    (*dat->stream.cbc)(in, out, len, &dat->ks, ctx->iv, ctx->encrypt);
+    (*dat->stream.cbc)(in, out, len, &dat->ks.ks, ctx->iv, ctx->encrypt);
   } else if (ctx->encrypt) {
-    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   } else {
-    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks, ctx->iv, dat->block);
+    CRYPTO_cbc128_decrypt(in, out, len, &dat->ks.ks, ctx->iv, dat->block);
   }
 
   return 1;
@@ -277,7 +273,7 @@
 
   len -= bl;
   for (size_t i = 0; i <= len; i += bl) {
-    (*dat->block)(in + i, out + i, &dat->ks);
+    (*dat->block)(in + i, out + i, &dat->ks.ks);
   }
 
   return 1;
@@ -288,11 +284,11 @@
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
   if (dat->stream.ctr) {
-    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks, ctx->iv, ctx->buf,
+    CRYPTO_ctr128_encrypt_ctr32(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
                                 &ctx->num, dat->stream.ctr);
   } else {
-    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks, ctx->iv, ctx->buf, &ctx->num,
-                          dat->block);
+    CRYPTO_ctr128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, ctx->buf,
+                          &ctx->num, dat->block);
   }
   return 1;
 }
@@ -301,7 +297,8 @@
                           size_t len) {
   EVP_AES_KEY *dat = (EVP_AES_KEY *)ctx->cipher_data;
 
-  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks, ctx->iv, &ctx->num, dat->block);
+  CRYPTO_ofb128_encrypt(in, out, len, &dat->ks.ks, ctx->iv, &ctx->num,
+                        dat->block);
   return 1;
 }
 
@@ -311,42 +308,42 @@
   if (hwaes_capable()) {
     aes_hw_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)aes_hw_encrypt, 1);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, aes_hw_encrypt, 1);
     }
     if (out_block) {
-      *out_block = (block128_f) aes_hw_encrypt;
+      *out_block = aes_hw_encrypt;
     }
-    return (ctr128_f)aes_hw_ctr32_encrypt_blocks;
+    return aes_hw_ctr32_encrypt_blocks;
   }
 
   if (bsaes_capable()) {
     AES_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)AES_encrypt, 0);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
     }
     if (out_block) {
-      *out_block = (block128_f) AES_encrypt;
+      *out_block = AES_encrypt;
     }
-    return (ctr128_f)bsaes_ctr32_encrypt_blocks;
+    return bsaes_ctr32_encrypt_blocks;
   }
 
   if (vpaes_capable()) {
     vpaes_set_encrypt_key(key, key_bytes * 8, aes_key);
     if (out_block) {
-      *out_block = (block128_f) vpaes_encrypt;
+      *out_block = vpaes_encrypt;
     }
     if (gcm_key != NULL) {
-      CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)vpaes_encrypt, 0);
+      CRYPTO_gcm128_init_key(gcm_key, aes_key, vpaes_encrypt, 0);
     }
     return NULL;
   }
 
   AES_set_encrypt_key(key, key_bytes * 8, aes_key);
   if (gcm_key != NULL) {
-    CRYPTO_gcm128_init_key(gcm_key, aes_key, (block128_f)AES_encrypt, 0);
+    CRYPTO_gcm128_init_key(gcm_key, aes_key, AES_encrypt, 0);
   }
   if (out_block) {
-    *out_block = (block128_f) AES_encrypt;
+    *out_block = AES_encrypt;
   }
   return NULL;
 }
diff --git a/src/crypto/fipsmodule/modes/cbc.c b/src/crypto/fipsmodule/modes/cbc.c
index db9f024..64ea505 100644
--- a/src/crypto/fipsmodule/modes/cbc.c
+++ b/src/crypto/fipsmodule/modes/cbc.c
@@ -53,7 +53,7 @@
 
 
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   const uint8_t *iv = ivec;
@@ -108,7 +108,7 @@
 }
 
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            block128_f block) {
   size_t n;
   union {
diff --git a/src/crypto/fipsmodule/modes/ccm.c b/src/crypto/fipsmodule/modes/ccm.c
index 784e4fa..5a153f4 100644
--- a/src/crypto/fipsmodule/modes/ccm.c
+++ b/src/crypto/fipsmodule/modes/ccm.c
@@ -64,8 +64,8 @@
   } nonce, cmac;
 };
 
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L) {
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L) {
   if (M < 4 || M > 16 || (M & 1) != 0 || L < 2 || L > 8) {
     return 0;
   }
@@ -82,7 +82,7 @@
 }
 
 static int ccm128_init_state(const CCM128_CONTEXT *ctx,
-                             struct ccm128_state *state, const void *key,
+                             struct ccm128_state *state, const AES_KEY *key,
                              const uint8_t *nonce, size_t nonce_len,
                              const uint8_t *aad, size_t aad_len,
                              size_t plaintext_len) {
@@ -170,7 +170,7 @@
 }
 
 static int ccm128_encrypt(const CCM128_CONTEXT *ctx, struct ccm128_state *state,
-                          const void *key, uint8_t *out, const uint8_t *in,
+                          const AES_KEY *key, uint8_t *out, const uint8_t *in,
                           size_t len) {
   // The counter for encryption begins at one.
   for (unsigned i = 0; i < ctx->L; i++) {
@@ -191,7 +191,7 @@
 }
 
 static int ccm128_compute_mac(const CCM128_CONTEXT *ctx,
-                              struct ccm128_state *state, const void *key,
+                              struct ccm128_state *state, const AES_KEY *key,
                               uint8_t *out_tag, size_t tag_len,
                               const uint8_t *in, size_t len) {
   block128_f block = ctx->block;
@@ -231,7 +231,7 @@
   return 1;
 }
 
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -243,7 +243,7 @@
          ccm128_encrypt(ctx, &state, key, out, in, len);
 }
 
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
diff --git a/src/crypto/fipsmodule/modes/cfb.c b/src/crypto/fipsmodule/modes/cfb.c
index e1b0a80..d3a38d6 100644
--- a/src/crypto/fipsmodule/modes/cfb.c
+++ b/src/crypto/fipsmodule/modes/cfb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_cfb);
 
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block) {
   size_t l = 0;
 
@@ -161,7 +161,7 @@
 /* This expects a single block of size nbits for both in and out. Note that
    it corrupts any extra bits in the last byte of out */
 static void cfbr_encrypt_block(const uint8_t *in, uint8_t *out, unsigned nbits,
-                               const void *key, uint8_t ivec[16], int enc,
+                               const AES_KEY *key, uint8_t ivec[16], int enc,
                                block128_f block) {
   int n, rem, num;
   uint8_t ovec[16 * 2 + 1]; /* +1 because we dererefence (but don't use) one
@@ -203,8 +203,8 @@
 
 // N.B. This expects the input to be packed, MS bit first
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block) {
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block) {
   size_t n;
   uint8_t c[1], d[1];
 
@@ -220,7 +220,7 @@
 }
 
 void CRYPTO_cfb128_8_encrypt(const unsigned char *in, unsigned char *out,
-                             size_t length, const void *key,
+                             size_t length, const AES_KEY *key,
                              unsigned char ivec[16], unsigned *num, int enc,
                              block128_f block) {
   size_t n;
diff --git a/src/crypto/fipsmodule/modes/ctr.c b/src/crypto/fipsmodule/modes/ctr.c
index 63907b4..5093408 100644
--- a/src/crypto/fipsmodule/modes/ctr.c
+++ b/src/crypto/fipsmodule/modes/ctr.c
@@ -82,7 +82,7 @@
 // of the IV.  This implementation takes NO responsibility for checking that
 // the counter doesn't overflow into the rest of the IV when incremented.
 void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            uint8_t ecount_buf[16], unsigned int *num,
                            block128_f block) {
   unsigned int n;
@@ -153,11 +153,10 @@
   } while (n);
 }
 
-void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out,
-                                 size_t len, const void *key,
-                                 uint8_t ivec[16],
-                                 uint8_t ecount_buf[16],
-                                 unsigned int *num, ctr128_f func) {
+void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
+                                 const AES_KEY *key, uint8_t ivec[16],
+                                 uint8_t ecount_buf[16], unsigned int *num,
+                                 ctr128_f func) {
   unsigned int n, ctr32;
 
   assert(key && ecount_buf && num);
diff --git a/src/crypto/fipsmodule/modes/gcm.c b/src/crypto/fipsmodule/modes/gcm.c
index 6eff479..5e556df 100644
--- a/src/crypto/fipsmodule/modes/gcm.c
+++ b/src/crypto/fipsmodule/modes/gcm.c
@@ -271,9 +271,9 @@
                    size_t len);
 #define AESNI_GCM
 size_t aesni_gcm_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 size_t aesni_gcm_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], uint64_t *Xi);
+                         const AES_KEY *key, uint8_t ivec[16], uint64_t *Xi);
 #endif
 
 #if defined(OPENSSL_X86)
@@ -419,7 +419,7 @@
 #endif
 }
 
-void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const void *aes_key,
+void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const AES_KEY *aes_key,
                             block128_f block, int block_is_hwaes) {
   OPENSSL_memset(gcm_key, 0, sizeof(*gcm_key));
   gcm_key->block = block;
@@ -435,7 +435,7 @@
   gcm_key->use_aesni_gcm_crypt = (is_avx && block_is_hwaes) ? 1 : 0;
 }
 
-void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                          const uint8_t *iv, size_t len) {
   unsigned int ctr;
 #ifdef GCM_FUNCREF_4BIT
@@ -553,7 +553,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const uint8_t *in, uint8_t *out, size_t len) {
   unsigned int n, ctr;
   uint64_t mlen = ctx->len.u[1];
@@ -679,7 +679,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const AES_KEY *key,
                           const unsigned char *in, unsigned char *out,
                           size_t len) {
   unsigned int n, ctr;
@@ -813,7 +813,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
@@ -915,7 +915,7 @@
   return 1;
 }
 
-int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                 const uint8_t *in, uint8_t *out, size_t len,
                                 ctr128_f stream) {
   unsigned int n, ctr;
diff --git a/src/crypto/fipsmodule/modes/gcm_test.cc b/src/crypto/fipsmodule/modes/gcm_test.cc
index ab8cc34..30308f8 100644
--- a/src/crypto/fipsmodule/modes/gcm_test.cc
+++ b/src/crypto/fipsmodule/modes/gcm_test.cc
@@ -88,7 +88,7 @@
 
     GCM128_CONTEXT ctx;
     OPENSSL_memset(&ctx, 0, sizeof(ctx));
-    CRYPTO_gcm128_init_key(&ctx.gcm_key, &aes_key, (block128_f)AES_encrypt, 0);
+    CRYPTO_gcm128_init_key(&ctx.gcm_key, &aes_key, AES_encrypt, 0);
     CRYPTO_gcm128_setiv(&ctx, &aes_key, nonce.data(), nonce.size());
     if (!additional_data.empty()) {
       CRYPTO_gcm128_aad(&ctx, additional_data.data(), additional_data.size());
diff --git a/src/crypto/fipsmodule/modes/internal.h b/src/crypto/fipsmodule/modes/internal.h
index 788960b..23aaca2 100644
--- a/src/crypto/fipsmodule/modes/internal.h
+++ b/src/crypto/fipsmodule/modes/internal.h
@@ -50,6 +50,7 @@
 #define OPENSSL_HEADER_MODES_INTERNAL_H
 
 #include <openssl/base.h>
+#include <openssl/aes.h>
 
 #include <string.h>
 
@@ -87,16 +88,24 @@
   OPENSSL_memcpy(out, &v, sizeof(v));
 }
 
-// block128_f is the type of a 128-bit, block cipher.
+// block128_f is the type of an AES block cipher implementation.
+//
+// Unlike upstream OpenSSL, it and the other functions in this file hard-code
+// |AES_KEY|. It is undefined in C to call a function pointer with anything
+// other than the original type. Thus we either must match |block128_f| to the
+// type signature of |AES_encrypt| and friends or pass in |void*| wrapper
+// functions.
+//
+// These functions are called exclusively with AES, so we use the former.
 typedef void (*block128_f)(const uint8_t in[16], uint8_t out[16],
-                           const void *key);
+                           const AES_KEY *key);
 
 
 // CTR.
 
 // ctr128_f is the type of a function that performs CTR-mode encryption.
 typedef void (*ctr128_f)(const uint8_t *in, uint8_t *out, size_t blocks,
-                         const void *key, const uint8_t ivec[16]);
+                         const AES_KEY *key, const uint8_t ivec[16]);
 
 // CRYPTO_ctr128_encrypt encrypts (or decrypts, it's the same in CTR mode)
 // |len| bytes from |in| to |out| using |block| in counter mode. There's no
@@ -105,7 +114,7 @@
 // call. The counter is a 128-bit, big-endian value in |ivec| and is
 // incremented by this function.
 void CRYPTO_ctr128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16],
+                           const AES_KEY *key, uint8_t ivec[16],
                            uint8_t ecount_buf[16], unsigned *num,
                            block128_f block);
 
@@ -114,14 +123,14 @@
 // bits of the counter. This is useful when |ctr| can be an optimised
 // function.
 void CRYPTO_ctr128_encrypt_ctr32(const uint8_t *in, uint8_t *out, size_t len,
-                                 const void *key, uint8_t ivec[16],
+                                 const AES_KEY *key, uint8_t ivec[16],
                                  uint8_t ecount_buf[16], unsigned *num,
                                  ctr128_f ctr);
 
 #if !defined(OPENSSL_NO_ASM) && \
     (defined(OPENSSL_X86) || defined(OPENSSL_X86_64))
 void aesni_ctr32_encrypt_blocks(const uint8_t *in, uint8_t *out, size_t blocks,
-                                const void *key, const uint8_t *ivec);
+                                const AES_KEY *key, const uint8_t *ivec);
 #endif
 
 
@@ -194,13 +203,13 @@
 
 // CRYPTO_gcm128_init_key initialises |gcm_key| to use |block| (typically AES)
 // with the given key. |block_is_hwaes| is one if |block| is |aes_hw_encrypt|.
-OPENSSL_EXPORT void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key, const void *key,
-                                           block128_f block,
+OPENSSL_EXPORT void CRYPTO_gcm128_init_key(GCM128_KEY *gcm_key,
+                                           const AES_KEY *key, block128_f block,
                                            int block_is_hwaes);
 
 // CRYPTO_gcm128_setiv sets the IV (nonce) for |ctx|. The |key| must be the
 // same key that was passed to |CRYPTO_gcm128_init|.
-OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const void *key,
+OPENSSL_EXPORT void CRYPTO_gcm128_setiv(GCM128_CONTEXT *ctx, const AES_KEY *key,
                                         const uint8_t *iv, size_t iv_len);
 
 // CRYPTO_gcm128_aad sets the authenticated data for an instance of GCM.
@@ -212,16 +221,16 @@
 // CRYPTO_gcm128_encrypt encrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_encrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_decrypt decrypts |len| bytes from |in| to |out|. The |key|
 // must be the same key that was passed to |CRYPTO_gcm128_init|. It returns one
 // on success and zero otherwise.
-OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx, const void *key,
-                                         const uint8_t *in, uint8_t *out,
-                                         size_t len);
+OPENSSL_EXPORT int CRYPTO_gcm128_decrypt(GCM128_CONTEXT *ctx,
+                                         const AES_KEY *key, const uint8_t *in,
+                                         uint8_t *out, size_t len);
 
 // CRYPTO_gcm128_encrypt_ctr32 encrypts |len| bytes from |in| to |out| using
 // a CTR function that only handles the bottom 32 bits of the nonce, like
@@ -229,7 +238,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_encrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -239,7 +248,7 @@
 // passed to |CRYPTO_gcm128_init|. It returns one on success and zero
 // otherwise.
 OPENSSL_EXPORT int CRYPTO_gcm128_decrypt_ctr32(GCM128_CONTEXT *ctx,
-                                               const void *key,
+                                               const AES_KEY *key,
                                                const uint8_t *in, uint8_t *out,
                                                size_t len, ctr128_f stream);
 
@@ -265,8 +274,8 @@
 // CRYPTO_ccm128_init initialises |ctx| to use |block| (typically AES) with the
 // specified |M| and |L| parameters. It returns one on success and zero if |M|
 // or |L| is invalid.
-int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const void *key, block128_f block,
-                       ctr128_f ctr, unsigned M, unsigned L);
+int CRYPTO_ccm128_init(CCM128_CONTEXT *ctx, const AES_KEY *key,
+                       block128_f block, ctr128_f ctr, unsigned M, unsigned L);
 
 // CRYPTO_ccm128_max_input returns the maximum input length accepted by |ctx|.
 size_t CRYPTO_ccm128_max_input(const CCM128_CONTEXT *ctx);
@@ -274,7 +283,7 @@
 // CRYPTO_ccm128_encrypt encrypts |len| bytes from |in| to |out| writing the tag
 // to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_encrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -283,7 +292,7 @@
 // CRYPTO_ccm128_decrypt decrypts |len| bytes from |in| to |out|, writing the
 // expected tag to |out_tag|. |key| must be the same key that was passed to
 // |CRYPTO_ccm128_init|. It returns one on success and zero otherwise.
-int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const void *key,
+int CRYPTO_ccm128_decrypt(const CCM128_CONTEXT *ctx, const AES_KEY *key,
                           uint8_t *out, uint8_t *out_tag, size_t tag_len,
                           const uint8_t *nonce, size_t nonce_len,
                           const uint8_t *in, size_t len, const uint8_t *aad,
@@ -294,21 +303,23 @@
 
 // cbc128_f is the type of a function that performs CBC-mode encryption.
 typedef void (*cbc128_f)(const uint8_t *in, uint8_t *out, size_t len,
-                         const void *key, uint8_t ivec[16], int enc);
+                         const AES_KEY *key, uint8_t ivec[16], int enc);
 
 // CRYPTO_cbc128_encrypt encrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. The input need not be a multiple of
 // 128 bits long, but the output will round up to the nearest 128 bit multiple,
 // zero padding the input if needed. The IV will be updated on return.
 void CRYPTO_cbc128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 // CRYPTO_cbc128_decrypt decrypts |len| bytes from |in| to |out| using the
 // given IV and block cipher in CBC mode. If |len| is not a multiple of 128
 // bits then only that many bytes will be written, but a multiple of 128 bits
 // is always read from |in|. The IV will be updated on return.
 void CRYPTO_cbc128_decrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], block128_f block);
+                           const AES_KEY *key, uint8_t ivec[16],
+                           block128_f block);
 
 
 // OFB.
@@ -318,9 +329,9 @@
 // requirement that |len| be a multiple of any value and any partial blocks are
 // stored in |ivec| and |*num|, the latter must be zero before the initial
 // call.
-void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out,
-                           size_t len, const void *key, uint8_t ivec[16],
-                           unsigned *num, block128_f block);
+void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
+                           block128_f block);
 
 
 // CFB.
@@ -330,25 +341,25 @@
 // |len| be a multiple of any value and any partial blocks are stored in |ivec|
 // and |*num|, the latter must be zero before the initial call.
 void CRYPTO_cfb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            int enc, block128_f block);
 
 // CRYPTO_cfb128_8_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-8 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_8_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 // CRYPTO_cfb128_1_encrypt encrypts (or decrypts, if |enc| is zero) |len| bytes
 // from |in| to |out| using |block| in CFB-1 mode. Prior to the first call
 // |num| should be set to zero.
 void CRYPTO_cfb128_1_encrypt(const uint8_t *in, uint8_t *out, size_t bits,
-                             const void *key, uint8_t ivec[16], unsigned *num,
-                             int enc, block128_f block);
+                             const AES_KEY *key, uint8_t ivec[16],
+                             unsigned *num, int enc, block128_f block);
 
 size_t CRYPTO_cts128_encrypt_block(const uint8_t *in, uint8_t *out, size_t len,
-                                   const void *key, uint8_t ivec[16],
+                                   const AES_KEY *key, uint8_t ivec[16],
                                    block128_f block);
 
 
diff --git a/src/crypto/fipsmodule/modes/ofb.c b/src/crypto/fipsmodule/modes/ofb.c
index 63bba68..b1b4d87 100644
--- a/src/crypto/fipsmodule/modes/ofb.c
+++ b/src/crypto/fipsmodule/modes/ofb.c
@@ -57,7 +57,7 @@
 OPENSSL_COMPILE_ASSERT((16 % sizeof(size_t)) == 0, bad_size_t_size_ofb);
 
 void CRYPTO_ofb128_encrypt(const uint8_t *in, uint8_t *out, size_t len,
-                           const void *key, uint8_t ivec[16], unsigned *num,
+                           const AES_KEY *key, uint8_t ivec[16], unsigned *num,
                            block128_f block) {
   assert(in && out && key && ivec && num);
 
diff --git a/src/crypto/fipsmodule/self_check/self_check.c b/src/crypto/fipsmodule/self_check/self_check.c
index 2c5b363..1bbefa9 100644
--- a/src/crypto/fipsmodule/self_check/self_check.c
+++ b/src/crypto/fipsmodule/self_check/self_check.c
@@ -394,6 +394,7 @@
   // AES-CBC Encryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_encrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_encrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kPlaintext, output, sizeof(kPlaintext), &aes_key, aes_iv,
@@ -406,6 +407,7 @@
   // AES-CBC Decryption KAT
   memcpy(aes_iv, kAESIV, sizeof(kAESIV));
   if (AES_set_decrypt_key(kAESKey, 8 * sizeof(kAESKey), &aes_key) != 0) {
+    fprintf(stderr, "AES_set_decrypt_key failed.\n");
     goto err;
   }
   AES_cbc_encrypt(kAESCBCCiphertext, output, sizeof(kAESCBCCiphertext),
@@ -420,6 +422,7 @@
   OPENSSL_memset(nonce, 0, sizeof(nonce));
   if (!EVP_AEAD_CTX_init(&aead_ctx, EVP_aead_aes_128_gcm(), kAESKey,
                          sizeof(kAESKey), 0, NULL)) {
+    fprintf(stderr, "EVP_AEAD_CTX_init for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -429,6 +432,7 @@
                          kPlaintext, sizeof(kPlaintext), NULL, 0) ||
       !check_test(kAESGCMCiphertext, output, sizeof(kAESGCMCiphertext),
                   "AES-GCM Encryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_seal for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -439,6 +443,7 @@
                          0) ||
       !check_test(kPlaintext, output, sizeof(kPlaintext),
                   "AES-GCM Decryption KAT")) {
+    fprintf(stderr, "EVP_AEAD_CTX_open for AES-128-GCM failed.\n");
     goto err;
   }
 
@@ -504,6 +509,7 @@
                 &sig_len, rsa_key) ||
       !check_test(kRSASignature, output, sizeof(kRSASignature),
                   "RSA Sign KAT")) {
+    fprintf(stderr, "RSA signing test failed.\n");
     goto err;
   }
 
@@ -558,6 +564,7 @@
                          sizeof(kDRBGAD)) ||
       !check_test(kDRBGReseedOutput, output, sizeof(kDRBGReseedOutput),
                   "DRBG Reseed KAT")) {
+    fprintf(stderr, "CTR-DRBG failed.\n");
     goto err;
   }
   CTR_DRBG_clear(&drbg);
diff --git a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
index cec3669..80567d9 100644
--- a/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha1-armv8.pl
@@ -330,6 +330,7 @@
 .asciz	"SHA1 block transform for ARMv8, CRYPTOGAMS by <appro\@openssl.org>"
 .align	2
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 ___
 }}}
 
diff --git a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
index e3244f7..22c47d7 100644
--- a/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
+++ b/src/crypto/fipsmodule/sha/asm/sha512-armv8.pl
@@ -428,6 +428,7 @@
 $code.=<<___;
 #ifndef	__KERNEL__
 .comm	OPENSSL_armcap_P,4,4
+.hidden	OPENSSL_armcap_P
 #endif
 ___
 
diff --git a/src/crypto/internal.h b/src/crypto/internal.h
index 0e8ae3a..6944d56 100644
--- a/src/crypto/internal.h
+++ b/src/crypto/internal.h
@@ -132,13 +132,13 @@
 #endif
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && \
+#if defined(OPENSSL_THREADS) && \
     (!defined(OPENSSL_WINDOWS) || defined(__MINGW32__))
 #include <pthread.h>
 #define OPENSSL_PTHREADS
 #endif
 
-#if !defined(OPENSSL_NO_THREADS) && !defined(OPENSSL_PTHREADS) && \
+#if defined(OPENSSL_THREADS) && !defined(OPENSSL_PTHREADS) && \
     defined(OPENSSL_WINDOWS)
 #define OPENSSL_WINDOWS_THREADS
 OPENSSL_MSVC_PRAGMA(warning(push, 3))
@@ -367,7 +367,7 @@
 
 // Thread-safe initialisation.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 typedef uint32_t CRYPTO_once_t;
 #define CRYPTO_ONCE_INIT 0
 #elif defined(OPENSSL_WINDOWS_THREADS)
@@ -423,7 +423,7 @@
 // thread.h as a structure large enough to fit the real type. The global lock is
 // a different type so it may be initialized with platform initializer macros.
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 struct CRYPTO_STATIC_MUTEX {
   char padding;  // Empty structs have different sizes in C and C++.
 };
diff --git a/src/crypto/pem/pem_info.c b/src/crypto/pem/pem_info.c
index d707e42..3627a45 100644
--- a/src/crypto/pem/pem_info.c
+++ b/src/crypto/pem/pem_info.c
@@ -75,221 +75,203 @@
 STACK_OF(X509_INFO) *PEM_X509_INFO_read(FILE *fp, STACK_OF(X509_INFO) *sk,
                                         pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    STACK_OF(X509_INFO) *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
+    STACK_OF(X509_INFO) *ret = PEM_X509_INFO_read_bio(b, sk, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
+enum parse_result_t {
+    parse_ok,
+    parse_error,
+    parse_new_entry,
+};
+
+static enum parse_result_t parse_x509(X509_INFO *info, const uint8_t *data,
+                                      size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_x509_aux(X509_INFO *info, const uint8_t *data,
+                                          size_t len, int key_type)
+{
+    if (info->x509 != NULL) {
+        return parse_new_entry;
+    }
+    info->x509 = d2i_X509_AUX(NULL, &data, len);
+    return info->x509 != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_crl(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->crl != NULL) {
+        return parse_new_entry;
+    }
+    info->crl = d2i_X509_CRL(NULL, &data, len);
+    return info->crl != NULL ? parse_ok : parse_error;
+}
+
+static enum parse_result_t parse_key(X509_INFO *info, const uint8_t *data,
+                                     size_t len, int key_type)
+{
+    if (info->x_pkey != NULL) {
+        return parse_new_entry;
+    }
+    info->x_pkey = X509_PKEY_new();
+    if (info->x_pkey == NULL) {
+        return parse_error;
+    }
+    info->x_pkey->dec_pkey = d2i_PrivateKey(key_type, NULL, &data, len);
+    return info->x_pkey->dec_pkey != NULL ? parse_ok : parse_error;
+}
+
 STACK_OF(X509_INFO) *PEM_X509_INFO_read_bio(BIO *bp, STACK_OF(X509_INFO) *sk,
                                             pem_password_cb *cb, void *u)
 {
-    X509_INFO *xi = NULL;
+    X509_INFO *info = NULL;
     char *name = NULL, *header = NULL;
-    void *pp;
     unsigned char *data = NULL;
-    const unsigned char *p;
-    long len, error = 0;
+    long len;
     int ok = 0;
     STACK_OF(X509_INFO) *ret = NULL;
-    unsigned int i, raw, ptype;
-    d2i_of_void *d2i = 0;
 
     if (sk == NULL) {
-        if ((ret = sk_X509_INFO_new_null()) == NULL) {
+        ret = sk_X509_INFO_new_null();
+        if (ret == NULL) {
             OPENSSL_PUT_ERROR(PEM, ERR_R_MALLOC_FAILURE);
-            goto err;
+            return NULL;
         }
-    } else
+    } else {
         ret = sk;
+    }
+    size_t orig_num = sk_X509_INFO_num(ret);
 
-    if ((xi = X509_INFO_new()) == NULL)
+    info = X509_INFO_new();
+    if (info == NULL) {
         goto err;
+    }
+
     for (;;) {
-        raw = 0;
-        ptype = 0;
-        i = PEM_read_bio(bp, &name, &header, &data, &len);
-        if (i == 0) {
-            error = ERR_GET_REASON(ERR_peek_last_error());
-            if (error == PEM_R_NO_START_LINE) {
+        if (!PEM_read_bio(bp, &name, &header, &data, &len)) {
+            uint32_t error = ERR_peek_last_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_clear_error();
                 break;
             }
             goto err;
         }
- start:
-        if ((strcmp(name, PEM_STRING_X509) == 0) ||
-            (strcmp(name, PEM_STRING_X509_OLD) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
-        } else if ((strcmp(name, PEM_STRING_X509_TRUSTED) == 0)) {
-            d2i = (D2I_OF(void)) d2i_X509_AUX;
-            if (xi->x509 != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->x509);
+
+        enum parse_result_t (*parse_function)(X509_INFO *, const uint8_t *,
+                                              size_t, int) = NULL;
+        int key_type = EVP_PKEY_NONE;
+        if (strcmp(name, PEM_STRING_X509) == 0 ||
+            strcmp(name, PEM_STRING_X509_OLD) == 0) {
+            parse_function = parse_x509;
+        } else if (strcmp(name, PEM_STRING_X509_TRUSTED) == 0) {
+            parse_function = parse_x509_aux;
         } else if (strcmp(name, PEM_STRING_X509_CRL) == 0) {
-            d2i = (D2I_OF(void)) d2i_X509_CRL;
-            if (xi->crl != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-            pp = &(xi->crl);
+            parse_function = parse_crl;
         } else if (strcmp(name, PEM_STRING_RSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_RSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_RSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#ifndef OPENSSL_NO_DSA
-        if (strcmp(name, PEM_STRING_DSA) == 0) {
-            d2i = (D2I_OF(void)) d2i_DSAPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_DSA;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else
-#endif
-        if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
-            d2i = (D2I_OF(void)) d2i_ECPrivateKey;
-            if (xi->x_pkey != NULL) {
-                if (!sk_X509_INFO_push(ret, xi))
-                    goto err;
-                if ((xi = X509_INFO_new()) == NULL)
-                    goto err;
-                goto start;
-            }
-
-            xi->enc_data = NULL;
-            xi->enc_len = 0;
-
-            xi->x_pkey = X509_PKEY_new();
-            ptype = EVP_PKEY_EC;
-            pp = &xi->x_pkey->dec_pkey;
-            if ((int)strlen(header) > 10) /* assume encrypted */
-                raw = 1;
-        } else {
-            d2i = NULL;
-            pp = NULL;
+            parse_function = parse_key;
+            key_type = EVP_PKEY_RSA;
+        } else if (strcmp(name, PEM_STRING_DSA) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_DSA;
+        } else if (strcmp(name, PEM_STRING_ECPRIVATEKEY) == 0) {
+            parse_function = parse_key;
+            key_type = EVP_PKEY_EC;
         }
 
-        if (d2i != NULL) {
-            if (!raw) {
-                EVP_CIPHER_INFO cipher;
-
-                if (!PEM_get_EVP_CIPHER_INFO(header, &cipher))
-                    goto err;
-                if (!PEM_do_header(&cipher, data, &len, cb, u))
-                    goto err;
-                p = data;
-                if (ptype) {
-                    if (!d2i_PrivateKey(ptype, pp, &p, len)) {
-                        OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
-                        goto err;
-                    }
-                } else if (d2i(pp, &p, len) == NULL) {
-                    OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+        /* If a private key has a header, assume it is encrypted. */
+        if (key_type != EVP_PKEY_NONE && strlen(header) > 10) {
+            if (info->x_pkey != NULL) {
+                if (!sk_X509_INFO_push(ret, info)) {
                     goto err;
                 }
-            } else {            /* encrypted RSA data */
-                if (!PEM_get_EVP_CIPHER_INFO(header, &xi->enc_cipher))
+                info = X509_INFO_new();
+                if (info == NULL) {
                     goto err;
-                xi->enc_data = (char *)data;
-                xi->enc_len = (int)len;
-                data = NULL;
+                }
             }
-        } else {
-            /* unknown */
+            /* Historically, raw entries pushed an empty key. */
+            info->x_pkey = X509_PKEY_new();
+            if (info->x_pkey == NULL ||
+                !PEM_get_EVP_CIPHER_INFO(header, &info->enc_cipher)) {
+                goto err;
+            }
+            info->enc_data = (char *)data;
+            info->enc_len = (int)len;
+            data = NULL;
+        } else if (parse_function != NULL) {
+            EVP_CIPHER_INFO cipher;
+            if (!PEM_get_EVP_CIPHER_INFO(header, &cipher) ||
+                !PEM_do_header(&cipher, data, &len, cb, u)) {
+                goto err;
+            }
+            enum parse_result_t result =
+                parse_function(info, data, len, key_type);
+            if (result == parse_new_entry) {
+                if (!sk_X509_INFO_push(ret, info)) {
+                    goto err;
+                }
+                info = X509_INFO_new();
+                if (info == NULL) {
+                    goto err;
+                }
+                result = parse_function(info, data, len, key_type);
+            }
+            if (result != parse_ok) {
+                OPENSSL_PUT_ERROR(PEM, ERR_R_ASN1_LIB);
+                goto err;
+            }
         }
-        if (name != NULL)
-            OPENSSL_free(name);
-        if (header != NULL)
-            OPENSSL_free(header);
-        if (data != NULL)
-            OPENSSL_free(data);
+        OPENSSL_free(name);
+        OPENSSL_free(header);
+        OPENSSL_free(data);
         name = NULL;
         header = NULL;
         data = NULL;
     }
 
-    /*
-     * if the last one hasn't been pushed yet and there is anything in it
-     * then add it to the stack ...
-     */
-    if ((xi->x509 != NULL) || (xi->crl != NULL) ||
-        (xi->x_pkey != NULL) || (xi->enc_data != NULL)) {
-        if (!sk_X509_INFO_push(ret, xi))
+    /* Push the last entry on the stack if not empty. */
+    if (info->x509 != NULL || info->crl != NULL ||
+        info->x_pkey != NULL || info->enc_data != NULL) {
+        if (!sk_X509_INFO_push(ret, info)) {
             goto err;
-        xi = NULL;
-    }
-    ok = 1;
- err:
-    if (xi != NULL)
-        X509_INFO_free(xi);
-    if (!ok) {
-        for (i = 0; i < sk_X509_INFO_num(ret); i++) {
-            xi = sk_X509_INFO_value(ret, i);
-            X509_INFO_free(xi);
         }
-        if (ret != sk)
+        info = NULL;
+    }
+
+    ok = 1;
+
+ err:
+    X509_INFO_free(info);
+    if (!ok) {
+        while (sk_X509_INFO_num(ret) > orig_num) {
+            X509_INFO_free(sk_X509_INFO_pop(ret));
+        }
+        if (ret != sk) {
             sk_X509_INFO_free(ret);
+        }
         ret = NULL;
     }
 
-    if (name != NULL)
-        OPENSSL_free(name);
-    if (header != NULL)
-        OPENSSL_free(header);
-    if (data != NULL)
-        OPENSSL_free(data);
-    return (ret);
+    OPENSSL_free(name);
+    OPENSSL_free(header);
+    OPENSSL_free(data);
+    return ret;
 }
 
 /* A TJH addition */
diff --git a/src/crypto/pem/pem_lib.c b/src/crypto/pem/pem_lib.c
index 5180e55..c682429 100644
--- a/src/crypto/pem/pem_lib.c
+++ b/src/crypto/pem/pem_lib.c
@@ -121,17 +121,14 @@
 void *PEM_ASN1_read(d2i_of_void *d2i, const char *name, FILE *fp, void **x,
                     pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    void *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
+    void *ret = PEM_ASN1_read_bio(d2i, name, b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -220,8 +217,11 @@
 
     for (;;) {
         if (!PEM_read_bio(bp, &nm, &header, &data, &len)) {
-            if (ERR_GET_REASON(ERR_peek_error()) == PEM_R_NO_START_LINE)
+            uint32_t error = ERR_peek_error();
+            if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                ERR_GET_REASON(error) == PEM_R_NO_START_LINE) {
                 ERR_add_error_data(2, "Expecting: ", name);
+            }
             return 0;
         }
         if (check_pem(nm, name))
@@ -257,17 +257,14 @@
                    void *x, const EVP_CIPHER *enc, unsigned char *kstr,
                    int klen, pem_password_cb *callback, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
+    int ret = PEM_ASN1_write_bio(i2d, name, b, x, enc, kstr, klen, callback, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
@@ -514,15 +511,12 @@
 int PEM_write(FILE *fp, const char *name, const char *header,
               const unsigned char *data, long len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_write_bio(b, name, header, data, len);
+    int ret = PEM_write_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
@@ -588,15 +582,12 @@
 int PEM_read(FILE *fp, char **name, char **header, unsigned char **data,
              long *len)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio(b, name, header, data, len);
+    int ret = PEM_read_bio(b, name, header, data, len);
     BIO_free(b);
     return (ret);
 }
diff --git a/src/crypto/pem/pem_pkey.c b/src/crypto/pem/pem_pkey.c
index 9fbaeef..725a84b 100644
--- a/src/crypto/pem/pem_pkey.c
+++ b/src/crypto/pem/pem_pkey.c
@@ -155,31 +155,26 @@
 EVP_PKEY *PEM_read_PrivateKey(FILE *fp, EVP_PKEY **x, pem_password_cb *cb,
                               void *u)
 {
-    BIO *b;
-    EVP_PKEY *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_PrivateKey(b, x, cb, u);
+    EVP_PKEY *ret = PEM_read_bio_PrivateKey(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int PEM_write_PrivateKey(FILE *fp, EVP_PKEY *x, const EVP_CIPHER *enc,
                          unsigned char *kstr, int klen,
                          pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new_fp(fp, BIO_NOCLOSE)) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
         return 0;
     }
-    ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
+    int ret = PEM_write_bio_PrivateKey(b, x, enc, kstr, klen, cb, u);
     BIO_free(b);
     return ret;
 }
@@ -212,16 +207,13 @@
 #ifndef OPENSSL_NO_FP_API
 DH *PEM_read_DHparams(FILE *fp, DH **x, pem_password_cb *cb, void *u)
 {
-    BIO *b;
-    DH *ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(PEM, ERR_R_BUF_LIB);
-        return (0);
+        return NULL;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = PEM_read_bio_DHparams(b, x, cb, u);
+    DH *ret = PEM_read_bio_DHparams(b, x, cb, u);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
diff --git a/src/crypto/pkcs7/pkcs7_x509.c b/src/crypto/pkcs7/pkcs7_x509.c
index a2a6b46..d6ca44e 100644
--- a/src/crypto/pkcs7/pkcs7_x509.c
+++ b/src/crypto/pkcs7/pkcs7_x509.c
@@ -333,17 +333,7 @@
 }
 
 int i2d_PKCS7_bio(BIO *bio, const PKCS7 *p7) {
-  size_t written = 0;
-  while (written < p7->ber_len) {
-    size_t todo = p7->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p7->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p7->ber_bytes, p7->ber_len);
 }
 
 void PKCS7_free(PKCS7 *p7) {
diff --git a/src/crypto/pkcs8/pkcs8_x509.c b/src/crypto/pkcs8/pkcs8_x509.c
index dc74d96..2c7841e 100644
--- a/src/crypto/pkcs8/pkcs8_x509.c
+++ b/src/crypto/pkcs8/pkcs8_x509.c
@@ -861,17 +861,7 @@
 }
 
 int i2d_PKCS12_bio(BIO *bio, const PKCS12 *p12) {
-  size_t written = 0;
-  while (written < p12->ber_len) {
-    size_t todo = p12->ber_len - written;
-    int len = todo > INT_MAX ? INT_MAX : (int)todo;
-    int ret = BIO_write(bio, p12->ber_bytes + written, len);
-    if (ret <= 0) {
-      return 0;
-    }
-    written += (size_t)ret;
-  }
-  return 1;
+  return BIO_write_all(bio, p12->ber_bytes, p12->ber_len);
 }
 
 int i2d_PKCS12_fp(FILE *fp, const PKCS12 *p12) {
diff --git a/src/crypto/pool/pool_test.cc b/src/crypto/pool/pool_test.cc
index 62fb17d..8f32fb6 100644
--- a/src/crypto/pool/pool_test.cc
+++ b/src/crypto/pool/pool_test.cc
@@ -18,7 +18,7 @@
 
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <chrono>
 #include <thread>
 #endif
@@ -61,7 +61,7 @@
   EXPECT_EQ(buf.get(), buf2.get()) << "CRYPTO_BUFFER_POOL did not dedup data.";
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(PoolTest, Threads) {
   bssl::UniquePtr<CRYPTO_BUFFER_POOL> pool(CRYPTO_BUFFER_POOL_new());
   ASSERT_TRUE(pool);
diff --git a/src/crypto/rand_extra/rand_test.cc b/src/crypto/rand_extra/rand_test.cc
index bd2eb18..d0a7e40 100644
--- a/src/crypto/rand_extra/rand_test.cc
+++ b/src/crypto/rand_extra/rand_test.cc
@@ -20,7 +20,7 @@
 
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <array>
 #include <thread>
 #include <vector>
@@ -50,7 +50,8 @@
   EXPECT_NE(Bytes(buf2), Bytes(kZeros));
 }
 
-#if !defined(OPENSSL_WINDOWS) && !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
+#if !defined(OPENSSL_WINDOWS) && !defined(OPENSSL_IOS) && \
+    !defined(BORINGSSL_UNSAFE_DETERMINISTIC_MODE)
 static bool ForkAndRand(bssl::Span<uint8_t> out) {
   int pipefds[2];
   if (pipe(pipefds) < 0) {
@@ -144,9 +145,10 @@
   EXPECT_NE(Bytes(buf2), Bytes(kZeros));
   EXPECT_NE(Bytes(buf3), Bytes(kZeros));
 }
-#endif  // !OPENSSL_WINDOWS && !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
+#endif  // !OPENSSL_WINDOWS && !OPENSSL_IOS &&
+        // !BORINGSSL_UNSAFE_DETERMINISTIC_MODE
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 static void RunConcurrentRands(size_t num_threads) {
   static const uint8_t kZeros[256] = {0};
 
diff --git a/src/crypto/refcount_test.cc b/src/crypto/refcount_test.cc
index 6ce0746..700863f 100644
--- a/src/crypto/refcount_test.cc
+++ b/src/crypto/refcount_test.cc
@@ -16,7 +16,7 @@
 
 #include <gtest/gtest.h>
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #endif
 
@@ -43,7 +43,7 @@
   EXPECT_EQ(1u, count);
 }
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 // This test is primarily intended to run under ThreadSanitizer.
 TEST(RefCountTest, Threads) {
   CRYPTO_refcount_t count = 0;
diff --git a/src/crypto/rsa_extra/rsa_test.cc b/src/crypto/rsa_extra/rsa_test.cc
index b0a0b7e..9bd47b1 100644
--- a/src/crypto/rsa_extra/rsa_test.cc
+++ b/src/crypto/rsa_extra/rsa_test.cc
@@ -72,7 +72,7 @@
 #include "../internal.h"
 #include "../test/test_util.h"
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 #include <thread>
 #include <vector>
 #endif
@@ -1048,7 +1048,7 @@
 }
 #endif  // !BORINGSSL_SHARED_LIBRARY
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 TEST(RSATest, Threads) {
   bssl::UniquePtr<RSA> rsa_template(
       RSA_private_key_from_bytes(kKey1, sizeof(kKey1) - 1));
diff --git a/src/crypto/stack/stack.c b/src/crypto/stack/stack.c
index 7aa3218..93b9d1b 100644
--- a/src/crypto/stack/stack.c
+++ b/src/crypto/stack/stack.c
@@ -133,19 +133,31 @@
   OPENSSL_free(sk);
 }
 
-void sk_pop_free(_STACK *sk, void (*func)(void *)) {
+void sk_pop_free_ex(_STACK *sk, void (*call_free_func)(stack_free_func, void *),
+                    stack_free_func free_func) {
   if (sk == NULL) {
     return;
   }
 
   for (size_t i = 0; i < sk->num; i++) {
     if (sk->data[i] != NULL) {
-      func(sk->data[i]);
+      call_free_func(free_func, sk->data[i]);
     }
   }
   sk_free(sk);
 }
 
+// Historically, |sk_pop_free| called the function as |stack_free_func|
+// directly. This is undefined in C. Some callers called |sk_pop_free| directly,
+// so we must maintain a compatibility version for now.
+static void call_free_func_legacy(stack_free_func func, void *ptr) {
+  func(ptr);
+}
+
+void sk_pop_free(_STACK *sk, stack_free_func free_func) {
+  sk_pop_free_ex(sk, call_free_func_legacy, free_func);
+}
+
 size_t sk_insert(_STACK *sk, void *p, size_t where) {
   if (sk == NULL) {
     return 0;
@@ -209,7 +221,7 @@
   return ret;
 }
 
-void *sk_delete_ptr(_STACK *sk, void *p) {
+void *sk_delete_ptr(_STACK *sk, const void *p) {
   if (sk == NULL) {
     return NULL;
   }
@@ -223,7 +235,9 @@
   return NULL;
 }
 
-int sk_find(const _STACK *sk, size_t *out_index, void *p) {
+int sk_find(const _STACK *sk, size_t *out_index, const void *p,
+            int (*call_cmp_func)(stack_cmp_func, const void **,
+                                 const void **)) {
   if (sk == NULL) {
     return 0;
   }
@@ -247,7 +261,8 @@
 
   if (!sk_is_sorted(sk)) {
     for (size_t i = 0; i < sk->num; i++) {
-      if (sk->comp((const void **)&p, (const void **)&sk->data[i]) == 0) {
+      const void *elem = sk->data[i];
+      if (call_cmp_func(sk->comp, &p, &elem) == 0) {
         if (out_index) {
           *out_index = i;
         }
@@ -262,15 +277,25 @@
   // elements. However, since we're passing an array of pointers to
   // qsort/bsearch, we can just cast the comparison function and everything
   // works.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |bsearch| is missing a void*
+  // parameter in its callback and |bsearch_s| is a mess of incompatibility.
   const void *const *r = bsearch(&p, sk->data, sk->num, sizeof(void *),
                                  (int (*)(const void *, const void *))sk->comp);
   if (r == NULL) {
     return 0;
   }
   size_t idx = ((void **)r) - sk->data;
-  // This function always returns the first result.
-  while (idx > 0 &&
-         sk->comp((const void **)&p, (const void **)&sk->data[idx - 1]) == 0) {
+  // This function always returns the first result. Note this logic is, in the
+  // worst case, O(N) rather than O(log(N)). If this ever becomes a problem,
+  // restore https://boringssl-review.googlesource.com/c/boringssl/+/32115/
+  // which integrates the preference into the binary search.
+  while (idx > 0) {
+    const void *elem = sk->data[idx - 1];
+    if (call_cmp_func(sk->comp, &p, &elem) != 0) {
+      break;
+    }
     idx--;
   }
   if (out_index) {
@@ -340,6 +365,11 @@
   }
 
   // See the comment in sk_find about this cast.
+  //
+  // TODO(davidben): This is undefined behavior, but the call is in libc so,
+  // e.g., CFI does not notice. Unfortunately, |qsort| is missing a void*
+  // parameter in its callback and |qsort_s| / |qsort_r| are a mess of
+  // incompatibility.
   comp_func = (int (*)(const void *, const void *))(sk->comp);
   qsort(sk->data, sk->num, sizeof(void *), comp_func);
   sk->sorted = 1;
@@ -363,8 +393,11 @@
   return old;
 }
 
-_STACK *sk_deep_copy(const _STACK *sk, void *(*copy_func)(void *),
-                     void (*free_func)(void *)) {
+_STACK *sk_deep_copy(const _STACK *sk,
+                     void *(*call_copy_func)(stack_copy_func, void *),
+                     stack_copy_func copy_func,
+                     void (*call_free_func)(stack_free_func, void *),
+                     stack_free_func free_func) {
   _STACK *ret = sk_dup(sk);
   if (ret == NULL) {
     return NULL;
@@ -374,11 +407,11 @@
     if (ret->data[i] == NULL) {
       continue;
     }
-    ret->data[i] = copy_func(ret->data[i]);
+    ret->data[i] = call_copy_func(copy_func, ret->data[i]);
     if (ret->data[i] == NULL) {
       for (size_t j = 0; j < i; j++) {
         if (ret->data[j] != NULL) {
-          free_func(ret->data[j]);
+          call_free_func(free_func, ret->data[j]);
         }
       }
       sk_free(ret);
diff --git a/src/crypto/stack/stack_test.cc b/src/crypto/stack/stack_test.cc
new file mode 100644
index 0000000..8b26971
--- /dev/null
+++ b/src/crypto/stack/stack_test.cc
@@ -0,0 +1,394 @@
+/* Copyright (c) 2018, 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/stack.h>
+
+#include <limits.h>
+
+#include <algorithm>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <openssl/mem.h>
+
+
+// Define a custom stack type for testing.
+using TEST_INT = int;
+
+static void TEST_INT_free(TEST_INT *x) { OPENSSL_free(x); }
+
+namespace bssl {
+BORINGSSL_MAKE_DELETER(TEST_INT, TEST_INT_free)
+}  // namespace bssl
+
+static bssl::UniquePtr<TEST_INT> TEST_INT_new(int x) {
+  bssl::UniquePtr<TEST_INT> ret(
+      static_cast<TEST_INT *>(OPENSSL_malloc(sizeof(TEST_INT))));
+  if (!ret) {
+    return nullptr;
+  }
+  *ret = x;
+  return ret;
+}
+
+DEFINE_STACK_OF(TEST_INT)
+
+struct ShallowStackDeleter {
+  void operator()(STACK_OF(TEST_INT) *sk) const { sk_TEST_INT_free(sk); }
+};
+
+using ShallowStack = std::unique_ptr<STACK_OF(TEST_INT), ShallowStackDeleter>;
+
+// kNull is treated as a nullptr expectation for purposes of ExpectStackEquals.
+// The tests in this file will never use it as a test value.
+static const int kNull = INT_MIN;
+
+static void ExpectStackEquals(const STACK_OF(TEST_INT) *sk,
+                              const std::vector<int> &vec) {
+  EXPECT_EQ(vec.size(), sk_TEST_INT_num(sk));
+  for (size_t i = 0; i < vec.size(); i++) {
+    SCOPED_TRACE(i);
+    const TEST_INT *obj = sk_TEST_INT_value(sk, i);
+    if (vec[i] == kNull) {
+      EXPECT_FALSE(obj);
+    } else {
+      EXPECT_TRUE(obj);
+      if (obj) {
+        EXPECT_EQ(vec[i], *obj);
+      }
+    }
+  }
+
+  // Reading out-of-bounds fails.
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size()));
+  EXPECT_FALSE(sk_TEST_INT_value(sk, vec.size() + 1));
+}
+
+TEST(StackTest, Basic) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  // The stack starts out empty.
+  ExpectStackEquals(sk.get(), {});
+
+  // Removing elements from an empty stack does nothing.
+  EXPECT_FALSE(sk_TEST_INT_pop(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_shift(sk.get()));
+  EXPECT_FALSE(sk_TEST_INT_delete(sk.get(), 0));
+
+  // Push some elements.
+  for (int i = 0; i < 6; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 4, 5});
+
+  // Items may be inserted in the middle.
+  auto value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  // Hold on to the object for later.
+  TEST_INT *raw = value.get();
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 4));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5});
+
+  // Without a comparison function, find searches by pointer.
+  value = TEST_INT_new(6);
+  ASSERT_TRUE(value);
+  size_t index;
+  EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, value.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, raw));
+  EXPECT_EQ(4u, index);
+
+  // sk_TEST_INT_insert can also insert values at the end.
+  value = TEST_INT_new(7);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 7));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  // Out-of-bounds indices are clamped.
+  value = TEST_INT_new(8);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), value.get(), 999));
+  value.release();  // sk_TEST_INT_insert takes ownership on success.
+
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7, 8});
+
+  // Test removing elements from various places.
+  bssl::UniquePtr<TEST_INT> removed(sk_TEST_INT_pop(sk.get()));
+  EXPECT_EQ(8, *removed);
+  ExpectStackEquals(sk.get(), {0, 1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_shift(sk.get()));
+  EXPECT_EQ(0, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 3, 6, 4, 5, 7});
+
+  removed.reset(sk_TEST_INT_delete(sk.get(), 2));
+  EXPECT_EQ(3, *removed);
+  ExpectStackEquals(sk.get(), {1, 2, 6, 4, 5, 7});
+
+  // Objects may also be deleted by pointer.
+  removed.reset(sk_TEST_INT_delete_ptr(sk.get(), raw));
+  EXPECT_EQ(raw, removed.get());
+  ExpectStackEquals(sk.get(), {1, 2, 4, 5, 7});
+
+  // Deleting is a no-op is the object is not found.
+  value = TEST_INT_new(100);
+  ASSERT_TRUE(value);
+  EXPECT_FALSE(sk_TEST_INT_delete_ptr(sk.get(), value.get()));
+
+  // Insert nullptr to test deep copy handling of it.
+  ASSERT_TRUE(sk_TEST_INT_insert(sk.get(), nullptr, 0));
+  ExpectStackEquals(sk.get(), {kNull, 1, 2, 4, 5, 7});
+
+  // Test both deep and shallow copies.
+  bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+      sk.get(),
+      [](TEST_INT *x) -> TEST_INT * {
+        return x == nullptr ? nullptr : TEST_INT_new(*x).release();
+      },
+      TEST_INT_free));
+  ASSERT_TRUE(copy);
+  ExpectStackEquals(copy.get(), {kNull, 1, 2, 4, 5, 7});
+
+  ShallowStack shallow(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow);
+  ASSERT_EQ(sk_TEST_INT_num(sk.get()), sk_TEST_INT_num(shallow.get()));
+  for (size_t i = 0; i < sk_TEST_INT_num(sk.get()); i++) {
+    EXPECT_EQ(sk_TEST_INT_value(sk.get(), i),
+              sk_TEST_INT_value(shallow.get(), i));
+  }
+
+  // Deep copies may fail. This should clean up temporaries.
+  EXPECT_FALSE(sk_TEST_INT_deep_copy(sk.get(),
+                                     [](TEST_INT *x) -> TEST_INT * {
+                                       return x == nullptr || *x == 4
+                                                  ? nullptr
+                                                  : TEST_INT_new(*x).release();
+                                     },
+                                     TEST_INT_free));
+
+  // sk_TEST_INT_zero clears a stack, but does not free the elements.
+  ShallowStack shallow2(sk_TEST_INT_dup(sk.get()));
+  ASSERT_TRUE(shallow2);
+  sk_TEST_INT_zero(shallow2.get());
+  ExpectStackEquals(shallow2.get(), {});
+}
+
+TEST(StackTest, BigStack) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new_null());
+  ASSERT_TRUE(sk);
+
+  std::vector<int> expected;
+  static const int kCount = 100000;
+  for (int i = 0; i < kCount; i++) {
+    auto value = TEST_INT_new(i);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    expected.push_back(i);
+  }
+  ExpectStackEquals(sk.get(), expected);
+}
+
+static uint64_t g_compare_count = 0;
+
+static int compare(const TEST_INT **a, const TEST_INT **b) {
+  g_compare_count++;
+  if (**a < **b) {
+    return -1;
+  }
+  if (**a > **b) {
+    return 1;
+  }
+  return 0;
+}
+
+static int compare_reverse(const TEST_INT **a, const TEST_INT **b) {
+  return -compare(a, b);
+}
+
+TEST(StackTest, Sorted) {
+  std::vector<int> vec_sorted = {0, 1, 2, 3, 4, 5, 6};
+  std::vector<int> vec = vec_sorted;
+  do {
+    bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+    ASSERT_TRUE(sk);
+    for (int v : vec) {
+      auto value = TEST_INT_new(v);
+      ASSERT_TRUE(value);
+      ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+    }
+
+    // The stack is not (known to be) sorted.
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // With a comparison function, find matches by value.
+    auto ten = TEST_INT_new(10);
+    ASSERT_TRUE(ten);
+    size_t index;
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    auto three = TEST_INT_new(3);
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3, *sk_TEST_INT_value(sk.get(), index));
+
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // Sorting an already-sorted list is a no-op.
+    uint64_t old_compare_count = g_compare_count;
+    sk_TEST_INT_sort(sk.get());
+    EXPECT_EQ(old_compare_count, g_compare_count);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+    ExpectStackEquals(sk.get(), vec_sorted);
+
+    // When sorted, find uses binary search.
+    ASSERT_TRUE(ten);
+    EXPECT_FALSE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+
+    ASSERT_TRUE(three);
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Copies preserve comparison and sorted information.
+    bssl::UniquePtr<STACK_OF(TEST_INT)> copy(sk_TEST_INT_deep_copy(
+        sk.get(),
+        [](TEST_INT *x) -> TEST_INT * { return TEST_INT_new(*x).release(); },
+        TEST_INT_free));
+    ASSERT_TRUE(copy);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    ShallowStack copy2(sk_TEST_INT_dup(sk.get()));
+    ASSERT_TRUE(copy2);
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(copy2.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(copy2.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Removing elements does not affect sortedness.
+    TEST_INT_free(sk_TEST_INT_delete(sk.get(), 0));
+    EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+
+    // Changing the comparison function invalidates sortedness.
+    sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(2u, index);
+
+    sk_TEST_INT_sort(sk.get());
+    ExpectStackEquals(sk.get(), {6, 5, 4, 3, 2, 1});
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, three.get()));
+    EXPECT_EQ(3u, index);
+
+    // Inserting a new element invalidates sortedness.
+    auto tmp = TEST_INT_new(10);
+    ASSERT_TRUE(tmp);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(tmp)));
+    EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+    ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, ten.get()));
+    EXPECT_EQ(6u, index);
+  } while (std::next_permutation(vec.begin(), vec.end()));
+}
+
+// sk_*_find should return the first matching element in all cases.
+TEST(StackTest, FindFirst) {
+  bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+  auto value = TEST_INT_new(1);
+  ASSERT_TRUE(value);
+  ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  for (int i = 0; i < 10; i++) {
+    value = TEST_INT_new(2);
+    ASSERT_TRUE(value);
+    ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+  }
+
+  const TEST_INT *two = sk_TEST_INT_value(sk.get(), 1);
+  // Pointer-based equality.
+  size_t index;
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, unsorted.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare);
+  EXPECT_FALSE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted.
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(1u, index);
+
+  // Comparator-based equality, sorted and at the front.
+  sk_TEST_INT_set_cmp_func(sk.get(), compare_reverse);
+  sk_TEST_INT_sort(sk.get());
+  EXPECT_TRUE(sk_TEST_INT_is_sorted(sk.get()));
+  ASSERT_TRUE(sk_TEST_INT_find(sk.get(), &index, two));
+  EXPECT_EQ(0u, index);
+}
+
+// Exhaustively test the binary search.
+TEST(StackTest, BinarySearch) {
+  static const size_t kCount = 100;
+  for (size_t i = 0; i < kCount; i++) {
+    SCOPED_TRACE(i);
+    for (size_t j = i; j <= kCount; j++) {
+      SCOPED_TRACE(j);
+      // Make a stack where [0, i) are below, [i, j) match, and [j, kCount) are
+      // above.
+      bssl::UniquePtr<STACK_OF(TEST_INT)> sk(sk_TEST_INT_new(compare));
+      ASSERT_TRUE(sk);
+      for (size_t k = 0; k < i; k++) {
+        auto value = TEST_INT_new(-1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = i; k < j; k++) {
+        auto value = TEST_INT_new(0);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      for (size_t k = j; k < kCount; k++) {
+        auto value = TEST_INT_new(1);
+        ASSERT_TRUE(value);
+        ASSERT_TRUE(bssl::PushToStack(sk.get(), std::move(value)));
+      }
+      sk_TEST_INT_sort(sk.get());
+
+      auto key = TEST_INT_new(0);
+      ASSERT_TRUE(key);
+
+      size_t idx;
+      int found = sk_TEST_INT_find(sk.get(), &idx, key.get());
+      if (i == j) {
+        EXPECT_FALSE(found);
+      } else {
+        ASSERT_TRUE(found);
+        EXPECT_EQ(i, idx);
+      }
+    }
+  }
+}
diff --git a/src/crypto/thread_none.c b/src/crypto/thread_none.c
index 718d960..4f07b9d 100644
--- a/src/crypto/thread_none.c
+++ b/src/crypto/thread_none.c
@@ -14,7 +14,7 @@
 
 #include "internal.h"
 
-#if defined(OPENSSL_NO_THREADS)
+#if !defined(OPENSSL_THREADS)
 
 void CRYPTO_MUTEX_init(CRYPTO_MUTEX *lock) {}
 
@@ -56,4 +56,4 @@
   return 1;
 }
 
-#endif  // OPENSSL_NO_THREADS
+#endif  // !OPENSSL_THREADS
diff --git a/src/crypto/thread_test.cc b/src/crypto/thread_test.cc
index f89b22e..f9fad9b 100644
--- a/src/crypto/thread_test.cc
+++ b/src/crypto/thread_test.cc
@@ -25,7 +25,7 @@
 #include "test/test_util.h"
 
 
-#if !defined(OPENSSL_NO_THREADS)
+#if defined(OPENSSL_THREADS)
 
 static unsigned g_once_init_called = 0;
 
@@ -130,4 +130,4 @@
   thread.join();
 }
 
-#endif  // !OPENSSL_NO_THREADS
+#endif  // OPENSSL_THREADS
diff --git a/src/crypto/x509/by_file.c b/src/crypto/x509/by_file.c
index 555cb85..dfff425 100644
--- a/src/crypto/x509/by_file.c
+++ b/src/crypto/x509/by_file.c
@@ -138,14 +138,15 @@
         for (;;) {
             x = PEM_read_bio_X509_AUX(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_cert(ctx->store_ctx, x);
             if (!i)
@@ -197,14 +198,15 @@
         for (;;) {
             x = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
             if (x == NULL) {
-                if ((ERR_GET_REASON(ERR_peek_last_error()) ==
-                     PEM_R_NO_START_LINE) && (count > 0)) {
+                uint32_t error = ERR_peek_last_error();
+                if (ERR_GET_LIB(error) == ERR_LIB_PEM &&
+                    ERR_GET_REASON(error) == PEM_R_NO_START_LINE &&
+                    count > 0) {
                     ERR_clear_error();
                     break;
-                } else {
-                    OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
-                    goto err;
                 }
+                OPENSSL_PUT_ERROR(X509, ERR_R_PEM_LIB);
+                goto err;
             }
             i = X509_STORE_add_crl(ctx->store_ctx, x);
             if (!i)
diff --git a/src/crypto/x509/t_crl.c b/src/crypto/x509/t_crl.c
index 6c347cb..dc9b87f 100644
--- a/src/crypto/x509/t_crl.c
+++ b/src/crypto/x509/t_crl.c
@@ -64,17 +64,14 @@
 #ifndef OPENSSL_NO_FP_API
 int X509_CRL_print_fp(FILE *fp, X509_CRL *x)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_CRL_print(b, x);
+    int ret = X509_CRL_print(b, x);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 #endif
 
diff --git a/src/crypto/x509/t_req.c b/src/crypto/x509/t_req.c
index 39c836c..2fd36f8 100644
--- a/src/crypto/x509/t_req.c
+++ b/src/crypto/x509/t_req.c
@@ -65,13 +65,11 @@
 
 
 int X509_REQ_print_fp(FILE *fp, X509_REQ *x) {
-  BIO *bio = BIO_new(BIO_s_file());
+  BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);
   if (bio == NULL) {
     OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
     return 0;
   }
-
-  BIO_set_fp(bio, fp, BIO_NOCLOSE);
   int ret = X509_REQ_print(bio, x);
   BIO_free(bio);
   return ret;
diff --git a/src/crypto/x509/t_x509.c b/src/crypto/x509/t_x509.c
index 3339523..e45a765 100644
--- a/src/crypto/x509/t_x509.c
+++ b/src/crypto/x509/t_x509.c
@@ -72,17 +72,14 @@
 int X509_print_ex_fp(FILE *fp, X509 *x, unsigned long nmflag,
                      unsigned long cflag)
 {
-    BIO *b;
-    int ret;
-
-    if ((b = BIO_new(BIO_s_file())) == NULL) {
+    BIO *b = BIO_new_fp(fp, BIO_NOCLOSE);
+    if (b == NULL) {
         OPENSSL_PUT_ERROR(X509, ERR_R_BUF_LIB);
-        return (0);
+        return 0;
     }
-    BIO_set_fp(b, fp, BIO_NOCLOSE);
-    ret = X509_print_ex(b, x, nmflag, cflag);
+    int ret = X509_print_ex(b, x, nmflag, cflag);
     BIO_free(b);
-    return (ret);
+    return ret;
 }
 
 int X509_print_fp(FILE *fp, X509 *x)
diff --git a/src/crypto/x509/x509_test.cc b/src/crypto/x509/x509_test.cc
index 9ed1b52..bf0b29a 100644
--- a/src/crypto/x509/x509_test.cc
+++ b/src/crypto/x509/x509_test.cc
@@ -1549,3 +1549,125 @@
   EXPECT_EQ(ERR_LIB_X509, ERR_GET_LIB(err));
   EXPECT_EQ(X509_R_SIGNATURE_ALGORITHM_MISMATCH, ERR_GET_REASON(err));
 }
+
+TEST(X509Test, PEMX509Info) {
+  std::string cert = kRootCAPEM;
+  auto cert_obj = CertFromPEM(kRootCAPEM);
+  ASSERT_TRUE(cert_obj);
+
+  std::string rsa = kRSAKey;
+  auto rsa_obj = PrivateKeyFromPEM(kRSAKey);
+  ASSERT_TRUE(rsa_obj);
+
+  std::string crl = kBasicCRL;
+  auto crl_obj = CRLFromPEM(kBasicCRL);
+  ASSERT_TRUE(crl_obj);
+
+  std::string unknown =
+      "-----BEGIN UNKNOWN-----\n"
+      "AAAA\n"
+      "-----END UNKNOWN-----\n";
+
+  std::string invalid =
+      "-----BEGIN CERTIFICATE-----\n"
+      "AAAA\n"
+      "-----END CERTIFICATE-----\n";
+
+  // Each X509_INFO contains at most one certificate, CRL, etc. The format
+  // creates a new X509_INFO when a repeated type is seen.
+  std::string pem =
+      // The first few entries have one of everything in different orders.
+      cert + rsa + crl +
+      rsa + crl + cert +
+      // Unknown types are ignored.
+      crl + unknown + cert + rsa +
+      // Seeing a new certificate starts a new entry, so now we have a bunch of
+      // certificate-only entries.
+      cert + cert + cert +
+      // The key folds into the certificate's entry.
+      cert + rsa +
+      // Doubled keys also start new entries.
+      rsa + rsa + rsa + rsa + crl +
+      // As do CRLs.
+      crl + crl;
+
+  const struct ExpectedInfo {
+    const X509 *cert;
+    const EVP_PKEY *key;
+    const X509_CRL *crl;
+  } kExpected[] = {
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), rsa_obj.get(), crl_obj.get()},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), nullptr, nullptr},
+    {cert_obj.get(), rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), nullptr},
+    {nullptr, rsa_obj.get(), crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+    {nullptr, nullptr, crl_obj.get()},
+  };
+
+  auto check_info = [](const ExpectedInfo *expected, const X509_INFO *info) {
+    if (expected->cert != nullptr) {
+      EXPECT_EQ(0, X509_cmp(expected->cert, info->x509));
+    } else {
+      EXPECT_EQ(nullptr, info->x509);
+    }
+    if (expected->crl != nullptr) {
+      EXPECT_EQ(0, X509_CRL_cmp(expected->crl, info->crl));
+    } else {
+      EXPECT_EQ(nullptr, info->crl);
+    }
+    if (expected->key != nullptr) {
+      ASSERT_NE(nullptr, info->x_pkey);
+      // EVP_PKEY_cmp returns one if the keys are equal.
+      EXPECT_EQ(1, EVP_PKEY_cmp(expected->key, info->x_pkey->dec_pkey));
+    } else {
+      EXPECT_EQ(nullptr, info->x_pkey);
+    }
+  };
+
+  bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  ASSERT_TRUE(infos);
+  ASSERT_EQ(OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+  }
+
+  // Passing an existing stack appends to it.
+  bio.reset(BIO_new_mem_buf(pem.data(), pem.size()));
+  ASSERT_TRUE(bio);
+  ASSERT_EQ(infos.get(),
+            PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  ASSERT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+  for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kExpected); i++) {
+    SCOPED_TRACE(i);
+    check_info(&kExpected[i], sk_X509_INFO_value(infos.get(), i));
+    check_info(
+        &kExpected[i],
+        sk_X509_INFO_value(infos.get(), i + OPENSSL_ARRAY_SIZE(kExpected)));
+  }
+
+  // Gracefully handle errors in both the append and fresh cases.
+  std::string bad_pem = cert + cert + invalid;
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  bssl::UniquePtr<STACK_OF(X509_INFO)> infos2(
+      PEM_X509_INFO_read_bio(bio.get(), nullptr, nullptr, nullptr));
+  EXPECT_FALSE(infos2);
+
+  bio.reset(BIO_new_mem_buf(bad_pem.data(), bad_pem.size()));
+  ASSERT_TRUE(bio);
+  EXPECT_FALSE(
+      PEM_X509_INFO_read_bio(bio.get(), infos.get(), nullptr, nullptr));
+  EXPECT_EQ(2 * OPENSSL_ARRAY_SIZE(kExpected), sk_X509_INFO_num(infos.get()));
+}
diff --git a/src/crypto/x509/x_all.c b/src/crypto/x509/x_all.c
index 064c89c..a37d7bd 100644
--- a/src/crypto/x509/x_all.c
+++ b/src/crypto/x509/x_all.c
@@ -54,14 +54,18 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <openssl/x509.h>
+
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/buf.h>
 #include <openssl/digest.h>
 #include <openssl/dsa.h>
 #include <openssl/evp.h>
+#include <openssl/mem.h>
 #include <openssl/rsa.h>
 #include <openssl/stack.h>
-#include <openssl/x509.h>
 
 int X509_verify(X509 *a, EVP_PKEY *r)
 {
@@ -201,154 +205,102 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-RSA *d2i_RSAPrivateKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPrivateKey, fp, rsa);
-}
 
-int i2d_RSAPrivateKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPrivateKey, fp, rsa);
-}
+#define IMPLEMENT_D2I_FP(type, name, bio_func) \
+  type *name(FILE *fp, type **obj) {           \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return NULL;                             \
+    }                                          \
+    type *ret = bio_func(bio, obj);            \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSAPublicKey_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp_of(RSA, RSA_new, d2i_RSAPublicKey, fp, rsa);
-}
+#define IMPLEMENT_I2D_FP(type, name, bio_func) \
+  int name(FILE *fp, type *obj) {              \
+    BIO *bio = BIO_new_fp(fp, BIO_NOCLOSE);    \
+    if (bio == NULL) {                         \
+      return 0;                                \
+    }                                          \
+    int ret = bio_func(bio, obj);              \
+    BIO_free(bio);                             \
+    return ret;                                \
+  }
 
-RSA *d2i_RSA_PUBKEY_fp(FILE *fp, RSA **rsa)
-{
-    return ASN1_d2i_fp((void *(*)(void))
-                       RSA_new, (D2I_OF(void)) d2i_RSA_PUBKEY, fp,
-                       (void **)rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPrivateKey_fp, d2i_RSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPrivateKey_fp, i2d_RSAPrivateKey_bio)
 
-int i2d_RSAPublicKey_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp_of_const(RSA, i2d_RSAPublicKey, fp, rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSAPublicKey_fp, d2i_RSAPublicKey_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSAPublicKey_fp, i2d_RSAPublicKey_bio)
 
-int i2d_RSA_PUBKEY_fp(FILE *fp, RSA *rsa)
-{
-    return ASN1_i2d_fp((I2D_OF_const(void))i2d_RSA_PUBKEY, fp, rsa);
-}
+IMPLEMENT_D2I_FP(RSA, d2i_RSA_PUBKEY_fp, d2i_RSA_PUBKEY_bio)
+IMPLEMENT_I2D_FP(RSA, i2d_RSA_PUBKEY_fp, i2d_RSA_PUBKEY_bio)
 #endif
 
-RSA *d2i_RSAPrivateKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_D2I_BIO(type, name, d2i_func)         \
+  type *name(BIO *bio, type **obj) {                    \
+    uint8_t *data;                                      \
+    size_t len;                                         \
+    if (!BIO_read_asn1(bio, &data, &len, 100 * 1024)) { \
+      return NULL;                                      \
+    }                                                   \
+    const uint8_t *ptr = data;                          \
+    type *ret = d2i_func(obj, &ptr, (long)len);         \
+    OPENSSL_free(data);                                 \
+    return ret;                                         \
+  }
 
-int i2d_RSAPrivateKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPrivateKey, bp, rsa);
-}
+#define IMPLEMENT_I2D_BIO(type, name, i2d_func) \
+  int name(BIO *bio, type *obj) {               \
+    uint8_t *data = NULL;                       \
+    int len = i2d_func(obj, &data);             \
+    if (len < 0) {                              \
+      return 0;                                 \
+    }                                           \
+    int ret = BIO_write_all(bio, data, len);    \
+    OPENSSL_free(data);                         \
+    return ret;                                 \
+  }
 
-RSA *d2i_RSAPublicKey_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSAPublicKey, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPrivateKey_bio, d2i_RSAPrivateKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPrivateKey_bio, i2d_RSAPrivateKey)
 
-RSA *d2i_RSA_PUBKEY_bio(BIO *bp, RSA **rsa)
-{
-    return ASN1_d2i_bio_of(RSA, RSA_new, d2i_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSAPublicKey_bio, d2i_RSAPublicKey)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSAPublicKey_bio, i2d_RSAPublicKey)
 
-int i2d_RSAPublicKey_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSAPublicKey, bp, rsa);
-}
-
-int i2d_RSA_PUBKEY_bio(BIO *bp, RSA *rsa)
-{
-    return ASN1_i2d_bio_of_const(RSA, i2d_RSA_PUBKEY, bp, rsa);
-}
+IMPLEMENT_D2I_BIO(RSA, d2i_RSA_PUBKEY_bio, d2i_RSA_PUBKEY)
+IMPLEMENT_I2D_BIO(RSA, i2d_RSA_PUBKEY_bio, i2d_RSA_PUBKEY)
 
 #ifndef OPENSSL_NO_DSA
 # ifndef OPENSSL_NO_FP_API
-DSA *d2i_DSAPrivateKey_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSAPrivateKey, fp, dsa);
-}
+IMPLEMENT_D2I_FP(DSA, d2i_DSAPrivateKey_fp, d2i_DSAPrivateKey_bio)
+IMPLEMENT_I2D_FP(DSA, i2d_DSAPrivateKey_fp, i2d_DSAPrivateKey_bio)
 
-int i2d_DSAPrivateKey_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSAPrivateKey, fp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_fp(FILE *fp, DSA **dsa)
-{
-    return ASN1_d2i_fp_of(DSA, DSA_new, d2i_DSA_PUBKEY, fp, dsa);
-}
-
-int i2d_DSA_PUBKEY_fp(FILE *fp, DSA *dsa)
-{
-    return ASN1_i2d_fp_of_const(DSA, i2d_DSA_PUBKEY, fp, dsa);
-}
+IMPLEMENT_D2I_FP(DSA, d2i_DSA_PUBKEY_fp, d2i_DSA_PUBKEY_bio)
+IMPLEMENT_I2D_FP(DSA, i2d_DSA_PUBKEY_fp, i2d_DSA_PUBKEY_bio)
 # endif
 
-DSA *d2i_DSAPrivateKey_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSAPrivateKey, bp, dsa);
-}
+IMPLEMENT_D2I_BIO(DSA, d2i_DSAPrivateKey_bio, d2i_DSAPrivateKey)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSAPrivateKey_bio, i2d_DSAPrivateKey)
 
-int i2d_DSAPrivateKey_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSAPrivateKey, bp, dsa);
-}
-
-DSA *d2i_DSA_PUBKEY_bio(BIO *bp, DSA **dsa)
-{
-    return ASN1_d2i_bio_of(DSA, DSA_new, d2i_DSA_PUBKEY, bp, dsa);
-}
-
-int i2d_DSA_PUBKEY_bio(BIO *bp, DSA *dsa)
-{
-    return ASN1_i2d_bio_of_const(DSA, i2d_DSA_PUBKEY, bp, dsa);
-}
-
+IMPLEMENT_D2I_BIO(DSA, d2i_DSA_PUBKEY_bio, d2i_DSA_PUBKEY)
+IMPLEMENT_I2D_BIO(DSA, i2d_DSA_PUBKEY_bio, i2d_DSA_PUBKEY)
 #endif
 
 #ifndef OPENSSL_NO_FP_API
-EC_KEY *d2i_EC_PUBKEY_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, fp, eckey);
-}
+IMPLEMENT_D2I_FP(EC_KEY, d2i_ECPrivateKey_fp, d2i_ECPrivateKey_bio)
+IMPLEMENT_I2D_FP(EC_KEY, i2d_ECPrivateKey_fp, i2d_ECPrivateKey_bio)
 
-int i2d_EC_PUBKEY_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_EC_PUBKEY, fp, eckey);
-}
-
-EC_KEY *d2i_ECPrivateKey_fp(FILE *fp, EC_KEY **eckey)
-{
-    return ASN1_d2i_fp_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, fp, eckey);
-}
-
-int i2d_ECPrivateKey_fp(FILE *fp, EC_KEY *eckey)
-{
-    return ASN1_i2d_fp_of_const(EC_KEY, i2d_ECPrivateKey, fp, eckey);
-}
+IMPLEMENT_D2I_FP(EC_KEY, d2i_EC_PUBKEY_fp, d2i_EC_PUBKEY_bio)
+IMPLEMENT_I2D_FP(EC_KEY, i2d_EC_PUBKEY_fp, i2d_EC_PUBKEY_bio)
 #endif
-EC_KEY *d2i_EC_PUBKEY_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_EC_PUBKEY, bp, eckey);
-}
 
-int i2d_EC_PUBKEY_bio(BIO *bp, EC_KEY *ecdsa)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_EC_PUBKEY, bp, ecdsa);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_ECPrivateKey_bio, d2i_ECPrivateKey)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_ECPrivateKey_bio, i2d_ECPrivateKey)
 
-EC_KEY *d2i_ECPrivateKey_bio(BIO *bp, EC_KEY **eckey)
-{
-    return ASN1_d2i_bio_of(EC_KEY, EC_KEY_new, d2i_ECPrivateKey, bp, eckey);
-}
-
-int i2d_ECPrivateKey_bio(BIO *bp, EC_KEY *eckey)
-{
-    return ASN1_i2d_bio_of_const(EC_KEY, i2d_ECPrivateKey, bp, eckey);
-}
+IMPLEMENT_D2I_BIO(EC_KEY, d2i_EC_PUBKEY_bio, d2i_EC_PUBKEY)
+IMPLEMENT_I2D_BIO(EC_KEY, i2d_EC_PUBKEY_bio, i2d_EC_PUBKEY)
 
 int X509_pubkey_digest(const X509 *data, const EVP_MD *type,
                        unsigned char *md, unsigned int *len)
@@ -389,40 +341,18 @@
 }
 
 #ifndef OPENSSL_NO_FP_API
-X509_SIG *d2i_PKCS8_fp(FILE *fp, X509_SIG **p8)
-{
-    return ASN1_d2i_fp_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, fp, p8);
-}
-
-int i2d_PKCS8_fp(FILE *fp, X509_SIG *p8)
-{
-    return ASN1_i2d_fp_of(X509_SIG, i2d_X509_SIG, fp, p8);
-}
+IMPLEMENT_D2I_FP(X509_SIG, d2i_PKCS8_fp, d2i_PKCS8_bio)
+IMPLEMENT_I2D_FP(X509_SIG, i2d_PKCS8_fp, i2d_PKCS8_bio)
 #endif
 
-X509_SIG *d2i_PKCS8_bio(BIO *bp, X509_SIG **p8)
-{
-    return ASN1_d2i_bio_of(X509_SIG, X509_SIG_new, d2i_X509_SIG, bp, p8);
-}
-
-int i2d_PKCS8_bio(BIO *bp, X509_SIG *p8)
-{
-    return ASN1_i2d_bio_of(X509_SIG, i2d_X509_SIG, bp, p8);
-}
+IMPLEMENT_D2I_BIO(X509_SIG, d2i_PKCS8_bio, d2i_X509_SIG)
+IMPLEMENT_I2D_BIO(X509_SIG, i2d_PKCS8_bio, i2d_X509_SIG)
 
 #ifndef OPENSSL_NO_FP_API
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_fp(FILE *fp,
-                                                PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_fp_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                          d2i_PKCS8_PRIV_KEY_INFO, fp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_fp(FILE *fp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_fp_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, fp,
-                          p8inf);
-}
+IMPLEMENT_D2I_FP(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_fp,
+                 d2i_PKCS8_PRIV_KEY_INFO_bio)
+IMPLEMENT_I2D_FP(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_fp,
+                 i2d_PKCS8_PRIV_KEY_INFO_bio)
 
 int i2d_PKCS8PrivateKeyInfo_fp(FILE *fp, EVP_PKEY *key)
 {
@@ -436,38 +366,16 @@
     return ret;
 }
 
-int i2d_PrivateKey_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PrivateKey, fp, pkey);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PrivateKey_fp, d2i_PrivateKey_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PrivateKey_fp, i2d_PrivateKey_bio)
 
-EVP_PKEY *d2i_PrivateKey_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, fp, a);
-}
+IMPLEMENT_D2I_FP(EVP_PKEY, d2i_PUBKEY_fp, d2i_PUBKEY_bio)
+IMPLEMENT_I2D_FP(EVP_PKEY, i2d_PUBKEY_fp, i2d_PUBKEY_bio)
 
-int i2d_PUBKEY_fp(FILE *fp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_fp_of_const(EVP_PKEY, i2d_PUBKEY, fp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_fp(FILE *fp, EVP_PKEY **a)
-{
-    return ASN1_d2i_fp_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, fp, a);
-}
-
-PKCS8_PRIV_KEY_INFO *d2i_PKCS8_PRIV_KEY_INFO_bio(BIO *bp,
-                                                 PKCS8_PRIV_KEY_INFO **p8inf)
-{
-    return ASN1_d2i_bio_of(PKCS8_PRIV_KEY_INFO, PKCS8_PRIV_KEY_INFO_new,
-                           d2i_PKCS8_PRIV_KEY_INFO, bp, p8inf);
-}
-
-int i2d_PKCS8_PRIV_KEY_INFO_bio(BIO *bp, PKCS8_PRIV_KEY_INFO *p8inf)
-{
-    return ASN1_i2d_bio_of(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO, bp,
-                           p8inf);
-}
+IMPLEMENT_D2I_BIO(PKCS8_PRIV_KEY_INFO, d2i_PKCS8_PRIV_KEY_INFO_bio,
+                  d2i_PKCS8_PRIV_KEY_INFO)
+IMPLEMENT_I2D_BIO(PKCS8_PRIV_KEY_INFO, i2d_PKCS8_PRIV_KEY_INFO_bio,
+                  i2d_PKCS8_PRIV_KEY_INFO)
 
 int i2d_PKCS8PrivateKeyInfo_bio(BIO *bp, EVP_PKEY *key)
 {
@@ -482,32 +390,11 @@
 }
 #endif
 
-int i2d_PrivateKey_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PrivateKey, bp, pkey);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PrivateKey_bio, d2i_AutoPrivateKey)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PrivateKey_bio, i2d_PrivateKey)
 
-EVP_PKEY *d2i_PrivateKey_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_AutoPrivateKey, bp, a);
-}
+IMPLEMENT_D2I_BIO(EVP_PKEY, d2i_PUBKEY_bio, d2i_PUBKEY)
+IMPLEMENT_I2D_BIO(EVP_PKEY, i2d_PUBKEY_bio, i2d_PUBKEY)
 
-int i2d_PUBKEY_bio(BIO *bp, EVP_PKEY *pkey)
-{
-    return ASN1_i2d_bio_of_const(EVP_PKEY, i2d_PUBKEY, bp, pkey);
-}
-
-EVP_PKEY *d2i_PUBKEY_bio(BIO *bp, EVP_PKEY **a)
-{
-    return ASN1_d2i_bio_of(EVP_PKEY, EVP_PKEY_new, d2i_PUBKEY, bp, a);
-}
-
-DH *d2i_DHparams_bio(BIO *bp, DH **dh)
-{
-    return ASN1_d2i_bio_of(DH, DH_new, d2i_DHparams, bp, dh);
-}
-
-int i2d_DHparams_bio(BIO *bp, const DH *dh)
-{
-    return ASN1_i2d_bio_of_const(DH, i2d_DHparams, bp, dh);
-}
+IMPLEMENT_D2I_BIO(DH, d2i_DHparams_bio, d2i_DHparams)
+IMPLEMENT_I2D_BIO(const DH, i2d_DHparams_bio, i2d_DHparams)
diff --git a/src/crypto/x509v3/v3_genn.c b/src/crypto/x509v3/v3_genn.c
index 8c92687..552a524 100644
--- a/src/crypto/x509v3/v3_genn.c
+++ b/src/crypto/x509v3/v3_genn.c
@@ -100,12 +100,7 @@
 
 IMPLEMENT_ASN1_FUNCTIONS(GENERAL_NAMES)
 
-GENERAL_NAME *GENERAL_NAME_dup(GENERAL_NAME *a)
-{
-    return (GENERAL_NAME *)ASN1_dup((i2d_of_void *)i2d_GENERAL_NAME,
-                                    (d2i_of_void *)d2i_GENERAL_NAME,
-                                    (char *)a);
-}
+IMPLEMENT_ASN1_DUP_FUNCTION(GENERAL_NAME)
 
 /* Returns 0 if they are equal, != 0 otherwise. */
 int GENERAL_NAME_cmp(GENERAL_NAME *a, GENERAL_NAME *b)