Pull EVP_PKEY print hooks out of the main method table.

This allows the static linker to drop it in consumers which don't need this
stuff (i.e. all sane ones), once crypto/x509 falls off. This cuts down
on a number of dependencies from the core crypto bits on crypto/asn1 and
crypto/x509.

BUG=499653

Change-Id: I76a10a04dcc444c1ded31683df9f87725a95a4e6
Reviewed-on: https://boringssl-review.googlesource.com/5660
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/CMakeLists.txt b/crypto/evp/CMakeLists.txt
index a1ede4e..ca1b511 100644
--- a/crypto/evp/CMakeLists.txt
+++ b/crypto/evp/CMakeLists.txt
@@ -15,6 +15,7 @@
   p_rsa.c
   p_rsa_asn1.c
   pbkdf.c
+  print.c
   sign.c
 )
 
diff --git a/crypto/evp/evp.c b/crypto/evp/evp.c
index fa84212..833f914 100644
--- a/crypto/evp/evp.c
+++ b/crypto/evp/evp.c
@@ -59,7 +59,6 @@
 #include <assert.h>
 #include <string.h>
 
-#include <openssl/bio.h>
 #include <openssl/dsa.h>
 #include <openssl/ec.h>
 #include <openssl/err.h>
@@ -358,41 +357,6 @@
   return -2;
 }
 
