Implement i2d_PUBKEY and friends without crypto/asn1.

Code which targets OpenSSL won't use EVP_parse_public_key. X509_PUBKEY
is fairly deeply tied to the old ASN.1 stack, but there's no reason for
i2d_PUBKEY and friends to be. Move them to crypto/evp and reimplement as
wrappers over our functions.

Bug: chromium:1102458
Change-Id: Ic11766acdac797602e4abe1253b0efe33faef298
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42005
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/evp_asn1.c b/crypto/evp/evp_asn1.c
index fc1dce3..4d13d59 100644
--- a/crypto/evp/evp_asn1.c
+++ b/crypto/evp/evp_asn1.c
@@ -65,6 +65,7 @@
 #include <openssl/rsa.h>
 
 #include "internal.h"
+#include "../bytestring/internal.h"
 #include "../internal.h"
 
 
@@ -386,3 +387,145 @@
   EVP_PKEY_free(ret);
   return NULL;
 }
+
+EVP_PKEY *d2i_PUBKEY(EVP_PKEY **out, const uint8_t **inp, long len) {
+  if (len < 0) {
+    return NULL;
+  }
+  CBS cbs;
+  CBS_init(&cbs, *inp, (size_t)len);
+  EVP_PKEY *ret = EVP_parse_public_key(&cbs);
+  if (ret == NULL) {
+    return NULL;
+  }
+  if (out != NULL) {
+    EVP_PKEY_free(*out);
+    *out = ret;
+  }
+  *inp = CBS_data(&cbs);
+  return ret;
+}
+
+int i2d_PUBKEY(const EVP_PKEY *pkey, uint8_t **outp) {
+  CBB cbb;
+  if (!CBB_init(&cbb, 128) ||
+      !EVP_marshal_public_key(&cbb, pkey)) {
+    CBB_cleanup(&cbb);
+    return -1;
+  }
+  return CBB_finish_i2d(&cbb, outp);
+}
+
+RSA *d2i_RSA_PUBKEY(RSA **out, const uint8_t **inp, long len) {
+  if (len < 0) {
+    return NULL;
+  }
+  CBS cbs;
+  CBS_init(&cbs, *inp, (size_t)len);
+  EVP_PKEY *pkey = EVP_parse_public_key(&cbs);
+  if (pkey == NULL) {
+    return NULL;
+  }
+  RSA *rsa = EVP_PKEY_get1_RSA(pkey);
+  EVP_PKEY_free(pkey);
+  if (rsa == NULL) {
+    return NULL;
+  }
+  if (out != NULL) {
+    RSA_free(*out);
+    *out = rsa;
+  }
+  *inp = CBS_data(&cbs);
+  return rsa;
+}
+
+int i2d_RSA_PUBKEY(const RSA *rsa, uint8_t **outp) {
+  int ret = -1;
+  EVP_PKEY *pkey = EVP_PKEY_new();
+  if (pkey == NULL ||
+      !EVP_PKEY_set1_RSA(pkey, (RSA *)rsa)) {
+    goto err;
+  }
+
+  ret = i2d_PUBKEY(pkey, outp);
+
+err:
+  EVP_PKEY_free(pkey);
+  return ret;
+}
+
+DSA *d2i_DSA_PUBKEY(DSA **out, const uint8_t **inp, long len) {
+  if (len < 0) {
+    return NULL;
+  }
+  CBS cbs;
+  CBS_init(&cbs, *inp, (size_t)len);
+  EVP_PKEY *pkey = EVP_parse_public_key(&cbs);
+  if (pkey == NULL) {
+    return NULL;
+  }
+  DSA *dsa = EVP_PKEY_get1_DSA(pkey);
+  EVP_PKEY_free(pkey);
+  if (dsa == NULL) {
+    return NULL;
+  }
+  if (out != NULL) {
+    DSA_free(*out);
+    *out = dsa;
+  }
+  *inp = CBS_data(&cbs);
+  return dsa;
+}
+
+int i2d_DSA_PUBKEY(const DSA *dsa, uint8_t **outp) {
+  int ret = -1;
+  EVP_PKEY *pkey = EVP_PKEY_new();
+  if (pkey == NULL ||
+      !EVP_PKEY_set1_DSA(pkey, (DSA *)dsa)) {
+    goto err;
+  }
+
+  ret = i2d_PUBKEY(pkey, outp);
+
+err:
+  EVP_PKEY_free(pkey);
+  return ret;
+}
+
+EC_KEY *d2i_EC_PUBKEY(EC_KEY **out, const uint8_t **inp, long len) {
+  if (len < 0) {
+    return NULL;
+  }
+  CBS cbs;
+  CBS_init(&cbs, *inp, (size_t)len);
+  EVP_PKEY *pkey = EVP_parse_public_key(&cbs);
+  if (pkey == NULL) {
+    return NULL;
+  }
+  EC_KEY *ec_key = EVP_PKEY_get1_EC_KEY(pkey);
+  EVP_PKEY_free(pkey);
+  if (ec_key == NULL) {
+    return NULL;
+  }
+  if (out != NULL) {
+    EC_KEY_free(*out);
+    *out = ec_key;
+  }
+  *inp = CBS_data(&cbs);
+  return ec_key;
+}
+
+int i2d_EC_PUBKEY(const EC_KEY *ec_key, uint8_t **outp) {
+  int ret = -1;
+  EVP_PKEY *pkey = EVP_PKEY_new();
+  if (pkey == NULL ||
+      !EVP_PKEY_set1_EC_KEY(pkey, (EC_KEY *)ec_key)) {
+    goto err;
+  }
+
+  ret = i2d_PUBKEY(pkey, outp);
+
+err:
+  EVP_PKEY_free(pkey);
+  return ret;
+}
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index 3d07d66..37dee49 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -180,160 +180,6 @@
     return NULL;
 }
 
