Implement new SPKI parsers.

Many consumers need SPKI support (X.509, TLS, QUIC, WebCrypto), each
with different ways to set signature parameters. SPKIs themselves can
get complex with id-RSASSA-PSS keys which come with various constraints
in the key parameters. This suggests we want a common in-library
representation of an SPKI.

This adds two new functions EVP_parse_public_key and
EVP_marshal_public_key which converts EVP_PKEY to and from SPKI and
implements X509_PUBKEY functions with them. EVP_PKEY seems to have been
intended to be able to express the supported SPKI types with
full-fidelity, so these APIs will continue this.

This means future support for id-RSASSA-PSS would *not* repurpose
EVP_PKEY_RSA. I'm worried about code assuming EVP_PKEY_RSA implies
acting on the RSA* is legal. Instead, it'd add an EVP_PKEY_RSA_PSS and
the data pointer would be some (exposed, so the caller may still check
key size, etc.) RSA_PSS_KEY struct. Internally, the EVP_PKEY_CTX
implementation would enforce the key constraints. If RSA_PSS_KEY would
later need its own API, that code would move there, but that seems
unlikely.

Ideally we'd have a 1:1 correspondence with key OID, although we may
have to fudge things if mistakes happen in standardization. (Whether or
not X.509 reuses id-ecPublicKey for Ed25519, we'll give it a separate
EVP_PKEY type.)

DSA parsing hooks are still implemented, missing parameters and all for
now. This isn't any worse than before.

Decoupling from the giant crypto/obj OID table will be a later task.

BUG=522228

Change-Id: I0e3964edf20cb795a18b0991d17e5ca8bce3e28c
Reviewed-on: https://boringssl-review.googlesource.com/6861
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/err/evp.errordata b/crypto/err/evp.errordata
index 8f8dd48..cfb81b1 100644
--- a/crypto/err/evp.errordata
+++ b/crypto/err/evp.errordata
@@ -6,6 +6,7 @@
 EVP,104,DIFFERENT_KEY_TYPES
 EVP,105,DIFFERENT_PARAMETERS
 EVP,147,DIGEST_AND_KEY_TYPE_NOT_SUPPORTED
+EVP,155,ENCODE_ERROR
 EVP,107,EXPECTING_AN_EC_KEY_KEY
 EVP,141,EXPECTING_AN_RSA_KEY
 EVP,109,EXPECTING_A_DH_KEY
diff --git a/crypto/evp/evp_asn1.c b/crypto/evp/evp_asn1.c
index da25b99..e90bed5 100644
--- a/crypto/evp/evp_asn1.c
+++ b/crypto/evp/evp_asn1.c
@@ -57,6 +57,7 @@
 #include <openssl/evp.h>
 
 #include <openssl/asn1.h>
+#include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/obj.h>
 #include <openssl/x509.h>
@@ -64,6 +65,55 @@
 #include "internal.h"
 
 
+EVP_PKEY *EVP_parse_public_key(CBS *cbs) {
+  /* Parse the SubjectPublicKeyInfo. */
+  CBS spki, algorithm, oid, key;
+  uint8_t padding;
+  if (!CBS_get_asn1(cbs, &spki, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !CBS_get_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
+      !CBS_get_asn1(&spki, &key, CBS_ASN1_BITSTRING) ||
+      CBS_len(&spki) != 0 ||
+      /* Every key type defined encodes the key as a byte string with the same
+       * conversion to BIT STRING. */
+      !CBS_get_u8(&key, &padding) ||
+      padding != 0) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+    return NULL;
+  }
+
+  /* Set up an |EVP_PKEY| of the appropriate type. */
+  EVP_PKEY *ret = EVP_PKEY_new();
+  if (ret == NULL ||
+      !EVP_PKEY_set_type(ret, OBJ_cbs2nid(&oid))) {
+    goto err;
+  }
+
+  /* Call into the type-specific SPKI decoding function. */
+  if (ret->ameth->pub_decode == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
+    goto err;
+  }
+  if (!ret->ameth->pub_decode(ret, &algorithm, &key)) {
+    goto err;
+  }
+
+  return ret;
+
+err:
+  EVP_PKEY_free(ret);
+  return NULL;
+}
+
+int EVP_marshal_public_key(CBB *cbb, const EVP_PKEY *key) {
+  if (key->ameth->pub_encode == NULL) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
+    return 0;
+  }
+
+  return key->ameth->pub_encode(cbb, key);
+}
+
 EVP_PKEY *d2i_PrivateKey(int type, EVP_PKEY **out, const uint8_t **inp,
                          long len) {
   EVP_PKEY *ret;
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc
index 8390654..4e7b56e 100644
--- a/crypto/evp/evp_test.cc
+++ b/crypto/evp/evp_test.cc
@@ -71,6 +71,7 @@
 #endif
 
 #include <openssl/bio.h>
+#include <openssl/bytestring.h>
 #include <openssl/crypto.h>
 #include <openssl/digest.h>
 #include <openssl/err.h>
@@ -104,6 +105,20 @@
   return nullptr;
 }
 