-static int print_unsupported(BIO *out, const EVP_PKEY *pkey, int indent,
-                             const char *kstr) {
-  BIO_indent(out, indent, 128);
-  BIO_printf(out, "%s algorithm \"%s\" unsupported\n", kstr,
-             OBJ_nid2ln(pkey->type));
-  return 1;
-}
-
-int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey, int indent,
-                          ASN1_PCTX *pctx) {
-  if (pkey->ameth && pkey->ameth->pub_print) {
-    return pkey->ameth->pub_print(out, pkey, indent, pctx);
-  }
-
-  return print_unsupported(out, pkey, indent, "Public Key");
-}
-
-int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey, int indent,
-                           ASN1_PCTX *pctx) {
-  if (pkey->ameth && pkey->ameth->priv_print) {
-    return pkey->ameth->priv_print(out, pkey, indent, pctx);
-  }
-
-  return print_unsupported(out, pkey, indent, "Private Key");
-}
-
-int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey, int indent,
-                          ASN1_PCTX *pctx) {
-  if (pkey->ameth && pkey->ameth->param_print) {
-    return pkey->ameth->param_print(out, pkey, indent, pctx);
-  }
-
-  return print_unsupported(out, pkey, indent, "Parameters");
-}
-
 int EVP_PKEY_CTX_set_signature_md(EVP_PKEY_CTX *ctx, const EVP_MD *md) {
   return EVP_PKEY_CTX_ctrl(ctx, -1, EVP_PKEY_OP_TYPE_SIG, EVP_PKEY_CTRL_MD, 0,
                            (void *)md);
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index b43ee2f..1cbf28b 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -59,6 +59,8 @@
 
 #include <openssl/base.h>
 
+#include <openssl/rsa.h>
+
 #if defined(__cplusplus)
 extern "C" {
 #endif
@@ -83,7 +85,6 @@
   int (*pub_encode)(CBB *out, const EVP_PKEY *key);
 
   int (*pub_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
-  int (*pub_print)(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx);
 
   /* priv_decode decodes |params| and |key| as a PrivateKeyInfo and writes the
    * result into |out|. It returns one on success and zero on error. |params| is
@@ -95,9 +96,6 @@
    * |out|. It returns one on success and zero on error. */
   int (*priv_encode)(CBB *out, const EVP_PKEY *key);
 
-  int (*priv_print)(BIO *out, const EVP_PKEY *pkey, int indent,
-                    ASN1_PCTX *pctx);
-
   /* pkey_opaque returns 1 if the |pk| is opaque. Opaque keys are backed by
    * custom implementations which do not expose key material and parameters.*/
   int (*pkey_opaque)(const EVP_PKEY *pk);
@@ -114,8 +112,6 @@
   int (*param_missing)(const EVP_PKEY *pk);
   int (*param_copy)(EVP_PKEY *to, const EVP_PKEY *from);
   int (*param_cmp)(const EVP_PKEY *a, const EVP_PKEY *b);
-  int (*param_print)(BIO *out, const EVP_PKEY *pkey, int indent,
-                     ASN1_PCTX *pctx);
 
   void (*pkey_free)(EVP_PKEY *pkey);
 
diff --git a/crypto/evp/p_dsa_asn1.c b/crypto/evp/p_dsa_asn1.c
index bd44ac3..755d5de 100644
--- a/crypto/evp/p_dsa_asn1.c
+++ b/crypto/evp/p_dsa_asn1.c
@@ -55,14 +55,11 @@
 
 #include <openssl/evp.h>
 
-#include <openssl/asn1.h>
-#include <openssl/asn1t.h>
 #include <openssl/digest.h>
 #include <openssl/bn.h>
 #include <openssl/bytestring.h>
 #include <openssl/dsa.h>
 #include <openssl/err.h>
-#include <openssl/mem.h>
 #include <openssl/obj.h>
 
 #include "internal.h"
@@ -244,91 +241,6 @@
 
 static void int_dsa_free(EVP_PKEY *pkey) { DSA_free(pkey->pkey.dsa); }
 
-static void update_buflen(const BIGNUM *b, size_t *pbuflen) {
-  size_t i;
-
-  if (!b) {
-    return;
-  }
-  i = BN_num_bytes(b);
-  if (*pbuflen < i) {
-    *pbuflen = i;
-  }
-}
-
-static int do_dsa_print(BIO *bp, const DSA *x, int off, int ptype) {
-  uint8_t *m = NULL;
-  int ret = 0;
-  size_t buf_len = 0;
-  const char *ktype = NULL;
-
-  const BIGNUM *priv_key, *pub_key;
-
-  priv_key = NULL;
-  if (ptype == 2) {
-    priv_key = x->priv_key;
-  }
-
-  pub_key = NULL;
-  if (ptype > 0) {
-    pub_key = x->pub_key;
-  }
-
-  ktype = "DSA-Parameters";
-  if (ptype == 2) {
-    ktype = "Private-Key";
-  } else if (ptype == 1) {
-    ktype = "Public-Key";
-  }
-
-  update_buflen(x->p, &buf_len);
-  update_buflen(x->q, &buf_len);
-  update_buflen(x->g, &buf_len);
-  update_buflen(priv_key, &buf_len);
-  update_buflen(pub_key, &buf_len);
-
-  m = OPENSSL_malloc(buf_len + 10);
-  if (m == NULL) {
-    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
-  if (priv_key) {
-    if (!BIO_indent(bp, off, 128) ||
-        BIO_printf(bp, "%s: (%d bit)\n", ktype, BN_num_bits(x->p)) <= 0) {
-      goto err;
-    }
-  }
-
-  if (!ASN1_bn_print(bp, "priv:", priv_key, m, off) ||
-      !ASN1_bn_print(bp, "pub: ", pub_key, m, off) ||
-      !ASN1_bn_print(bp, "P:   ", x->p, m, off) ||
-      !ASN1_bn_print(bp, "Q:   ", x->q, m, off) ||
-      !ASN1_bn_print(bp, "G:   ", x->g, m, off)) {
-    goto err;
-  }
-  ret = 1;
-
-err:
-  OPENSSL_free(m);
-  return ret;
-}
-
-static int dsa_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                           ASN1_PCTX *ctx) {
-  return do_dsa_print(bp, pkey->pkey.dsa, indent, 0);
-}
-
-static int dsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                         ASN1_PCTX *ctx) {
-  return do_dsa_print(bp, pkey->pkey.dsa, indent, 1);
-}
-
-static int dsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                          ASN1_PCTX *ctx) {
-  return do_dsa_print(bp, pkey->pkey.dsa, indent, 2);
-}
-
 static int old_dsa_priv_decode(EVP_PKEY *pkey, const uint8_t **pder,
                                int derlen) {
   DSA *dsa;
@@ -348,11 +260,9 @@
   dsa_pub_decode,
   dsa_pub_encode,
   dsa_pub_cmp,
-  dsa_pub_print,
 
   dsa_priv_decode,
   dsa_priv_encode,
-  dsa_priv_print,
 
   NULL /* pkey_opaque */,
   NULL /* pkey_supports_digest */,
@@ -363,7 +273,6 @@
   dsa_missing_parameters,
   dsa_copy_parameters,
   dsa_cmp_parameters,
-  dsa_param_print,
 
   int_dsa_free,
   old_dsa_priv_decode,
diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c
index 82e8d46..9a84bb6 100644
--- a/crypto/evp/p_ec_asn1.c
+++ b/crypto/evp/p_ec_asn1.c
@@ -55,14 +55,12 @@
 
 #include <openssl/evp.h>
 
-#include <openssl/asn1t.h>
 #include <openssl/bn.h>
 #include <openssl/bytestring.h>
 #include <openssl/ec.h>
 #include <openssl/ec_key.h>
 #include <openssl/ecdsa.h>
 #include <openssl/err.h>
-#include <openssl/mem.h>
 #include <openssl/obj.h>
 
 #include "internal.h"
@@ -236,125 +234,6 @@
 
 static void int_ec_free(EVP_PKEY *pkey) { EC_KEY_free(pkey->pkey.ec); }
 
-static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, int ktype) {
-  uint8_t *buffer = NULL;
-  const char *ecstr;
-  size_t buf_len = 0, i;
-  int ret = 0, reason = ERR_R_BIO_LIB;
-  BN_CTX *ctx = NULL;
-  const EC_GROUP *group;
-  const EC_POINT *public_key;
-  const BIGNUM *priv_key;
-  uint8_t *pub_key_bytes = NULL;
-  size_t pub_key_bytes_len = 0;
-
-  if (x == NULL || (group = EC_KEY_get0_group(x)) == NULL) {
-    reason = ERR_R_PASSED_NULL_PARAMETER;
-    goto err;
-  }
-
-  ctx = BN_CTX_new();
-  if (ctx == NULL) {
-    reason = ERR_R_MALLOC_FAILURE;
-    goto err;
-  }
-
-  if (ktype > 0) {
-    public_key = EC_KEY_get0_public_key(x);
-    if (public_key != NULL) {
-      pub_key_bytes_len = EC_POINT_point2oct(
-          group, public_key, EC_KEY_get_conv_form(x), NULL, 0, ctx);
-      if (pub_key_bytes_len == 0) {
-        reason = ERR_R_MALLOC_FAILURE;
-        goto err;
-      }
-      pub_key_bytes = OPENSSL_malloc(pub_key_bytes_len);
-      if (pub_key_bytes == NULL) {
-        reason = ERR_R_MALLOC_FAILURE;
-        goto err;
-      }
-      pub_key_bytes_len =
-          EC_POINT_point2oct(group, public_key, EC_KEY_get_conv_form(x),
-                             pub_key_bytes, pub_key_bytes_len, ctx);
-      if (pub_key_bytes_len == 0) {
-        reason = ERR_R_MALLOC_FAILURE;
-        goto err;
-      }
-      buf_len = pub_key_bytes_len;
-    }
-  }
-
-  if (ktype == 2) {
-    priv_key = EC_KEY_get0_private_key(x);
-    if (priv_key && (i = (size_t)BN_num_bytes(priv_key)) > buf_len) {
-      buf_len = i;
-    }
-  } else {
-    priv_key = NULL;
-  }
-
-  if (ktype > 0) {
-    buf_len += 10;
-    if ((buffer = OPENSSL_malloc(buf_len)) == NULL) {
-      reason = ERR_R_MALLOC_FAILURE;
-      goto err;
-    }
-  }
-  if (ktype == 2) {
-    ecstr = "Private-Key";
-  } else if (ktype == 1) {
-    ecstr = "Public-Key";
-  } else {
-    ecstr = "ECDSA-Parameters";
-  }
-
-  if (!BIO_indent(bp, off, 128)) {
-    goto err;
-  }
-  const BIGNUM *order = EC_GROUP_get0_order(group);
-  if (BIO_printf(bp, "%s: (%d bit)\n", ecstr, BN_num_bits(order)) <= 0) {
-    goto err;
-  }
-
-  if ((priv_key != NULL) &&
-      !ASN1_bn_print(bp, "priv:", priv_key, buffer, off)) {
-    goto err;
-  }
-  if (pub_key_bytes != NULL) {
-    BIO_hexdump(bp, pub_key_bytes, pub_key_bytes_len, off);
-  }
-  /* TODO(fork): implement */
-  /*
-  if (!ECPKParameters_print(bp, group, off))
-    goto err; */
-  ret = 1;
-
-err:
-  if (!ret) {
-    OPENSSL_PUT_ERROR(EVP, reason);
-  }
-  OPENSSL_free(pub_key_bytes);
-  BN_CTX_free(ctx);
-  OPENSSL_free(buffer);
-  return ret;
-}
-
-static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                             ASN1_PCTX *ctx) {
-  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 0);
-}
-
-static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                           ASN1_PCTX *ctx) {
-  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 1);
-}
-
-
-static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                            ASN1_PCTX *ctx) {
-  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 2);
-}
-
 static int eckey_opaque(const EVP_PKEY *pkey) {
   return EC_KEY_is_opaque(pkey->pkey.ec);
 }