-/*
- * Now two pseudo ASN1 routines that take an EVP_PKEY structure and encode or
- * decode as X509_PUBKEY
- */
-
-EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp, long length)
-{
-    X509_PUBKEY *xpk;
-    EVP_PKEY *pktmp;
-    xpk = d2i_X509_PUBKEY(NULL, pp, length);
-    if (!xpk)
-        return NULL;
-    pktmp = X509_PUBKEY_get(xpk);
-    X509_PUBKEY_free(xpk);
-    if (!pktmp)
-        return NULL;
-    if (a) {
-        EVP_PKEY_free(*a);
-        *a = pktmp;
-    }
-    return pktmp;
-}
-
-int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp)
-{
-    X509_PUBKEY *xpk = NULL;
-    int ret;
-    if (!a)
-        return 0;
-    if (!X509_PUBKEY_set(&xpk, (EVP_PKEY *)a))
-        return 0;
-    ret = i2d_X509_PUBKEY(xpk, pp);
-    X509_PUBKEY_free(xpk);
-    return ret;
-}
-
-/*
- * The following are equivalents but which return RSA and DSA keys
- */
-RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp, long length)
-{
-    EVP_PKEY *pkey;
-    RSA *key;
-    const unsigned char *q;
-    q = *pp;
-    pkey = d2i_PUBKEY(NULL, &q, length);
-    if (!pkey)
-        return NULL;
-    key = EVP_PKEY_get1_RSA(pkey);
-    EVP_PKEY_free(pkey);
-    if (!key)
-        return NULL;
-    *pp = q;
-    if (a) {
-        RSA_free(*a);
-        *a = key;
-    }
-    return key;
-}
-
-int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp)
-{
-    EVP_PKEY *pktmp;
-    int ret;
-    if (!a)
-        return 0;
-    pktmp = EVP_PKEY_new();
-    if (!pktmp) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-    EVP_PKEY_set1_RSA(pktmp, (RSA *)a);
-    ret = i2d_PUBKEY(pktmp, pp);
-    EVP_PKEY_free(pktmp);
-    return ret;
-}
-
-#ifndef OPENSSL_NO_DSA
-DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp, long length)
-{
-    EVP_PKEY *pkey;
-    DSA *key;
-    const unsigned char *q;
-    q = *pp;
-    pkey = d2i_PUBKEY(NULL, &q, length);
-    if (!pkey)
-        return NULL;
-    key = EVP_PKEY_get1_DSA(pkey);
-    EVP_PKEY_free(pkey);
-    if (!key)
-        return NULL;
-    *pp = q;
-    if (a) {
-        DSA_free(*a);
-        *a = key;
-    }
-    return key;
-}
-
-int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp)
-{
-    EVP_PKEY *pktmp;
-    int ret;
-    if (!a)
-        return 0;
-    pktmp = EVP_PKEY_new();
-    if (!pktmp) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        return 0;
-    }
-    EVP_PKEY_set1_DSA(pktmp, (DSA *)a);
-    ret = i2d_PUBKEY(pktmp, pp);
-    EVP_PKEY_free(pktmp);
-    return ret;
-}
-#endif
-
-EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp, long length)
-{
-    EVP_PKEY *pkey;
-    EC_KEY *key;
-    const unsigned char *q;
-    q = *pp;
-    pkey = d2i_PUBKEY(NULL, &q, length);
-    if (!pkey)
-        return (NULL);
-    key = EVP_PKEY_get1_EC_KEY(pkey);
-    EVP_PKEY_free(pkey);
-    if (!key)
-        return (NULL);
-    *pp = q;
-    if (a) {
-        EC_KEY_free(*a);
-        *a = key;
-    }
-    return (key);
-}
-
-int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp)
-{
-    EVP_PKEY *pktmp;
-    int ret;
-    if (!a)
-        return (0);
-    if ((pktmp = EVP_PKEY_new()) == NULL) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-    EVP_PKEY_set1_EC_KEY(pktmp, (EC_KEY *)a);
-    ret = i2d_PUBKEY(pktmp, pp);
-    EVP_PKEY_free(pktmp);
-    return (ret);
-}
-
 int X509_PUBKEY_set0_param(X509_PUBKEY *pub, const ASN1_OBJECT *aobj,
                            int ptype, void *pval,
                            unsigned char *penc, int penclen)
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index fe6c8b6..b305225 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -947,6 +947,75 @@
 OPENSSL_EXPORT int EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md(EVP_PKEY_CTX *ctx,
                                                            const EVP_MD *md);
 