+static int GetKeyType(FileTest *t, const std::string &name) {
+  if (name == "RSA") {
+    return EVP_PKEY_RSA;
+  }
+  if (name == "EC") {
+    return EVP_PKEY_EC;
+  }
+  if (name == "DSA") {
+    return EVP_PKEY_DSA;
+  }
+  t->PrintLine("Unknown key type: '%s'", name.c_str());
+  return EVP_PKEY_NONE;
+}
+
 using KeyMap = std::map<std::string, ScopedEVP_PKEY>;
 
 // ImportPrivateKey evaluates a PrivateKey test in |t| and writes the resulting
@@ -128,12 +143,63 @@
   return true;
 }
 
+static bool ImportPublicKey(FileTest *t, KeyMap *key_map) {
+  std::vector<uint8_t> input;
+  if (!t->GetBytes(&input, "Input")) {
+    return false;
+  }
+
+  CBS cbs;
+  CBS_init(&cbs, input.data(), input.size());
+  ScopedEVP_PKEY pkey(EVP_parse_public_key(&cbs));
+  if (!pkey) {
+    return false;
+  }
+
+  std::string key_type;
+  if (!t->GetAttribute(&key_type, "Type")) {
+    return false;
+  }
+  if (EVP_PKEY_id(pkey.get()) != GetKeyType(t, key_type)) {
+    t->PrintLine("Bad key type.");
+    return false;
+  }
+
+  // The encoding must round-trip.
+  ScopedCBB cbb;
+  uint8_t *spki;
+  size_t spki_len;
+  if (!CBB_init(cbb.get(), 0) ||
+      !EVP_marshal_public_key(cbb.get(), pkey.get()) ||
+      !CBB_finish(cbb.get(), &spki, &spki_len)) {
+    return false;
+  }
+  ScopedOpenSSLBytes free_spki(spki);
+  if (!t->ExpectBytesEqual(input.data(), input.size(), spki, spki_len)) {
+    t->PrintLine("Re-encoding the SPKI did not match.");
+    return false;
+  }
+
+  // Save the key for future tests.
+  const std::string &key_name = t->GetParameter();
+  if (key_map->count(key_name) > 0) {
+    t->PrintLine("Duplicate key '%s'.", key_name.c_str());
+    return false;
+  }
+  (*key_map)[key_name] = std::move(pkey);
+  return true;
+}
+
 static bool TestEVP(FileTest *t, void *arg) {
   KeyMap *key_map = reinterpret_cast<KeyMap*>(arg);
   if (t->GetType() == "PrivateKey") {
     return ImportPrivateKey(t, key_map);
   }
 
+  if (t->GetType() == "PublicKey") {
+    return ImportPublicKey(t, key_map);
+  }
+
   int (*key_op_init)(EVP_PKEY_CTX *ctx);
   int (*key_op)(EVP_PKEY_CTX *ctx, uint8_t *out, size_t *out_len,
                 const uint8_t *in, size_t in_len);
diff --git a/crypto/evp/evp_tests.txt b/crypto/evp/evp_tests.txt
index 97ddaa0..2320125 100644
--- a/crypto/evp/evp_tests.txt
+++ b/crypto/evp/evp_tests.txt
@@ -1,9 +1,8 @@
 # Public key algorithm tests
 
-# Private keys used for PKEY operations.
+# Keys used for PKEY operations.
 
 # RSA 2048 bit key.
-
 PrivateKey = RSA-2048
 -----BEGIN PRIVATE KEY-----
 MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNAIHqeyrh6gbV
@@ -34,8 +33,22 @@
 fMkTd7GabVourqIZdgvu1Q==
 -----END PRIVATE KEY-----
 
-# EC P-256 key
+# The public half of the same key encoded as a SubjectPublicKeyInfo.
+PublicKey = RSA-2048-SPKI
+Type = RSA
+Input = 30820122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001
 
+# The same key but with missing parameters rather than a NULL.
+PublicKey = RSA-2048-SPKI-Invalid
+Input = 30820120300b06092a864886f70d0101010382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001
+Error = DECODE_ERROR
+
+# The same key but with an incorrectly-encoded length prefix.
+PublicKey = RSA-2048-SPKI-Invalid2
+Input = 3083000122300d06092a864886f70d01010105000382010f003082010a0282010100cd0081ea7b2ae1ea06d59f7c73d9ffb94a09615c2e4ba7c636cef08dd3533ec3185525b015c769b99a77d6725bf9c3532a9b6e5f6627d5fb85160768d3dda9cbd35974511717dc3d309d2fc47ee41f97e32adb7f9dd864a1c4767a666ecd71bc1aacf5e7517f4b38594fea9b05e42d5ada9912008013e45316a4d9bb8ed086b88d28758bacaf922d46a868b485d239c9baeb0e2b64592710f42b2d1ea0a4b4802c0becab328f8a68b0073bdb546feea9809d2849912b390c1532bc7e29c7658f8175fae46f34332ff87bcab3e40649b98577869da0ea718353f0722754886913648760d122be676e0fc483dd20ffc31bda96a31966c9aa2e75ad03de47e1c44f0203010001
+Error = DECODE_ERROR
+
+# EC P-256 key
 PrivateKey = P-256
 -----BEGIN PRIVATE KEY-----
 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgiocvtiiTxNH/xbnw
@@ -43,6 +56,27 @@
 +JQkBywnGX14szuSDpXNtmTpkNzwz+oNlOKo5q+dDlgFbmUxBJJbn+bJ
 -----END PRIVATE KEY-----
 
+# The public half of the same key encoded as a PublicKey.
+PublicKey = P-256-SPKI
+Type = EC
+Input = 3059301306072a8648ce3d020106082a8648ce3d030107034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+
+# The same as above, but with the curve explicitly spelled out.
+PublicKey = P-256-SPKI
+Input = 3082014b3082010306072a8648ce3d02013081f7020101302c06072a8648ce3d0101022100ffffffff00000001000000000000000000000000ffffffffffffffffffffffff305b0420ffffffff00000001000000000000000000000000fffffffffffffffffffffffc04205ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b031500c49d360886e704936a6678e1139d26b7819f7e900441046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c2964fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5022100ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551020101034200042c150f429ce70f216c252cf5e062ce1f639cd5d165c7f89424072c27197d78b33b920e95cdb664e990dcf0cfea0d94e2a8e6af9d0e58056e653104925b9fe6c9
+Error = DECODE_ERROR
+
+# A DSA key.
+PublicKey = DSA-1024
+Type = DSA
+Input = 308201b73082012c06072a8648ce3804013082011f02818100b3429b8b128c9079f9b72e86857e98d265e5d91661ed8b5f4cc56e5eed1e571da30186983a9dd76297eab73ee13a1db841f8800d04a7cab478af6cde2ea4a2868531af169a24858c6268efa39ceb7ed0d4227eb5bbb01124a2a5a26038c7bcfb8cc827f68f5202345166e4718596799b65c9def82828ce44e62e38e41a0d24b1021500c5a56c81ddd87f47e676546c56d05706421624cf0281810094de40d27314fe929e47ff9b1ac65cfc73ef38c4d381c890be6217b15039ae18190e6b421af8c0bda35a5cfd050f58ae2644adce83e68c8e5ba11729df56bbb21e227a60b816cc033fa799a38fe1ba5b4aa1801b6f841ce3df99feb3b4fb96950c960af13fa2ce920aabc12dd24ad2044a35063ea0e25f67f560f4cfbdc5598303818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+
+# The same key as above, but without the parameters.
+PublicKey = DSA-1024-No-Params
+Type = DSA
+Input = 308192300906072a8648ce38040103818400028180258c30ebbb7f34fdc873ce679f6cea373c7886d75d4421b90920db034daedd292c64d8edd8cdbdd7f3ad23d74cfa2135247d0cef6ecf2e14f99e19d22a8c1266bd8fb8719c0e5667c716c45c7adbdabe548085bdad2dfee636f8d52fd6adb2193df6c4f0520fbd171b91882e0e4f321f8250ffecf4dbea00e114427d3ef96c1a
+
+
 # RSA tests
 
 Sign = RSA-2048
@@ -55,6 +89,11 @@
 Input = "0123456789ABCDEF1234"
 Output = c09d402423cbf233d26cae21f954547bc43fe80fd41360a0336cfdbe9aedad05bef6fd2eaee6cd60089a52482d4809a238149520df3bdde4cb9e23d9307b05c0a6f327052325a29adf2cc95b66523be7024e2a585c3d4db15dfbe146efe0ecdc0402e33fe5d40324ee96c5c3edd374a15cdc0f5d84aa243c0f07e188c6518fbfceae158a9943be398e31097da81b62074f626eff738be6160741d5a26957a482b3251fd85d8df78b98148459de10aa93305dbb4a5230aa1da291a9b0e481918f99b7638d72bb687f97661d304ae145d64a474437a4ef39d7b8059332ddeb07e92bf6e0e3acaf8afedc93795e4511737ec1e7aab6d5bc9466afc950c1c17b48ad
 
+Verify = RSA-2048-SPKI
+Digest = SHA1
+Input = "0123456789ABCDEF1234"
+Output = c09d402423cbf233d26cae21f954547bc43fe80fd41360a0336cfdbe9aedad05bef6fd2eaee6cd60089a52482d4809a238149520df3bdde4cb9e23d9307b05c0a6f327052325a29adf2cc95b66523be7024e2a585c3d4db15dfbe146efe0ecdc0402e33fe5d40324ee96c5c3edd374a15cdc0f5d84aa243c0f07e188c6518fbfceae158a9943be398e31097da81b62074f626eff738be6160741d5a26957a482b3251fd85d8df78b98148459de10aa93305dbb4a5230aa1da291a9b0e481918f99b7638d72bb687f97661d304ae145d64a474437a4ef39d7b8059332ddeb07e92bf6e0e3acaf8afedc93795e4511737ec1e7aab6d5bc9466afc950c1c17b48ad
+
 # Digest too long
 Sign = RSA-2048
 Digest = SHA1
@@ -125,6 +164,11 @@
 Input = "0123456789ABCDEF1234"
 Output = 3045022100b1d1cb1a577035bccdd5a86c6148c2cc7c633cd42b7234139b593076d041e15202201898cdd52b41ca502098184b409cf83a21bc945006746e3b7cea52234e043ec8
 
+Verify = P-256-SPKI
+Digest = SHA1
+Input = "0123456789ABCDEF1234"
+Output = 3045022100b1d1cb1a577035bccdd5a86c6148c2cc7c633cd42b7234139b593076d041e15202201898cdd52b41ca502098184b409cf83a21bc945006746e3b7cea52234e043ec8
+
 # Digest too long
 Verify = P-256
 Digest = SHA1
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index 67b2a65..f2bad30 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -90,8 +90,19 @@
 
   const char *pem_str;
 
-  int (*pub_decode)(EVP_PKEY *pk, X509_PUBKEY *pub);
-  int (*pub_encode)(X509_PUBKEY *pub, const EVP_PKEY *pk);
+  /* pub_decode decodes |params| and |key| as a SubjectPublicKeyInfo
+   * and writes the result into |out|. It returns one on success and zero on
+   * error. |params| is the AlgorithmIdentifier after the OBJECT IDENTIFIER
+   * type field, and |key| is the contents of the subjectPublicKey with the
+   * leading padding byte checked and removed. Although X.509 uses BIT STRINGs
+   * to represent SubjectPublicKeyInfo, every key type defined encodes the key
+   * as a byte string with the same conversion to BIT STRING. */
+  int (*pub_decode)(EVP_PKEY *out, CBS *params, CBS *key);
+
+  /* pub_encode encodes |key| as a SubjectPublicKeyInfo and appends the result
+   * to |out|. It returns one on success and zero on error. */
+  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);
 