@@ -377,11 +256,9 @@
   eckey_pub_decode,
   eckey_pub_encode,
   eckey_pub_cmp,
-  eckey_pub_print,
 
   eckey_priv_decode,
   eckey_priv_encode,
-  eckey_priv_print,
 
   eckey_opaque,
   0 /* pkey_supports_digest */,
@@ -392,7 +269,6 @@
   ec_missing_parameters,
   ec_copy_parameters,
   ec_cmp_parameters,
-  eckey_param_print,
 
   int_ec_free,
   old_ec_priv_decode,
diff --git a/crypto/evp/p_rsa_asn1.c b/crypto/evp/p_rsa_asn1.c
index a41f00d..480ef22 100644
--- a/crypto/evp/p_rsa_asn1.c
+++ b/crypto/evp/p_rsa_asn1.c
@@ -55,7 +55,7 @@
 
 #include <openssl/evp.h>
 
-#include <openssl/asn1.h>
+#include <openssl/bn.h>
 #include <openssl/bytestring.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
@@ -175,133 +175,6 @@
 
 static void int_rsa_free(EVP_PKEY *pkey) { RSA_free(pkey->pkey.rsa); }
 
-static void update_buflen(const BIGNUM *b, size_t *pbuflen) {
-  size_t i;
-
-  if (!b) {
-    return;
-  }
-
-  i = BN_num_bytes(b);
-  if (*pbuflen < i) {
-    *pbuflen = i;
-  }
-}
-
-static int do_rsa_print(BIO *out, const RSA *rsa, int off,
-                        int include_private) {
-  char *str;
-  const char *s;
-  uint8_t *m = NULL;
-  int ret = 0, mod_len = 0;
-  size_t buf_len = 0;
-
-  update_buflen(rsa->n, &buf_len);
-  update_buflen(rsa->e, &buf_len);
-
-  if (include_private) {
-    update_buflen(rsa->d, &buf_len);
-    update_buflen(rsa->p, &buf_len);
-    update_buflen(rsa->q, &buf_len);
-    update_buflen(rsa->dmp1, &buf_len);
-    update_buflen(rsa->dmq1, &buf_len);
-    update_buflen(rsa->iqmp, &buf_len);
-
-    if (rsa->additional_primes != NULL) {
-      size_t i;
-
-      for (i = 0; i < sk_RSA_additional_prime_num(rsa->additional_primes);
-           i++) {
-        const RSA_additional_prime *ap =
-            sk_RSA_additional_prime_value(rsa->additional_primes, i);
-        update_buflen(ap->prime, &buf_len);
-        update_buflen(ap->exp, &buf_len);
-        update_buflen(ap->coeff, &buf_len);
-      }
-    }
-  }
-
-  m = OPENSSL_malloc(buf_len + 10);
-  if (m == NULL) {
-    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
-  if (rsa->n != NULL) {
-    mod_len = BN_num_bits(rsa->n);
-  }
-
-  if (!BIO_indent(out, off, 128)) {
-    goto err;
-  }
-
-  if (include_private && rsa->d) {
-    if (BIO_printf(out, "Private-Key: (%d bit)\n", mod_len) <= 0) {
-      goto err;
-    }
-    str = "modulus:";
-    s = "publicExponent:";
-  } else {
-    if (BIO_printf(out, "Public-Key: (%d bit)\n", mod_len) <= 0) {
-      goto err;
-    }
-    str = "Modulus:";
-    s = "Exponent:";
-  }
-  if (!ASN1_bn_print(out, str, rsa->n, m, off) ||
-      !ASN1_bn_print(out, s, rsa->e, m, off)) {
-    goto err;
-  }
-
-  if (include_private) {
-    if (!ASN1_bn_print(out, "privateExponent:", rsa->d, m, off) ||
-        !ASN1_bn_print(out, "prime1:", rsa->p, m, off) ||
-        !ASN1_bn_print(out, "prime2:", rsa->q, m, off) ||
-        !ASN1_bn_print(out, "exponent1:", rsa->dmp1, m, off) ||
-        !ASN1_bn_print(out, "exponent2:", rsa->dmq1, m, off) ||
-        !ASN1_bn_print(out, "coefficient:", rsa->iqmp, m, off)) {
-      goto err;
-    }
-
-    if (rsa->additional_primes != NULL &&
-        sk_RSA_additional_prime_num(rsa->additional_primes) > 0) {
-      size_t i;
-
-      if (BIO_printf(out, "otherPrimeInfos:\n") <= 0) {
-        goto err;
-      }
-      for (i = 0; i < sk_RSA_additional_prime_num(rsa->additional_primes);
-           i++) {
-        const RSA_additional_prime *ap =
-            sk_RSA_additional_prime_value(rsa->additional_primes, i);
-
-        if (BIO_printf(out, "otherPrimeInfo (prime %u):\n",
-                       (unsigned)(i + 3)) <= 0 ||
-            !ASN1_bn_print(out, "prime:", ap->prime, m, off) ||
-            !ASN1_bn_print(out, "exponent:", ap->exp, m, off) ||
-            !ASN1_bn_print(out, "coeff:", ap->coeff, m, off)) {
-          goto err;
-        }
-      }
-    }
-  }
-  ret = 1;
-
-err:
-  OPENSSL_free(m);
-  return ret;
-}
-
-static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                         ASN1_PCTX *ctx) {
-  return do_rsa_print(bp, pkey->pkey.rsa, indent, 0);
-}
-
-static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
-                          ASN1_PCTX *ctx) {
-  return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
-}
-
 static int old_rsa_priv_decode(EVP_PKEY *pkey, const uint8_t **pder,
                                int derlen) {
   RSA *rsa = d2i_RSAPrivateKey(NULL, pder, derlen);
@@ -320,11 +193,9 @@
   rsa_pub_decode,
   rsa_pub_encode,
   rsa_pub_cmp,
-  rsa_pub_print,
 
   rsa_priv_decode,
   rsa_priv_encode,
-  rsa_priv_print,
 
   rsa_opaque,
   rsa_supports_digest,
@@ -332,7 +203,7 @@
   int_rsa_size,
   rsa_bits,
 
-  0,0,0,0,
+  0,0,0,
 
   int_rsa_free,
 
diff --git a/crypto/evp/print.c b/crypto/evp/print.c
new file mode 100644
index 0000000..c650adb
--- /dev/null
+++ b/crypto/evp/print.c
@@ -0,0 +1,474 @@
+/* ====================================================================
+ * Copyright (c) 2006 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com). */
+
+#include <openssl/evp.h>
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/mem.h>
+#include <openssl/rsa.h>
+
+#include "../rsa/internal.h"
+
+
+static void update_buflen(const BIGNUM *b, size_t *pbuflen) {
+  size_t i;
+
+  if (!b) {
+    return;
+  }
+
+  i = BN_num_bytes(b);
+  if (*pbuflen < i) {
+    *pbuflen = i;
+  }
+}
+
+/* RSA keys. */
+
+static int do_rsa_print(BIO *out, const RSA *rsa, int off,
+                        int include_private) {
+  char *str;
+  const char *s;
+  uint8_t *m = NULL;
+  int ret = 0, mod_len = 0;
+  size_t buf_len = 0;
+
+  update_buflen(rsa->n, &buf_len);
+  update_buflen(rsa->e, &buf_len);
+
+  if (include_private) {
+    update_buflen(rsa->d, &buf_len);
+    update_buflen(rsa->p, &buf_len);
+    update_buflen(rsa->q, &buf_len);
+    update_buflen(rsa->dmp1, &buf_len);
+    update_buflen(rsa->dmq1, &buf_len);
+    update_buflen(rsa->iqmp, &buf_len);
+
+    if (rsa->additional_primes != NULL) {
+      size_t i;
+
+      for (i = 0; i < sk_RSA_additional_prime_num(rsa->additional_primes);
+           i++) {
+        const RSA_additional_prime *ap =
+            sk_RSA_additional_prime_value(rsa->additional_primes, i);
+        update_buflen(ap->prime, &buf_len);
+        update_buflen(ap->exp, &buf_len);
+        update_buflen(ap->coeff, &buf_len);
+      }
+    }
+  }
+
+  m = (uint8_t *)OPENSSL_malloc(buf_len + 10);
+  if (m == NULL) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (rsa->n != NULL) {
+    mod_len = BN_num_bits(rsa->n);
+  }
+
+  if (!BIO_indent(out, off, 128)) {
+    goto err;
+  }
+
+  if (include_private && rsa->d) {
+    if (BIO_printf(out, "Private-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "modulus:";
+    s = "publicExponent:";
+  } else {
+    if (BIO_printf(out, "Public-Key: (%d bit)\n", mod_len) <= 0) {
+      goto err;
+    }
+    str = "Modulus:";
+    s = "Exponent:";
+  }
+  if (!ASN1_bn_print(out, str, rsa->n, m, off) ||
+      !ASN1_bn_print(out, s, rsa->e, m, off)) {
+    goto err;
+  }
+
+  if (include_private) {
+    if (!ASN1_bn_print(out, "privateExponent:", rsa->d, m, off) ||
+        !ASN1_bn_print(out, "prime1:", rsa->p, m, off) ||
+        !ASN1_bn_print(out, "prime2:", rsa->q, m, off) ||
+        !ASN1_bn_print(out, "exponent1:", rsa->dmp1, m, off) ||
+        !ASN1_bn_print(out, "exponent2:", rsa->dmq1, m, off) ||
+        !ASN1_bn_print(out, "coefficient:", rsa->iqmp, m, off)) {
+      goto err;
+    }
+
+    if (rsa->additional_primes != NULL &&
+        sk_RSA_additional_prime_num(rsa->additional_primes) > 0) {
+      size_t i;
+
+      if (BIO_printf(out, "otherPrimeInfos:\n") <= 0) {
+        goto err;
+      }
+      for (i = 0; i < sk_RSA_additional_prime_num(rsa->additional_primes);
+           i++) {
+        const RSA_additional_prime *ap =
+            sk_RSA_additional_prime_value(rsa->additional_primes, i);
+
+        if (BIO_printf(out, "otherPrimeInfo (prime %u):\n",
+                       (unsigned)(i + 3)) <= 0 ||
+            !ASN1_bn_print(out, "prime:", ap->prime, m, off) ||
+            !ASN1_bn_print(out, "exponent:", ap->exp, m, off) ||
+            !ASN1_bn_print(out, "coeff:", ap->coeff, m, off)) {
+          goto err;
+        }
+      }
+    }
+  }
+  ret = 1;
+
+err:
+  OPENSSL_free(m);
+  return ret;
+}
+
+static int rsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 0);
+}
+
+static int rsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx) {
+  return do_rsa_print(bp, pkey->pkey.rsa, indent, 1);
+}
+
+
+/* DSA keys. */
+
+static int do_dsa_print(BIO *bp, const DSA *x, int off, int ptype) {
+  uint8_t *m = NULL;
+  int ret = 0;
+  size_t buf_len = 0;
+  const char *ktype = NULL;
+
+  const BIGNUM *priv_key, *pub_key;
+
+  priv_key = NULL;
+  if (ptype == 2) {
+    priv_key = x->priv_key;
+  }
+
+  pub_key = NULL;
+  if (ptype > 0) {
+    pub_key = x->pub_key;
+  }
+
+  ktype = "DSA-Parameters";
+  if (ptype == 2) {
+    ktype = "Private-Key";
+  } else if (ptype == 1) {
+    ktype = "Public-Key";
+  }
+
+  update_buflen(x->p, &buf_len);
+  update_buflen(x->q, &buf_len);
+  update_buflen(x->g, &buf_len);
+  update_buflen(priv_key, &buf_len);
+  update_buflen(pub_key, &buf_len);
+
+  m = (uint8_t *)OPENSSL_malloc(buf_len + 10);
+  if (m == NULL) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
+    goto err;
+  }
+
+  if (priv_key) {
+    if (!BIO_indent(bp, off, 128) ||
+        BIO_printf(bp, "%s: (%d bit)\n", ktype, BN_num_bits(x->p)) <= 0) {
+      goto err;
+    }
+  }
+
+  if (!ASN1_bn_print(bp, "priv:", priv_key, m, off) ||
+      !ASN1_bn_print(bp, "pub: ", pub_key, m, off) ||
+      !ASN1_bn_print(bp, "P:   ", x->p, m, off) ||
+      !ASN1_bn_print(bp, "Q:   ", x->q, m, off) ||
+      !ASN1_bn_print(bp, "G:   ", x->g, m, off)) {
+    goto err;
+  }
+  ret = 1;
+
+err:
+  OPENSSL_free(m);
+  return ret;
+}
+
+static int dsa_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx) {
+  return do_dsa_print(bp, pkey->pkey.dsa, indent, 0);
+}
+
+static int dsa_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                         ASN1_PCTX *ctx) {
+  return do_dsa_print(bp, pkey->pkey.dsa, indent, 1);
+}
+
+static int dsa_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *ctx) {
+  return do_dsa_print(bp, pkey->pkey.dsa, indent, 2);
+}
+
+
+/* EC keys. */
+
+static int do_EC_KEY_print(BIO *bp, const EC_KEY *x, int off, int ktype) {
+  uint8_t *buffer = NULL;
+  const char *ecstr;
+  size_t buf_len = 0, i;
+  int ret = 0, reason = ERR_R_BIO_LIB;
+  BIGNUM *order = NULL;
+  BN_CTX *ctx = NULL;
+  const EC_GROUP *group;
+  const EC_POINT *public_key;
+  const BIGNUM *priv_key;
+  uint8_t *pub_key_bytes = NULL;
+  size_t pub_key_bytes_len = 0;
+
+  if (x == NULL || (group = EC_KEY_get0_group(x)) == NULL) {
+    reason = ERR_R_PASSED_NULL_PARAMETER;
+    goto err;
+  }
+
+  ctx = BN_CTX_new();
+  if (ctx == NULL) {
+    reason = ERR_R_MALLOC_FAILURE;
+    goto err;
+  }
+
+  if (ktype > 0) {
+    public_key = EC_KEY_get0_public_key(x);
+    if (public_key != NULL) {
+      pub_key_bytes_len = EC_POINT_point2oct(
+          group, public_key, EC_KEY_get_conv_form(x), NULL, 0, ctx);
+      if (pub_key_bytes_len == 0) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      pub_key_bytes = OPENSSL_malloc(pub_key_bytes_len);
+      if (pub_key_bytes == NULL) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      pub_key_bytes_len =
+          EC_POINT_point2oct(group, public_key, EC_KEY_get_conv_form(x),
+                             pub_key_bytes, pub_key_bytes_len, ctx);
+      if (pub_key_bytes_len == 0) {
+        reason = ERR_R_MALLOC_FAILURE;
+        goto err;
+      }
+      buf_len = pub_key_bytes_len;
+    }
+  }
+
+  if (ktype == 2) {
+    priv_key = EC_KEY_get0_private_key(x);
+    if (priv_key && (i = (size_t)BN_num_bytes(priv_key)) > buf_len) {
+      buf_len = i;
+    }
+  } else {
+    priv_key = NULL;
+  }
+
+  if (ktype > 0) {
+    buf_len += 10;
+    if ((buffer = OPENSSL_malloc(buf_len)) == NULL) {
+      reason = ERR_R_MALLOC_FAILURE;
+      goto err;
+    }
+  }
+  if (ktype == 2) {
+    ecstr = "Private-Key";
+  } else if (ktype == 1) {
+    ecstr = "Public-Key";
+  } else {
+    ecstr = "ECDSA-Parameters";
+  }
+
+  if (!BIO_indent(bp, off, 128)) {
+    goto err;
+  }
+  order = BN_new();
+  if (order == NULL || !EC_GROUP_get_order(group, order, NULL) ||
+      BIO_printf(bp, "%s: (%d bit)\n", ecstr, BN_num_bits(order)) <= 0) {
+    goto err;
+  }
+
+  if ((priv_key != NULL) &&
+      !ASN1_bn_print(bp, "priv:", priv_key, buffer, off)) {
+    goto err;
+  }
+  if (pub_key_bytes != NULL) {
+    BIO_hexdump(bp, pub_key_bytes, pub_key_bytes_len, off);
+  }
+  /* TODO(fork): implement */
+  /*
+  if (!ECPKParameters_print(bp, group, off))
+    goto err; */
+  ret = 1;
+
+err:
+  if (!ret) {
+    OPENSSL_PUT_ERROR(EVP, reason);
+  }
+  OPENSSL_free(pub_key_bytes);
+  BN_free(order);
+  BN_CTX_free(ctx);
+  OPENSSL_free(buffer);
+  return ret;
+}
+
+static int eckey_param_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                             ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 0);
+}
+
+static int eckey_pub_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 1);
+}
+
+
+static int eckey_priv_print(BIO *bp, const EVP_PKEY *pkey, int indent,
+                            ASN1_PCTX *ctx) {
+  return do_EC_KEY_print(bp, pkey->pkey.ec, indent, 2);
+}
+
+
+typedef struct {
+  int type;
+  int (*pub_print)(BIO *out, const EVP_PKEY *pkey, int indent, ASN1_PCTX *pctx);
+  int (*priv_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                    ASN1_PCTX *pctx);
+  int (*param_print)(BIO *out, const EVP_PKEY *pkey, int indent,
+                     ASN1_PCTX *pctx);
+} EVP_PKEY_PRINT_METHOD;
+
+static EVP_PKEY_PRINT_METHOD kPrintMethods[] = {
+    {
+        EVP_PKEY_RSA,
+        rsa_pub_print,
+        rsa_priv_print,
+        NULL /* param_print */,
+    },
+    {
+        EVP_PKEY_DSA,
+        dsa_pub_print,
+        dsa_priv_print,
+        dsa_param_print,
+    },
+    {
+        EVP_PKEY_EC,
+        eckey_pub_print,
+        eckey_priv_print,
+        eckey_param_print,
+    },
+};
+
+static size_t kPrintMethodsLen =
+    sizeof(kPrintMethods) / sizeof(kPrintMethods[0]);
+
+static EVP_PKEY_PRINT_METHOD *find_method(int type) {
+  size_t i;
+  for (i = 0; i < kPrintMethodsLen; i++) {
+    if (kPrintMethods[i].type == type) {
+      return &kPrintMethods[i];
+    }
+  }
+  return NULL;
+}
+
+static int print_unsupported(BIO *out, const EVP_PKEY *pkey, int indent,
+                             const char *kstr) {
+  BIO_indent(out, indent, 128);
+  BIO_printf(out, "%s algorithm \"%s\" unsupported\n", kstr,
+             OBJ_nid2ln(pkey->type));
+  return 1;
+}
+
+int EVP_PKEY_print_public(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  EVP_PKEY_PRINT_METHOD *method = find_method(pkey->type);
+  if (method != NULL && method->pub_print != NULL) {
+    return method->pub_print(out, pkey, indent, pctx);
+  }
+  return print_unsupported(out, pkey, indent, "Public Key");
+}
+
+int EVP_PKEY_print_private(BIO *out, const EVP_PKEY *pkey, int indent,
+                           ASN1_PCTX *pctx) {
+  EVP_PKEY_PRINT_METHOD *method = find_method(pkey->type);
+  if (method != NULL && method->priv_print != NULL) {
+    return method->priv_print(out, pkey, indent, pctx);
+  }
+  return print_unsupported(out, pkey, indent, "Private Key");
+}
+
+int EVP_PKEY_print_params(BIO *out, const EVP_PKEY *pkey, int indent,
+                          ASN1_PCTX *pctx) {
+  EVP_PKEY_PRINT_METHOD *method = find_method(pkey->type);
+  if (method != NULL && method->param_print != NULL) {
+    return method->param_print(out, pkey, indent, pctx);
+  }
+  return print_unsupported(out, pkey, indent, "Parameters");
+}