+// i2d_PUBKEY marshals a public key from |pkey| as a DER-encoded
+// SubjectPublicKeyInfo. If |outp| is not NULL, the result is written to |*outp|
+// and |*outp| is advanced just past the output. It returns the number of bytes
+// in the result, whether written or not, or a negative value on error.
+//
+// Use |EVP_marshal_public_key| instead.
+OPENSSL_EXPORT int i2d_PUBKEY(const EVP_PKEY *pkey, uint8_t **outp);
+
+// d2i_PUBKEY parses a DER-encoded SubjectPublicKeyInfo from |len| bytes at
+// |*inp|. It returns a newly-allocated result, or NULL on error. On success,
+// |*inp| is advanced past the DER structure. If |out| is not NULL, it also
+// frees any existing object pointed by |*out| and writes the result.
+//
+// Use |EVP_parse_public_key| instead.
+OPENSSL_EXPORT EVP_PKEY *d2i_PUBKEY(EVP_PKEY **out, const uint8_t **inp,
+                                    long len);
+
+// i2d_RSA_PUBKEY marshals |rsa| as a DER-encoded SubjectPublicKeyInfo. If
+// |outp| is not NULL, the result is written to |*outp| and
+// |*outp| is advanced just past the output. It returns the number of bytes in
+// the result, whether written or not, or a negative value on error.
+//
+// Use |EVP_marshal_public_key| instead.
+OPENSSL_EXPORT int i2d_RSA_PUBKEY(const RSA *rsa, uint8_t **outp);
+
+// d2i_RSA_PUBKEY parses an RSA public key as a DER-encoded SubjectPublicKeyInfo
+// from |len| bytes at |*inp|. It returns a newly-allocated result, or NULL on
+// error. On success, |*inp| is advanced past the DER structure. If |out| is not
+// NULL, it also frees any existing object pointed by |*out| and writes the
+// result.
+//
+// Use |EVP_parse_public_key| instead.
+OPENSSL_EXPORT RSA *d2i_RSA_PUBKEY(RSA **out, const uint8_t **inp, long len);
+
+// i2d_DSA_PUBKEY marshals |dsa| as a DER-encoded SubjectPublicKeyInfo. If
+// |outp| is not NULL, the result is written to |*outp| and |*outp| is advanced
+// just past the output. It returns the number of bytes in the result, whether
+// written or not, or a negative value on error.
+//
+// Use |EVP_marshal_public_key| instead.
+OPENSSL_EXPORT int i2d_DSA_PUBKEY(const DSA *dsa, uint8_t **outp);
+
+// d2i_DSA_PUBKEY parses a DSA public key as a DER-encoded SubjectPublicKeyInfo
+// from |len| bytes at |*inp|. It returns a newly-allocated result, or NULL on
+// error. On success, |*inp| is advanced past the DER structure. If |out| is not
+// NULL, it also frees any existing object pointed by |*out| and writes the
+// result.
+//
+// Use |EVP_parse_public_key| instead.
+OPENSSL_EXPORT DSA *d2i_DSA_PUBKEY(DSA **out, const uint8_t **inp, long len);
+
+// i2d_EC_PUBKEY marshals |ec_key| as a DER-encoded SubjectPublicKeyInfo. If
+// |outp| is not NULL, the result is written to |*outp| and |*outp| is advanced
+// just past the output. It returns the number of bytes in the result, whether
+// written or not, or a negative value on error.
+//
+// Use |EVP_marshal_public_key| instead.
+OPENSSL_EXPORT int i2d_EC_PUBKEY(const EC_KEY *ec_key, uint8_t **outp);
+
+// d2i_EC_PUBKEY parses an EC public key as a DER-encoded SubjectPublicKeyInfo
+// from |len| bytes at |*inp|. It returns a newly-allocated result, or NULL on
+// error. On success, |*inp| is advanced past the DER structure. If |out| is not
+// NULL, it also frees any existing object pointed by |*out| and writes the
+// result.
+//
+// Use |EVP_parse_public_key| instead.
+OPENSSL_EXPORT EC_KEY *d2i_EC_PUBKEY(EC_KEY **out, const uint8_t **inp,
+                                     long len);
+
 
 // Preprocessor compatibility section (hidden).
 //