diff --git a/crypto/evp/p_dsa_asn1.c b/crypto/evp/p_dsa_asn1.c
index 9f97b16..62f2ac8 100644
--- a/crypto/evp/p_dsa_asn1.c
+++ b/crypto/evp/p_dsa_asn1.c
@@ -55,6 +55,8 @@
 
 #include <openssl/evp.h>
 
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
 #include <openssl/digest.h>
@@ -67,113 +69,63 @@
 #include "internal.h"
 
 
-static int dsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
-  const uint8_t *p, *pm;
-  int pklen, pmlen;
-  int ptype;
-  void *pval;
-  ASN1_STRING *pstr;
-  X509_ALGOR *palg;
-  ASN1_INTEGER *public_key = NULL;
+static int dsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
+  /* See RFC 3279, section 2.3.2. */
 
-  DSA *dsa = NULL;
-
-  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) {
-    return 0;
-  }
-  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-  if (ptype == V_ASN1_SEQUENCE) {
-    pstr = pval;
-    pm = pstr->data;
-    pmlen = pstr->length;
-
-    dsa = d2i_DSAparams(NULL, &pm, pmlen);
+  /* Parameters may or may not be present. */
+  DSA *dsa;
+  if (CBS_len(params) == 0) {
+    dsa = DSA_new();
     if (dsa == NULL) {
+      return 0;
+    }
+  } else {
+    dsa = DSA_parse_parameters(params);
+    if (dsa == NULL || CBS_len(params) != 0) {
       OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
       goto err;
     }
-  } else if (ptype == V_ASN1_NULL || ptype == V_ASN1_UNDEF) {
-    dsa = DSA_new();
-    if (dsa == NULL) {
-      OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-  } else {
-    OPENSSL_PUT_ERROR(EVP, EVP_R_PARAMETER_ENCODING_ERROR);
+  }
+
+  dsa->pub_key = BN_new();
+  if (dsa->pub_key == NULL) {
     goto err;
   }
 
-  public_key = d2i_ASN1_INTEGER(NULL, &p, pklen);
-  if (public_key == NULL) {
+  if (!BN_parse_asn1_unsigned(key, dsa->pub_key) ||
+      CBS_len(key) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     goto err;
   }
 
-  dsa->pub_key = ASN1_INTEGER_to_BN(public_key, NULL);
-  if (dsa->pub_key == NULL) {
-    OPENSSL_PUT_ERROR(EVP, EVP_R_BN_DECODE_ERROR);
-    goto err;
-  }
-
-  ASN1_INTEGER_free(public_key);
-  EVP_PKEY_assign_DSA(pkey, dsa);
+  EVP_PKEY_assign_DSA(out, dsa);
   return 1;
 
 err:
-  ASN1_INTEGER_free(public_key);
   DSA_free(dsa);
   return 0;
 }
 
-static int dsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
-  DSA *dsa;
-  ASN1_STRING *pval = NULL;
-  uint8_t *penc = NULL;
-  int penclen;
+static int dsa_pub_encode(CBB *out, const EVP_PKEY *key) {
+  const DSA *dsa = key->pkey.dsa;
+  const int has_params = dsa->p != NULL && dsa->q != NULL && dsa->g != NULL;
 
-  dsa = pkey->pkey.dsa;
-
-  int ptype;
-  if (dsa->p && dsa->q && dsa->g) {
-    pval = ASN1_STRING_new();
-    if (!pval) {
-      OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-    pval->length = i2d_DSAparams(dsa, &pval->data);
-    if (pval->length <= 0) {
-      OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-      goto err;
-    }
-    ptype = V_ASN1_SEQUENCE;
-  } else {
-    ptype = V_ASN1_UNDEF;
+  /* See RFC 5480, section 2. */
+  CBB spki, algorithm, key_bitstring;
+  if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&algorithm, NID_dsa) ||
+      (has_params &&
+       !DSA_marshal_parameters(&algorithm, dsa)) ||
+      !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
+      !BN_marshal_asn1(&key_bitstring, dsa->pub_key) ||
+      !CBB_flush(out)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
+    return 0;
   }
 
-  ASN1_INTEGER *pubint = BN_to_ASN1_INTEGER(dsa->pub_key, NULL);
-  if (pubint == NULL) {
-    goto err;
-  }
-
-  penclen = i2d_ASN1_INTEGER(pubint, &penc);
-  ASN1_INTEGER_free(pubint);
-
-  if (penclen <= 0) {
-    OPENSSL_PUT_ERROR(EVP, ERR_R_MALLOC_FAILURE);
-    goto err;
-  }
-
-  if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_DSA), ptype, pval,
-                             penc, penclen)) {
-    return 1;
-  }
-
-err:
-  OPENSSL_free(penc);
-  ASN1_STRING_free(pval);
-
-  return 0;
+  return 1;
 }
 
 static int dsa_priv_decode(EVP_PKEY *pkey, PKCS8_PRIV_KEY_INFO *p8) {
diff --git a/crypto/evp/p_ec_asn1.c b/crypto/evp/p_ec_asn1.c
index e093c18..2d8d38a 100644
--- a/crypto/evp/p_ec_asn1.c
+++ b/crypto/evp/p_ec_asn1.c
@@ -57,6 +57,7 @@
 
 #include <openssl/asn1t.h>
 #include <openssl/bn.h>
+#include <openssl/bytestring.h>
 #include <openssl/ec.h>
 #include <openssl/err.h>
 #include <openssl/mem.h>
@@ -86,82 +87,65 @@
   return 1;
 }
 
-static int eckey_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
-  EC_KEY *ec_key = pkey->pkey.ec;
-  void *pval = NULL;
-  int ptype;
-  uint8_t *penc = NULL, *p;
-  int penclen;
-
-  if (!eckey_param2type(&ptype, &pval, ec_key)) {
-    OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB);
+static int eckey_pub_encode(CBB *out, const EVP_PKEY *key) {
+  const EC_KEY *ec_key = key->pkey.ec;
+  const EC_GROUP *group = EC_KEY_get0_group(ec_key);
+  int curve_nid = EC_GROUP_get_curve_name(group);
+  if (curve_nid == NID_undef) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_NO_NID_FOR_CURVE);
     return 0;
   }
-  penclen = i2o_ECPublicKey(ec_key, NULL);
-  if (penclen <= 0) {
-    goto err;
-  }
-  penc = OPENSSL_malloc(penclen);
-  if (!penc) {
-    goto err;
-  }
-  p = penc;
-  penclen = i2o_ECPublicKey(ec_key, &p);
-  if (penclen <= 0) {
-    goto err;
-  }
-  if (X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_EC), ptype, pval, penc,
-                             penclen)) {
-    return 1;
+  const EC_POINT *public_key = EC_KEY_get0_public_key(ec_key);
+
+  /* See RFC 5480, section 2. */
+  CBB spki, algorithm, key_bitstring;
+  if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&algorithm, NID_X9_62_id_ecPublicKey) ||
+      !OBJ_nid2cbb(&algorithm, curve_nid) ||
+      !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
+      !EC_POINT_point2cbb(&key_bitstring, group, public_key,
+                          POINT_CONVERSION_UNCOMPRESSED, NULL) ||
+      !CBB_flush(out)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
+    return 0;
   }
 