diff --git a/include/openssl/x509.h b/include/openssl/x509.h
index d58e3ca..58dfad3 100644
--- a/include/openssl/x509.h
+++ b/include/openssl/x509.h
@@ -758,20 +758,6 @@
 
 OPENSSL_EXPORT int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
 OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key);
-OPENSSL_EXPORT int i2d_PUBKEY(const EVP_PKEY *a, unsigned char **pp);
-OPENSSL_EXPORT EVP_PKEY *d2i_PUBKEY(EVP_PKEY **a, const unsigned char **pp,
-                                    long length);
-OPENSSL_EXPORT int i2d_RSA_PUBKEY(const RSA *a, unsigned char **pp);
-OPENSSL_EXPORT RSA *d2i_RSA_PUBKEY(RSA **a, const unsigned char **pp,
-                                   long length);
-#ifndef OPENSSL_NO_DSA
-OPENSSL_EXPORT int i2d_DSA_PUBKEY(const DSA *a, unsigned char **pp);
-OPENSSL_EXPORT DSA *d2i_DSA_PUBKEY(DSA **a, const unsigned char **pp,
-                                   long length);
-#endif
-OPENSSL_EXPORT int i2d_EC_PUBKEY(const EC_KEY *a, unsigned char **pp);
-OPENSSL_EXPORT EC_KEY *d2i_EC_PUBKEY(EC_KEY **a, const unsigned char **pp,
-                                     long length);
 
 DECLARE_ASN1_FUNCTIONS(X509_SIG)
 DECLARE_ASN1_FUNCTIONS(X509_REQ_INFO)