-err:
-  if (ptype == V_ASN1_OBJECT) {
-    ASN1_OBJECT_free(pval);
-  } else {
-    ASN1_STRING_free(pval);
-  }
-  if (penc) {
-    OPENSSL_free(penc);
-  }
-  return 0;
+  return 1;
 }
 
-static int eckey_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
-  const uint8_t *p = NULL;
-  void *pval;
-  int ptype, pklen;
-  EC_KEY *eckey = NULL;
-  X509_ALGOR *palg;
+static int eckey_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
+  /* See RFC 5480, section 2. */
 
-  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, &palg, pubkey)) {
-    return 0;
-  }
-  X509_ALGOR_get0(NULL, &ptype, &pval, palg);
-
-  if (ptype != V_ASN1_OBJECT) {
+  /* The parameters are a named curve. */
+  CBS named_curve;
+  if (!CBS_get_asn1(params, &named_curve, CBS_ASN1_OBJECT) ||
+      CBS_len(params) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return 0;
   }
-  eckey = EC_KEY_new_by_curve_name(OBJ_obj2nid((ASN1_OBJECT *)pval));
+
+  EC_KEY *eckey = EC_KEY_new_by_curve_name(OBJ_cbs2nid(&named_curve));
   if (eckey == NULL) {
-    OPENSSL_PUT_ERROR(EVP, ERR_R_EC_LIB);
     return 0;
   }
 
-  /* We have parameters now set public key */
-  if (!o2i_ECPublicKey(&eckey, &p, pklen)) {
-    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
+  EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(eckey));
+  if (point == NULL ||
+      !EC_POINT_oct2point(EC_KEY_get0_group(eckey), point, CBS_data(key),
+                          CBS_len(key), NULL) ||
+      !EC_KEY_set_public_key(eckey, point)) {
     goto err;
   }
 
-  EVP_PKEY_assign_EC_KEY(pkey, eckey);
+  EC_POINT_free(point);
+  EVP_PKEY_assign_EC_KEY(out, eckey);
   return 1;
 
 err:
-  if (eckey) {
-    EC_KEY_free(eckey);
-  }
+  EC_POINT_free(point);
+  EC_KEY_free(eckey);
   return 0;
 }
 
diff --git a/crypto/evp/p_rsa_asn1.c b/crypto/evp/p_rsa_asn1.c
index 70f0b76..83da7df 100644
--- a/crypto/evp/p_rsa_asn1.c
+++ b/crypto/evp/p_rsa_asn1.c
@@ -69,26 +69,33 @@
 #include "internal.h"
 
 
-static int rsa_pub_encode(X509_PUBKEY *pk, const EVP_PKEY *pkey) {
-  uint8_t *encoded;
-  size_t encoded_len;
-  if (!RSA_public_key_to_bytes(&encoded, &encoded_len, pkey->pkey.rsa)) {
-    return 0;
-  }
-
-  if (!X509_PUBKEY_set0_param(pk, OBJ_nid2obj(EVP_PKEY_RSA), V_ASN1_NULL, NULL,
-                              encoded, encoded_len)) {
-    OPENSSL_free(encoded);
+static int rsa_pub_encode(CBB *out, const EVP_PKEY *key) {
+  /* See RFC 3279, section 2.3.1. */
+  CBB spki, algorithm, null, key_bitstring;
+  if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
+      !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
+      !OBJ_nid2cbb(&algorithm, NID_rsaEncryption) ||
+      !CBB_add_asn1(&algorithm, &null, CBS_ASN1_NULL) ||
+      !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
+      !CBB_add_u8(&key_bitstring, 0 /* padding */) ||
+      !RSA_marshal_public_key(&key_bitstring, key->pkey.rsa) ||
+      !CBB_flush(out)) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
     return 0;
   }
 
   return 1;
 }
 
-static int rsa_pub_decode(EVP_PKEY *pkey, X509_PUBKEY *pubkey) {
-  const uint8_t *p;
-  int pklen;
-  if (!X509_PUBKEY_get0_param(NULL, &p, &pklen, NULL, pubkey)) {
+static int rsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
+  /* See RFC 3279, section 2.3.1. */
+
+  /* The parameters must be NULL. */
+  CBS null;
+  if (!CBS_get_asn1(params, &null, CBS_ASN1_NULL) ||
+      CBS_len(&null) != 0 ||
+      CBS_len(params) != 0) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     return 0;
   }
 
@@ -98,16 +105,14 @@
    * TODO(davidben): Switch this to the strict version in March 2016 or when
    * Chromium can force client certificates down a different codepath, whichever
    * comes first. */
-  CBS cbs;
-  CBS_init(&cbs, p, pklen);
-  RSA *rsa = RSA_parse_public_key_buggy(&cbs);
-  if (rsa == NULL || CBS_len(&cbs) != 0) {
+  RSA *rsa = RSA_parse_public_key_buggy(key);
+  if (rsa == NULL || CBS_len(key) != 0) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
     RSA_free(rsa);
     return 0;
   }
 
-  EVP_PKEY_assign_RSA(pkey, rsa);
+  EVP_PKEY_assign_RSA(out, rsa);
   return 1;
 }
 
diff --git a/crypto/test/file_test.h b/crypto/test/file_test.h
index 8fb7ed2..501375b 100644
--- a/crypto/test/file_test.h
+++ b/crypto/test/file_test.h
@@ -111,7 +111,7 @@
   bool GetAttribute(std::string *out_value, const std::string &key);
 
   // GetAttributeOrDie looks up the attribute with key |key| and aborts if it is
-  // missing. It only be used after a |HasAttribute| call.
+  // missing. It should only be used after a |HasAttribute| call.
   const std::string &GetAttributeOrDie(const std::string &key);
 
   // GetBytes looks up the attribute with key |key| and decodes it as a byte
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index e8732a1..47f256c 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -54,8 +54,11 @@
  * copied and put under another distribution licence
  * [including the GNU Public Licence.] */
 
+#include <limits.h>
+
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
+#include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 #include <openssl/mem.h>
@@ -63,7 +66,6 @@
 #include <openssl/thread.h>
 #include <openssl/x509.h>
 
-#include "../evp/internal.h"
 #include "../internal.h"
 
 /* Minor tweak to operation: free up EVP_PKEY */
@@ -87,51 +89,50 @@
 int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey)
 {
     X509_PUBKEY *pk = NULL;
+    uint8_t *spki = NULL;
+    size_t spki_len;
 
     if (x == NULL)
         return (0);
 
-    if ((pk = X509_PUBKEY_new()) == NULL)
-        goto error;
-
-    if (pkey->ameth) {
-        if (pkey->ameth->pub_encode) {
-            if (!pkey->ameth->pub_encode(pk, pkey)) {
-                OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
-                goto error;
-            }
-        } else {
-            OPENSSL_PUT_ERROR(X509, X509_R_METHOD_NOT_SUPPORTED);
-            goto error;
-        }
-    } else {
-        OPENSSL_PUT_ERROR(X509, X509_R_UNSUPPORTED_ALGORITHM);
+    CBB cbb;
+    if (!CBB_init(&cbb, 0) ||
+        !EVP_marshal_public_key(&cbb, pkey) ||
+        !CBB_finish(&cbb, &spki, &spki_len) ||
+        spki_len > LONG_MAX) {
+        CBB_cleanup(&cbb);
+        OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_ENCODE_ERROR);
         goto error;
     }
 
-    if (*x != NULL)
-        X509_PUBKEY_free(*x);
+    const uint8_t *p = spki;
+    pk = d2i_X509_PUBKEY(NULL, &p, (long)spki_len);
+    if (pk == NULL || p != spki + spki_len) {
+        OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
+        goto error;
+    }
 
+    OPENSSL_free(spki);
+    X509_PUBKEY_free(*x);
     *x = pk;
 
     return 1;
  error:
-    if (pk != NULL)
-        X509_PUBKEY_free(pk);
+    X509_PUBKEY_free(pk);
+    OPENSSL_free(spki);
     return 0;
 }
 
-/*
- * g_pubkey_lock is used to protect the initialisation of the |pkey| member
- * of |X509_PUBKEY| objects. Really |X509_PUBKEY| should have a
- * |CRYPTO_once_t| inside it for this, but |CRYPTO_once_t| is private and
- * |X509_PUBKEY| is not.
- */
+/* g_pubkey_lock is used to protect the initialisation of the |pkey| member of
+ * |X509_PUBKEY| objects. Really |X509_PUBKEY| should have a |CRYPTO_once_t|
+ * inside it for this, but |CRYPTO_once_t| is private and |X509_PUBKEY| is
+ * not. */
 static struct CRYPTO_STATIC_MUTEX g_pubkey_lock = CRYPTO_STATIC_MUTEX_INIT;
 
 EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key)
 {
     EVP_PKEY *ret = NULL;
+    uint8_t *spki = NULL;
 
     if (key == NULL)
         goto error;
@@ -143,26 +144,16 @@
     }
     CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
 
-    if (key->public_key == NULL)
-        goto error;
-
-    if ((ret = EVP_PKEY_new()) == NULL) {
-        OPENSSL_PUT_ERROR(X509, ERR_R_MALLOC_FAILURE);
+    /* Re-encode the |X509_PUBKEY| to DER and parse it. */
+    int spki_len = i2d_X509_PUBKEY(key, &spki);
+    if (spki_len < 0) {
         goto error;
     }
-
-    if (!EVP_PKEY_set_type(ret, OBJ_obj2nid(key->algor->algorithm))) {
-        OPENSSL_PUT_ERROR(X509, X509_R_UNSUPPORTED_ALGORITHM);
-        goto error;
-    }
-
-    if (ret->ameth->pub_decode) {
-        if (!ret->ameth->pub_decode(ret, key)) {
-            OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
-            goto error;
-        }
-    } else {
-        OPENSSL_PUT_ERROR(X509, X509_R_METHOD_NOT_SUPPORTED);
+    CBS cbs;
+    CBS_init(&cbs, spki, (size_t)spki_len);
+    ret = EVP_parse_public_key(&cbs);
+    if (ret == NULL || CBS_len(&cbs) != 0) {
+        OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
         goto error;
     }
 
@@ -177,12 +168,13 @@
         CRYPTO_STATIC_MUTEX_unlock(&g_pubkey_lock);
     }
 
+    OPENSSL_free(spki);
     return EVP_PKEY_up_ref(ret);
 
  error:
-    if (ret != NULL)
-        EVP_PKEY_free(ret);
-    return (NULL);
+    OPENSSL_free(spki);
+    EVP_PKEY_free(ret);
+    return NULL;
 }
 
 /*
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 4f9426f..ec143e2 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -188,6 +188,20 @@
 
 /* ASN.1 functions */
 
+/* EVP_parse_public_key decodes a DER-encoded SubjectPublicKeyInfo structure
+ * (RFC 5280) from |cbs| and advances |cbs|. It returns a newly-allocated
+ * |EVP_PKEY| or NULL on error.
+ *
+ * The caller must check the type of the parsed public key to ensure it is
+ * suitable and validate other desired key properties such as RSA modulus size
+ * or EC curve. */
+OPENSSL_EXPORT EVP_PKEY *EVP_parse_public_key(CBS *cbs);
+
+/* EVP_marshal_public_key marshals |key| as a DER-encoded SubjectPublicKeyInfo
+ * structure (RFC 5280) and appends the result to |cbb|. It returns one on
+ * success and zero on error. */
+OPENSSL_EXPORT int EVP_marshal_public_key(CBB *cbb, const EVP_PKEY *key);
+
 /* d2i_PrivateKey parses an ASN.1, DER-encoded, private key from |len| bytes at
  * |*inp|. If |out| is not NULL then, on exit, a pointer to the result is in
  * |*out|. If |*out| is already non-NULL on entry then the result is written
@@ -780,5 +794,6 @@
 #define EVP_R_PARAMETER_ENCODING_ERROR 152
 #define EVP_R_UNSUPPORTED_PUBLIC_KEY_TYPE 153
 #define EVP_R_UNSUPPORTED_SIGNATURE_TYPE 154
+#define EVP_R_ENCODE_ERROR 155
 
 #endif  /* OPENSSL_HEADER_EVP_H */