Add a function to copy the public part of an EVP_PKEY

This provides a consistent way to take an EVP_PKEY containing a private
key and get a new EVP_PKEY that matches it, but only has the public key.
Without this convenience function, depending on the key type, the caller
may have to manipulate the underlying algorithm-specific key, or
serialize the public key and deserialize it into a new EVP_PKEY.

Change-Id: I399b1ee44f2f6531105ae63f1653b3206a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/91067
Commit-Queue: Lily Chen <chlily@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/err/evp.errordata b/crypto/err/evp.errordata
index cd2c84f..eeb9ab5 100644
--- a/crypto/err/evp.errordata
+++ b/crypto/err/evp.errordata
@@ -24,6 +24,7 @@
 EVP,117,KEYS_NOT_SET
 EVP,132,MEMORY_LIMIT_EXCEEDED
 EVP,118,MISSING_PARAMETERS
+EVP,140,MISSING_PUBLIC_KEY
 EVP,130,NOT_A_PRIVATE_KEY
 EVP,135,NOT_XOF_OR_INVALID_LENGTH
 EVP,119,NO_DEFAULT_DIGEST
diff --git a/crypto/evp/evp.cc b/crypto/evp/evp.cc
index 5029097..df05bf2 100644
--- a/crypto/evp/evp.cc
+++ b/crypto/evp/evp.cc
@@ -423,3 +423,17 @@
   }
   return impl->ameth->priv_present(impl);
 }
+
+EVP_PKEY *EVP_PKEY_copy_public(const EVP_PKEY *pkey) {
+  auto *impl = FromOpaque(pkey);
+  if (impl == nullptr || impl->ameth == nullptr || impl->pkey == nullptr ||
+      impl->ameth->pub_copy == nullptr) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE);
+    return nullptr;
+  }
+  UniquePtr<EvpPkey> ret(FromOpaque(EVP_PKEY_new()));
+  if (ret == nullptr || !impl->ameth->pub_copy(ret.get(), impl)) {
+    return nullptr;
+  }
+  return ret.release();
+}
diff --git a/crypto/evp/evp_test.cc b/crypto/evp/evp_test.cc
index 1605199..8ee626b 100644
--- a/crypto/evp/evp_test.cc
+++ b/crypto/evp/evp_test.cc
@@ -412,6 +412,26 @@
         !CheckRawKey(t, "PrivateSeed", pkey.get(), EVP_PKEY_get_private_seed)) {
       return false;
     }
+
+    // Test copying the public part of the key.
+    UniquePtr<EVP_PKEY> public_copy(EVP_PKEY_copy_public(pkey.get()));
+    EXPECT_TRUE(public_copy);
+    EXPECT_TRUE(EVP_PKEY_has_public(public_copy.get()));
+    EXPECT_FALSE(EVP_PKEY_has_private(public_copy.get()));
+    EXPECT_EQ(EVP_PKEY_cmp(public_copy.get(), pkey.get()), 1);
+    EXPECT_EQ(EVP_PKEY_cmp_parameters(public_copy.get(), pkey.get()), 1);
+    // Check that the copied public key serializes the same.
+    bssl::ScopedCBB cbb_public, cbb_public_copy;
+    if (!CBB_init(cbb_public.get(), 0) ||
+        !EVP_marshal_public_key(cbb_public.get(), pkey.get()) ||
+        !CBB_init(cbb_public_copy.get(), 0) ||
+        !EVP_marshal_public_key(cbb_public_copy.get(), public_copy.get())) {
+      return false;
+    }
+    EXPECT_EQ(
+        Bytes(CBB_data(cbb_public.get()), CBB_len(cbb_public.get())),
+        Bytes(CBB_data(cbb_public_copy.get()), CBB_len(cbb_public_copy.get())))
+        << "Public copy of the key did not match.";
   }
 
   // Save the key for future tests.
diff --git a/crypto/evp/internal.h b/crypto/evp/internal.h
index f5694c2..9594464 100644
--- a/crypto/evp/internal.h
+++ b/crypto/evp/internal.h
@@ -83,6 +83,11 @@
   // is not guaranteed and should be checked separately.)
   bool (*pub_present)(const EvpPkey *pk);
 
+  // pub_copy sets the key data of |out| to a newly allocated key data structure
+  // which contains a copy of only the public key of |pk|, freeing any key
+  // previously in |out|. Returns true on success or false on failure.
+  bool (*pub_copy)(EvpPkey *out, const EvpPkey *pk);
+
   // priv_decode decodes |params| and |key| as a PrivateKeyInfo and writes the
   // result into |out|.  It returns |evp_decode_ok| on success, and
   // |evp_decode_error| on error, and |evp_decode_unsupported| if the key type
diff --git a/crypto/evp/p_dh.cc b/crypto/evp/p_dh.cc
index b2420ab..0fe8a3b 100644
--- a/crypto/evp/p_dh.cc
+++ b/crypto/evp/p_dh.cc
@@ -103,6 +103,25 @@
   return DH_get0_pub_key(pk_dh) != nullptr;
 }
 
+static bool dh_pub_copy(EvpPkey *out, const EvpPkey *pk) {
+  const DH *pk_dh = reinterpret_cast<const DH *>(pk->pkey);
+  const BIGNUM *public_key = DH_get0_pub_key(pk_dh);
+  if (public_key == nullptr) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PUBLIC_KEY);
+    return false;
+  }
+  UniquePtr<DH> public_copy_dh(DHparams_dup(pk_dh));
+  BIGNUM *public_key_copy = BN_dup(public_key);
+  if (public_copy_dh == nullptr || public_key_copy == nullptr ||
+      !DH_set0_key(public_copy_dh.get(), public_key_copy, nullptr)) {
+    BN_free(public_key_copy);
+    OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  evp_pkey_set0(out, pk->ameth, public_copy_dh.release());
+  return true;
+}
+
 static bool dh_has_priv(const EvpPkey *pk) {
   const DH *pk_dh = reinterpret_cast<const DH *>(pk->pkey);
   return DH_get0_priv_key(pk_dh) != nullptr;
@@ -117,6 +136,7 @@
     /*pub_encode=*/nullptr,
     /*pub_equal=*/dh_pub_equal,
     /*pub_present=*/dh_has_pub,
+    /*pub_dup=*/dh_pub_copy,
     /*priv_decode=*/nullptr,
     /*priv_encode=*/nullptr,
     /*priv_present=*/dh_has_priv,
diff --git a/crypto/evp/p_dsa.cc b/crypto/evp/p_dsa.cc
index eb08b99..2ea241f 100644
--- a/crypto/evp/p_dsa.cc
+++ b/crypto/evp/p_dsa.cc
@@ -207,6 +207,25 @@
   return DSA_get0_pub_key(pk_dsa) != nullptr;
 }
 
+static bool dsa_pub_copy(EvpPkey *out, const EvpPkey *pk) {
+  const DSA *pk_dsa = reinterpret_cast<const DSA *>(pk->pkey);
+  const BIGNUM *public_key = DSA_get0_pub_key(pk_dsa);
+  if (public_key == nullptr) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PUBLIC_KEY);
+    return false;
+  }
+  UniquePtr<DSA> public_copy_dsa(DSAparams_dup(pk_dsa));
+  BIGNUM *public_key_copy = BN_dup(public_key);
+  if (public_copy_dsa == nullptr || public_key_copy == nullptr ||
+      !DSA_set0_key(public_copy_dsa.get(), public_key_copy, nullptr)) {
+    BN_free(public_key_copy);
+    OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  evp_pkey_set0(out, pk->ameth, public_copy_dsa.release());
+  return true;
+}
+
 static bool dsa_priv_present(const EvpPkey *pk) {
   const DSA *pk_dsa = reinterpret_cast<const DSA *>(pk->pkey);
   return DSA_get0_priv_key(pk_dsa) != nullptr;
@@ -229,6 +248,7 @@
     dsa_pub_encode,
     dsa_pub_equal,
     dsa_pub_present,
+    dsa_pub_copy,
 
     dsa_priv_decode,
     dsa_priv_encode,
diff --git a/crypto/evp/p_ec.cc b/crypto/evp/p_ec.cc
index 2e4704a..af3ff78 100644
--- a/crypto/evp/p_ec.cc
+++ b/crypto/evp/p_ec.cc
@@ -269,6 +269,24 @@
   return EC_KEY_get0_public_key(ec_key) != nullptr;
 }
 
+static bool eckey_pub_copy(EvpPkey *out, const EvpPkey *pkey) {
+  const EC_KEY *ec_key = reinterpret_cast<const EC_KEY *>(pkey->pkey);
+  const EC_POINT *public_key = EC_KEY_get0_public_key(ec_key);
+  if (public_key == nullptr) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PUBLIC_KEY);
+    return false;
+  }
+  UniquePtr<EC_KEY> public_copy_ec_key(EC_KEY_new());
+  if (public_copy_ec_key == nullptr ||
+      !EC_KEY_set_group(public_copy_ec_key.get(), public_key->group) ||
+      !EC_KEY_set_public_key(public_copy_ec_key.get(), public_key)) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  evp_pkey_set0(out, pkey->ameth, public_copy_ec_key.release());
+  return true;
+}
+
 static bool eckey_priv_present(const EvpPkey *pkey) {
   const EC_KEY *ec_key = reinterpret_cast<const EC_KEY *>(pkey->pkey);
   return EC_KEY_get0_private_key(ec_key) != nullptr;
@@ -286,6 +304,7 @@
     eckey_pub_encode,
     eckey_pub_equal,
     eckey_pub_present,
+    eckey_pub_copy,
 
     eckey_priv_decode,
     eckey_priv_encode,
diff --git a/crypto/evp/p_ed25519.cc b/crypto/evp/p_ed25519.cc
index 617c249..4fb1917 100644
--- a/crypto/evp/p_ed25519.cc
+++ b/crypto/evp/p_ed25519.cc
@@ -218,6 +218,21 @@
 
 static bool ed25519_pub_present(const EvpPkey *) { return true; }
 
+static bool ed25519_pub_copy(EvpPkey *out, const EvpPkey *pkey) {
+  const ED25519_KEY *pkey_ed25519 =
+      reinterpret_cast<const ED25519_KEY *>(pkey->pkey);
+  ED25519_KEY *public_copy = New<ED25519_KEY>();
+  if (public_copy == nullptr) {
+    return false;
+  }
+  OPENSSL_memcpy(public_copy->key + ED25519_PUBLIC_KEY_OFFSET,
+                 pkey_ed25519->key + ED25519_PUBLIC_KEY_OFFSET,
+                 ED25519_PUBLIC_KEY_LEN);
+  public_copy->has_private = false;
+  evp_pkey_set0(out, pkey->ameth, public_copy);
+  return true;
+}
+
 static bool ed25519_priv_present(const EvpPkey *pkey) {
   const ED25519_KEY *key = reinterpret_cast<const ED25519_KEY *>(pkey->pkey);
   return key->has_private;
@@ -236,6 +251,7 @@
     ed25519_pub_encode,
     ed25519_pub_equal,
     ed25519_pub_present,
+    ed25519_pub_copy,
     ed25519_priv_decode,
     ed25519_priv_encode,
     ed25519_priv_present,
diff --git a/crypto/evp/p_mldsa.cc b/crypto/evp/p_mldsa.cc
index 14365e0..e8b29bc 100644
--- a/crypto/evp/p_mldsa.cc
+++ b/crypto/evp/p_mldsa.cc
@@ -16,6 +16,8 @@
 
 #include <assert.h>
 
+#include <type_traits>
+
 #include <openssl/bytestring.h>
 #include <openssl/err.h>
 #include <openssl/mldsa.h>
@@ -59,6 +61,8 @@
     static constexpr auto PublicKeysEqual =                                   \
         &BCM_mldsa##kl##_public_keys_equal;                                   \
     static constexpr auto Verify = &MLDSA##kl##_verify;                       \
+    static_assert(std::is_trivially_copyable_v<PublicKey>,                    \
+                  "PublicKey type must be trivially copyable.");              \
   };
 
 MAKE_MLDSA_TRAITS(44)
@@ -105,6 +109,11 @@
  public:
   enum { kAllowUniquePtr = true };
   PublicKeyData() : KeyData<Traits>(/*is_private=*/false) {}
+
+  // Allows copying the PublicKey.
+  explicit PublicKeyData(const typename Traits::PublicKey &key)
+      : KeyData<Traits>(/*is_private=*/false), pub(key) {}
+
   typename Traits::PublicKey pub;
 };
 
@@ -279,6 +288,17 @@
 
   static bool HasPublic(const EvpPkey *pk) { return true; }
 
+  static bool CopyPublic(EvpPkey *out, const EvpPkey *pk) {
+    auto *public_copy =
+        New<PublicKeyData<Traits>>(*GetKeyData(pk)->GetPublicKey());
+    if (public_copy == nullptr) {
+      OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
+      return false;
+    }
+    evp_pkey_set0(out, pk->ameth, public_copy);
+    return true;
+  }
+
   static evp_decode_result_t DecodePrivate(const EVP_PKEY_ALG *alg,
                                            EvpPkey *out, CBS *params,
                                            CBS *key) {
@@ -476,6 +496,7 @@
         &EncodePublic,
         &EqualPublic,
         &HasPublic,
+        &CopyPublic,
         &DecodePrivate,
         &EncodePrivate,
         &HasPrivate,
@@ -494,8 +515,8 @@
         &PkeySize,
         &PkeyBits,
         /*param_missing=*/nullptr,
+        /*param_copy=*/nullptr,
         /*param_equal=*/nullptr,
-        /*param_cmp=*/nullptr,
         &PkeyFree,
     };
     // TODO(crbug.com/404286922): Use std::copy in C++20, when it's constexpr.
diff --git a/crypto/evp/p_rsa.cc b/crypto/evp/p_rsa.cc
index fff90c5..8d04a6e 100644
--- a/crypto/evp/p_rsa.cc
+++ b/crypto/evp/p_rsa.cc
@@ -108,6 +108,24 @@
   return RSA_get0_n(pk_rsa) != nullptr && RSA_get0_e(pk_rsa) != nullptr;
 }
 
+static bool rsa_pub_copy(EvpPkey *out, const EvpPkey *pkey) {
+  const RSAImpl *pk_rsa = reinterpret_cast<const RSAImpl *>(pkey->pkey);
+  const BIGNUM *pk_n = RSA_get0_n(pk_rsa);
+  const BIGNUM *pk_e = RSA_get0_e(pk_rsa);
+  if (pk_n == nullptr || pk_e == nullptr) {
+    OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PUBLIC_KEY);
+    return false;
+  }
+  UniquePtr<RSA> public_copy_rsa(RSA_new_public_key(pk_n, pk_e));
+  if (!public_copy_rsa) {
+    OPENSSL_PUT_ERROR(EVP, ERR_R_INTERNAL_ERROR);
+    return false;
+  }
+  FromOpaque(public_copy_rsa.get())->pss_params = pk_rsa->pss_params;
+  evp_pkey_set0(out, pkey->ameth, public_copy_rsa.release());
+  return true;
+}
+
 static int rsa_priv_encode(CBB *out, const EvpPkey *key) {
   const RSA *rsa = reinterpret_cast<const RSA *>(key->pkey);
   CBB pkcs8, algorithm, null, private_key;
@@ -280,6 +298,7 @@
     rsa_pub_encode,
     rsa_pub_equal,
     rsa_pub_present,
+    rsa_pub_copy,
 
     rsa_priv_decode,
     rsa_priv_encode,
@@ -318,6 +337,7 @@
     rsa_pub_encode_pss,
     rsa_pub_equal,
     rsa_pub_present,
+    rsa_pub_copy,
 
     rsa_priv_decode_pss,
     rsa_priv_encode_pss,
diff --git a/crypto/evp/p_x25519.cc b/crypto/evp/p_x25519.cc
index f18783a..c75d0aa 100644
--- a/crypto/evp/p_x25519.cc
+++ b/crypto/evp/p_x25519.cc
@@ -228,6 +228,19 @@
 
 static bool x25519_pub_present(const EvpPkey *) { return true; }
 
+static bool x25519_pub_copy(EvpPkey *out, const EvpPkey *pkey) {
+  const X25519_KEY *pkey_x25519 =
+      reinterpret_cast<const X25519_KEY *>(pkey->pkey);
+  X25519_KEY *public_copy = New<X25519_KEY>();
+  if (public_copy == nullptr) {
+    return false;
+  }
+  OPENSSL_memcpy(public_copy->pub, pkey_x25519->pub, 32);
+  public_copy->has_private = false;
+  evp_pkey_set0(out, pkey->ameth, public_copy);
+  return true;
+}
+
 static bool x25519_priv_present(const EvpPkey *pk) {
   const X25519_KEY *key = reinterpret_cast<const X25519_KEY *>(pk->pkey);
   return key->has_private;
@@ -246,6 +259,7 @@
     x25519_pub_encode,
     x25519_pub_equal,
     x25519_pub_present,
+    x25519_pub_copy,
     x25519_priv_decode,
     x25519_priv_encode,
     x25519_priv_present,
diff --git a/gen/crypto/err_data.cc b/gen/crypto/err_data.cc
index 9d8e2d2..45e5716 100644
--- a/gen/crypto/err_data.cc
+++ b/gen/crypto/err_data.cc
@@ -80,54 +80,54 @@
     0xc3b00f7,
     0xc3b8921,
     0x10320892,
-    0x103296c6,
-    0x103316d2,
-    0x103396eb,
-    0x103416fe,
+    0x103296d9,
+    0x103316e5,
+    0x103396fe,
+    0x10341711,
     0x10348fd3,
     0x10350d1f,
-    0x10359711,
-    0x1036173b,
-    0x1036974e,
-    0x1037176d,
-    0x10379786,
-    0x1038179b,
-    0x103897b9,
-    0x103917c8,
-    0x103997e4,
-    0x103a17ff,
-    0x103a980e,
-    0x103b182a,
-    0x103b9845,
-    0x103c186b,
+    0x10359724,
+    0x1036174e,
+    0x10369761,
+    0x10371780,
+    0x10379799,
+    0x103817ae,
+    0x103897cc,
+    0x103917db,
+    0x103997f7,
+    0x103a1812,
+    0x103a9821,
+    0x103b183d,
+    0x103b9858,
+    0x103c187e,
     0x103c80f7,
-    0x103d187c,
-    0x103d9890,
-    0x103e18af,
-    0x103e98be,
-    0x103f18d5,
-    0x103f98e8,
+    0x103d188f,
+    0x103d98a3,
+    0x103e18c2,
+    0x103e98d1,
+    0x103f18e8,
+    0x103f98fb,
     0x10400ce3,
-    0x104098fb,
-    0x10411919,
-    0x1041992c,
-    0x10421946,
-    0x10429956,
-    0x1043196a,
-    0x10439980,
-    0x10441998,
-    0x104499ad,
-    0x104519c1,
-    0x104599d3,
+    0x1040990e,
+    0x1041192c,
+    0x1041993f,
+    0x10421959,
+    0x10429969,
+    0x1043197d,
+    0x10439993,
+    0x104419ab,
+    0x104499c0,
+    0x104519d4,
+    0x104599e6,
     0x10460635,
     0x1046899a,
-    0x104719e8,
-    0x104799ff,
-    0x10481a14,
-    0x10489a22,
+    0x104719fb,
+    0x10479a12,
+    0x10481a27,
+    0x10489a35,
     0x10490f1f,
-    0x1049985c,
-    0x104a1726,
+    0x1049986f,
+    0x104a1739,
     0x14320cb3,
     0x14328cd4,
     0x14330ce3,
@@ -154,45 +154,46 @@
     0x183a1169,
     0x183a918f,
     0x183b0d2b,
-    0x183b91de,
-    0x183c11f0,
-    0x183c91fb,
-    0x183d120b,
-    0x183d921c,
-    0x183e122d,
-    0x183e923f,
-    0x183f1268,
-    0x183f929a,
-    0x184012b2,
+    0x183b91f1,
+    0x183c1203,
+    0x183c920e,
+    0x183d121e,
+    0x183d922f,
+    0x183e1240,
+    0x183e9252,
+    0x183f127b,
+    0x183f92ad,
+    0x184012c5,
     0x1840870d,
-    0x184111b2,
+    0x184111c5,
     0x1841917d,
     0x1842119c,
     0x18428cc1,
     0x18431158,
-    0x184391c4,
+    0x184391d7,
     0x18441068,
     0x184490e5,
     0x18451087,
-    0x18459281,
-    0x203212ec,
-    0x203292d9,
-    0x243212f8,
+    0x18459294,
+    0x184611b2,
+    0x203212ff,
+    0x203292ec,
+    0x2432130b,
     0x243289e0,
-    0x2433130a,
-    0x24339317,
-    0x24341324,
-    0x24349336,
-    0x24351345,
-    0x24359362,
-    0x2436136f,
-    0x2436937d,
-    0x2437138b,
-    0x24379399,
-    0x243813a2,
-    0x243893af,
-    0x243913c2,
-    0x243993d9,
+    0x2433131d,
+    0x2433932a,
+    0x24341337,
+    0x24349349,
+    0x24351358,
+    0x24359375,
+    0x24361382,
+    0x24369390,
+    0x2437139e,
+    0x243793ac,
+    0x243813b5,
+    0x243893c2,
+    0x243913d5,
+    0x243993ec,
     0x28320d13,
     0x28328d2b,
     0x28330ce3,
@@ -202,51 +203,51 @@
     0x283500f7,
     0x28358cc1,
     0x2836099a,
-    0x2c32344e,
-    0x2c3293f7,
-    0x2c33345c,
-    0x2c33b46e,
-    0x2c343482,
-    0x2c34b494,
-    0x2c3534af,
-    0x2c35b4c1,
-    0x2c3634f1,
+    0x2c323461,
+    0x2c32940a,
+    0x2c33346f,
+    0x2c33b481,
+    0x2c343495,
+    0x2c34b4a7,
+    0x2c3534c2,
+    0x2c35b4d4,
+    0x2c363504,
     0x2c36833a,
-    0x2c3734fe,
-    0x2c37b52a,
-    0x2c383568,
-    0x2c38b57f,
-    0x2c39359d,
-    0x2c39b5ad,
-    0x2c3a35bf,
-    0x2c3ab5d3,
-    0x2c3b35e4,
-    0x2c3bb603,
-    0x2c3c1409,
-    0x2c3c941f,
-    0x2c3d3648,
-    0x2c3d9438,
-    0x2c3e3672,
-    0x2c3eb680,
-    0x2c3f3698,
-    0x2c3fb6b0,
-    0x2c4036da,
-    0x2c4092ec,
-    0x2c4136eb,
-    0x2c41b6fe,
-    0x2c4212b2,
-    0x2c42b70f,
+    0x2c373511,
+    0x2c37b53d,
+    0x2c38357b,
+    0x2c38b592,
+    0x2c3935b0,
+    0x2c39b5c0,
+    0x2c3a35d2,
+    0x2c3ab5e6,
+    0x2c3b35f7,
+    0x2c3bb616,
+    0x2c3c141c,
+    0x2c3c9432,
+    0x2c3d365b,
+    0x2c3d944b,
+    0x2c3e3685,
+    0x2c3eb693,
+    0x2c3f36ab,
+    0x2c3fb6c3,
+    0x2c4036ed,
+    0x2c4092ff,
+    0x2c4136fe,
+    0x2c41b711,
+    0x2c4212c5,
+    0x2c42b722,
     0x2c43076d,
-    0x2c43b5f5,
-    0x2c44353d,
-    0x2c44b6bd,
-    0x2c4534d4,
-    0x2c45b510,
-    0x2c46358d,
-    0x2c46b617,
-    0x2c47362c,
-    0x2c47b665,
-    0x2c48354f,
+    0x2c43b608,
+    0x2c443550,
+    0x2c44b6d0,
+    0x2c4534e7,
+    0x2c45b523,
+    0x2c4635a0,
+    0x2c46b62a,
+    0x2c47363f,
+    0x2c47b678,
+    0x2c483562,
     0x30320000,
     0x30328015,
     0x3033001f,
@@ -386,271 +387,271 @@
     0x3c418e13,
     0x3c420f1f,
     0x3c428ea9,
-    0x40321a8e,
-    0x40329aa4,
-    0x40331ad2,
-    0x40339adc,
-    0x40341af3,
-    0x40349b11,
-    0x40351b21,
-    0x40359b33,
-    0x40361b40,
-    0x40369b4c,
-    0x40371b61,
-    0x40379b73,
-    0x40381b7e,
-    0x40389b90,
+    0x40321aa1,
+    0x40329ab7,
+    0x40331ae5,
+    0x40339aef,
+    0x40341b06,
+    0x40349b24,
+    0x40351b34,
+    0x40359b46,
+    0x40361b53,
+    0x40369b5f,
+    0x40371b74,
+    0x40379b86,
+    0x40381b91,
+    0x40389ba3,
     0x40390fd3,
-    0x40399ba0,
-    0x403a1bb3,
-    0x403a9bd4,
-    0x403b1be5,
-    0x403b9bf5,
+    0x40399bb3,
+    0x403a1bc6,
+    0x403a9be7,
+    0x403b1bf8,
+    0x403b9c08,
     0x403c0071,
     0x403c8090,
-    0x403d1c56,
-    0x403d9c6c,
-    0x403e1c7b,
-    0x403e9cb3,
-    0x403f1ccd,
-    0x403f9cf5,
-    0x40401d0a,
-    0x40409d1e,
-    0x40411d59,
-    0x40419d74,
-    0x40421d8d,
-    0x40429da0,
-    0x40431db4,
-    0x40439de2,
-    0x40441df9,
+    0x403d1c69,
+    0x403d9c7f,
+    0x403e1c8e,
+    0x403e9cc6,
+    0x403f1ce0,
+    0x403f9d08,
+    0x40401d1d,
+    0x40409d31,
+    0x40411d6c,
+    0x40419d87,
+    0x40421da0,
+    0x40429db3,
+    0x40431dc7,
+    0x40439df5,
+    0x40441e0c,
     0x404480b9,
-    0x40451e0e,
-    0x40459e20,
-    0x40461e44,
-    0x40469e64,
-    0x40471e72,
-    0x40479e99,
-    0x40481f1a,
-    0x40489fd4,
-    0x40491feb,
-    0x4049a005,
-    0x404a201c,
-    0x404aa03a,
-    0x404b2052,
-    0x404ba07f,
-    0x404c2095,
-    0x404ca0a7,
-    0x404d20c8,
-    0x404da101,
-    0x404e2115,
-    0x404ea122,
-    0x404f21f5,
-    0x404fa26b,
-    0x40502310,
-    0x4050a324,
-    0x40512371,
-    0x40522381,
-    0x4052a3a5,
-    0x405323bd,
-    0x4053a3d0,
-    0x405423e5,
-    0x4054a408,
-    0x40552433,
-    0x4055a470,
-    0x40562495,
-    0x4056a4ae,
-    0x405724c6,
-    0x4057a4d9,
-    0x405824ee,
-    0x4058a515,
-    0x40592544,
-    0x4059a584,
-    0x405aa598,
-    0x405b25b0,
-    0x405ba5c1,
-    0x405c25d4,
-    0x405ca629,
-    0x405d2636,
-    0x405da65b,
-    0x405e2699,
+    0x40451e21,
+    0x40459e33,
+    0x40461e57,
+    0x40469e77,
+    0x40471e85,
+    0x40479eac,
+    0x40481f2d,
+    0x40489fe7,
+    0x40491ffe,
+    0x4049a018,
+    0x404a202f,
+    0x404aa04d,
+    0x404b2065,
+    0x404ba092,
+    0x404c20a8,
+    0x404ca0ba,
+    0x404d20db,
+    0x404da114,
+    0x404e2128,
+    0x404ea135,
+    0x404f2208,
+    0x404fa27e,
+    0x40502323,
+    0x4050a337,
+    0x40512384,
+    0x40522394,
+    0x4052a3b8,
+    0x405323d0,
+    0x4053a3e3,
+    0x405423f8,
+    0x4054a41b,
+    0x40552446,
+    0x4055a483,
+    0x405624a8,
+    0x4056a4c1,
+    0x405724d9,
+    0x4057a4ec,
+    0x40582501,
+    0x4058a528,
+    0x40592557,
+    0x4059a597,
+    0x405aa5ab,
+    0x405b25c3,
+    0x405ba5d4,
+    0x405c25e7,
+    0x405ca63c,
+    0x405d2649,
+    0x405da66e,
+    0x405e26ac,
     0x405e8afe,
-    0x405f26e8,
-    0x405fa6f5,
-    0x40602703,
-    0x4060a725,
-    0x40612799,
-    0x4061a7d1,
-    0x406227e8,
-    0x4062a7f9,
-    0x40632846,
-    0x4063a85b,
-    0x40642872,
-    0x4064a89e,
-    0x406528b9,
-    0x4065a8d0,
-    0x406628e8,
-    0x4066a912,
-    0x4067293d,
-    0x4067a982,
-    0x406829ca,
-    0x4068a9eb,
-    0x40692a1d,
-    0x4069aa4b,
-    0x406a2a6c,
-    0x406aaa8c,
-    0x406b2c14,
-    0x406bac37,
-    0x406c2c4d,
-    0x406caf57,
-    0x406d2f86,
-    0x406dafae,
-    0x406e2fdc,
-    0x406eb029,
-    0x406f3082,
-    0x406fb0ba,
-    0x407030cd,
-    0x4070b0ea,
+    0x405f26fb,
+    0x405fa708,
+    0x40602716,
+    0x4060a738,
+    0x406127ac,
+    0x4061a7e4,
+    0x406227fb,
+    0x4062a80c,
+    0x40632859,
+    0x4063a86e,
+    0x40642885,
+    0x4064a8b1,
+    0x406528cc,
+    0x4065a8e3,
+    0x406628fb,
+    0x4066a925,
+    0x40672950,
+    0x4067a995,
+    0x406829dd,
+    0x4068a9fe,
+    0x40692a30,
+    0x4069aa5e,
+    0x406a2a7f,
+    0x406aaa9f,
+    0x406b2c27,
+    0x406bac4a,
+    0x406c2c60,
+    0x406caf6a,
+    0x406d2f99,
+    0x406dafc1,
+    0x406e2fef,
+    0x406eb03c,
+    0x406f3095,
+    0x406fb0cd,
+    0x407030e0,
+    0x4070b0fd,
     0x4071084d,
-    0x4071b0fc,
-    0x4072310f,
-    0x4072b145,
-    0x4073315d,
-    0x40739621,
-    0x40743171,
-    0x4074b18b,
-    0x4075319c,
-    0x4075b1b0,
-    0x407631be,
-    0x407693af,
-    0x407731e3,
-    0x4077b23f,
-    0x4078325a,
-    0x4078b293,
-    0x407932aa,
-    0x4079b2c0,
-    0x407a32ec,
-    0x407ab2ff,
-    0x407b3314,
-    0x407bb326,
-    0x407c3357,
-    0x407cb360,
-    0x407d2a06,
-    0x407da293,
-    0x407e326f,
-    0x407ea525,
-    0x407f1e86,
-    0x407fa069,
-    0x40802205,
-    0x40809eae,
-    0x40812393,
-    0x4081a170,
-    0x40822fc7,
-    0x40829c01,
-    0x40832500,
-    0x4083a883,
-    0x40841ed2,
-    0x4084a55d,
-    0x408525e5,
-    0x4085a760,
-    0x4086267b,
-    0x4086a2c8,
-    0x4087300d,
-    0x4087a7ae,
-    0x40881c3f,
-    0x4088a995,
-    0x40891c8e,
-    0x40899c1b,
-    0x408a2c85,
-    0x408a9a39,
-    0x408b333b,
-    0x408bb097,
-    0x408c260b,
-    0x408d1fba,
-    0x408d9f04,
-    0x408e20ea,
-    0x408ea450,
-    0x408f29a9,
-    0x408fa77c,
-    0x4090295e,
-    0x4090a64d,
-    0x40912c6d,
-    0x40919a71,
-    0x40921cdb,
-    0x4092b048,
-    0x40933128,
-    0x4093a2d9,
-    0x40941ee6,
-    0x4094ac9e,
-    0x4095280a,
-    0x4095b2cc,
-    0x40962ff4,
-    0x4096a21e,
-    0x40972359,
-    0x4097a139,
-    0x40981d3b,
-    0x4098a81e,
-    0x40993064,
-    0x4099a47d,
-    0x409a2416,
-    0x409a9a55,
-    0x409b1f40,
-    0x409b9f6b,
-    0x409c3221,
-    0x409c9f93,
-    0x409d21da,
-    0x409da186,
-    0x409e1dcc,
-    0x409ea253,
-    0x409f223b,
-    0x409f9f33,
-    0x40a0227b,
-    0x40a0a153,
-    0x40a121a1,
-    0x40a1a571,
-    0x40a222f5,
-    0x40a2a6d9,
-    0x40a3274d,
-    0x40a3b205,
-    0x40a4233f,
-    0x40a4a1b8,
-    0x40a51ec2,
-    0x40a5a2ad,
-    0x40a625f5,
-    0x41f42b3f,
-    0x41f92bd1,
-    0x41fe2ac4,
-    0x41fead7a,
-    0x41ff2ea8,
-    0x42032b58,
-    0x42082b7a,
-    0x4208abb6,
-    0x42092aa8,
-    0x4209abf0,
-    0x420a2aff,
-    0x420aaadf,
-    0x420b2b1f,
-    0x420bab98,
-    0x420c2ec4,
-    0x420cacae,
-    0x420d2d61,
-    0x420dad98,
-    0x42122dcb,
-    0x42172e8b,
-    0x4217ae0d,
-    0x421c2e2f,
-    0x421f2dea,
-    0x42212f3c,
-    0x42262e6e,
-    0x422b2f1a,
-    0x422bad3c,
-    0x422c2efc,
-    0x422cacef,
-    0x422d2cc8,
-    0x422daedb,
-    0x422e2d1b,
-    0x42302e4a,
-    0x4230adb2,
-    0x423126ba,
+    0x4071b10f,
+    0x40723122,
+    0x4072b158,
+    0x40733170,
+    0x40739634,
+    0x40743184,
+    0x4074b19e,
+    0x407531af,
+    0x4075b1c3,
+    0x407631d1,
+    0x407693c2,
+    0x407731f6,
+    0x4077b252,
+    0x4078326d,
+    0x4078b2a6,
+    0x407932bd,
+    0x4079b2d3,
+    0x407a32ff,
+    0x407ab312,
+    0x407b3327,
+    0x407bb339,
+    0x407c336a,
+    0x407cb373,
+    0x407d2a19,
+    0x407da2a6,
+    0x407e3282,
+    0x407ea538,
+    0x407f1e99,
+    0x407fa07c,
+    0x40802218,
+    0x40809ec1,
+    0x408123a6,
+    0x4081a183,
+    0x40822fda,
+    0x40829c14,
+    0x40832513,
+    0x4083a896,
+    0x40841ee5,
+    0x4084a570,
+    0x408525f8,
+    0x4085a773,
+    0x4086268e,
+    0x4086a2db,
+    0x40873020,
+    0x4087a7c1,
+    0x40881c52,
+    0x4088a9a8,
+    0x40891ca1,
+    0x40899c2e,
+    0x408a2c98,
+    0x408a9a4c,
+    0x408b334e,
+    0x408bb0aa,
+    0x408c261e,
+    0x408d1fcd,
+    0x408d9f17,
+    0x408e20fd,
+    0x408ea463,
+    0x408f29bc,
+    0x408fa78f,
+    0x40902971,
+    0x4090a660,
+    0x40912c80,
+    0x40919a84,
+    0x40921cee,
+    0x4092b05b,
+    0x4093313b,
+    0x4093a2ec,
+    0x40941ef9,
+    0x4094acb1,
+    0x4095281d,
+    0x4095b2df,
+    0x40963007,
+    0x4096a231,
+    0x4097236c,
+    0x4097a14c,
+    0x40981d4e,
+    0x4098a831,
+    0x40993077,
+    0x4099a490,
+    0x409a2429,
+    0x409a9a68,
+    0x409b1f53,
+    0x409b9f7e,
+    0x409c3234,
+    0x409c9fa6,
+    0x409d21ed,
+    0x409da199,
+    0x409e1ddf,
+    0x409ea266,
+    0x409f224e,
+    0x409f9f46,
+    0x40a0228e,
+    0x40a0a166,
+    0x40a121b4,
+    0x40a1a584,
+    0x40a22308,
+    0x40a2a6ec,
+    0x40a32760,
+    0x40a3b218,
+    0x40a42352,
+    0x40a4a1cb,
+    0x40a51ed5,
+    0x40a5a2c0,
+    0x40a62608,
+    0x41f42b52,
+    0x41f92be4,
+    0x41fe2ad7,
+    0x41fead8d,
+    0x41ff2ebb,
+    0x42032b6b,
+    0x42082b8d,
+    0x4208abc9,
+    0x42092abb,
+    0x4209ac03,
+    0x420a2b12,
+    0x420aaaf2,
+    0x420b2b32,
+    0x420babab,
+    0x420c2ed7,
+    0x420cacc1,
+    0x420d2d74,
+    0x420dadab,
+    0x42122dde,
+    0x42172e9e,
+    0x4217ae20,
+    0x421c2e42,
+    0x421f2dfd,
+    0x42212f4f,
+    0x42262e81,
+    0x422b2f2d,
+    0x422bad4f,
+    0x422c2f0f,
+    0x422cad02,
+    0x422d2cdb,
+    0x422daeee,
+    0x422e2d2e,
+    0x42302e5d,
+    0x4230adc5,
+    0x423126cd,
     0x44320778,
     0x44328787,
     0x44330793,
@@ -668,109 +669,109 @@
     0x4439084d,
     0x4439885b,
     0x443a086e,
-    0x483213f7,
-    0x48329409,
-    0x4833141f,
-    0x48339438,
-    0x4c321475,
-    0x4c329485,
-    0x4c331498,
-    0x4c3394b8,
+    0x4832140a,
+    0x4832941c,
+    0x48331432,
+    0x4833944b,
+    0x4c321488,
+    0x4c329498,
+    0x4c3314ab,
+    0x4c3394cb,
     0x4c3400b9,
     0x4c3480f7,
-    0x4c3514c4,
-    0x4c3594d2,
-    0x4c3614ee,
-    0x4c369514,
-    0x4c371523,
-    0x4c379531,
-    0x4c381546,
-    0x4c389552,
-    0x4c391572,
-    0x4c39959c,
-    0x4c3a15b5,
-    0x4c3a95ce,
+    0x4c3514d7,
+    0x4c3594e5,
+    0x4c361501,
+    0x4c369527,
+    0x4c371536,
+    0x4c379544,
+    0x4c381559,
+    0x4c389565,
+    0x4c391585,
+    0x4c3995af,
+    0x4c3a15c8,
+    0x4c3a95e1,
     0x4c3b0635,
-    0x4c3b95e7,
-    0x4c3c15f9,
-    0x4c3c9608,
-    0x4c3d1621,
+    0x4c3b95fa,
+    0x4c3c160c,
+    0x4c3c961b,
+    0x4c3d1634,
     0x4c3d8d06,
-    0x4c3e168e,
-    0x4c3e9630,
-    0x4c3f16b0,
-    0x4c3f93af,
-    0x4c401646,
-    0x4c409461,
-    0x4c41167e,
-    0x4c419501,
-    0x4c42166a,
-    0x4c429449,
-    0x50323721,
-    0x5032b730,
-    0x5033373b,
-    0x5033b74b,
-    0x50343764,
-    0x5034b77e,
-    0x5035378c,
-    0x5035b7a2,
-    0x503637b4,
-    0x5036b7ca,
-    0x503737e3,
-    0x5037b7f6,
-    0x5038380e,
-    0x5038b81f,
-    0x50393834,
-    0x5039b848,
-    0x503a3868,
-    0x503ab87e,
-    0x503b3896,
-    0x503bb8a8,
-    0x503c38c4,
-    0x503cb8db,
-    0x503d38f4,
-    0x503db90a,
-    0x503e3917,
-    0x503eb92d,
-    0x503f393f,
+    0x4c3e16a1,
+    0x4c3e9643,
+    0x4c3f16c3,
+    0x4c3f93c2,
+    0x4c401659,
+    0x4c409474,
+    0x4c411691,
+    0x4c419514,
+    0x4c42167d,
+    0x4c42945c,
+    0x50323734,
+    0x5032b743,
+    0x5033374e,
+    0x5033b75e,
+    0x50343777,
+    0x5034b791,
+    0x5035379f,
+    0x5035b7b5,
+    0x503637c7,
+    0x5036b7dd,
+    0x503737f6,
+    0x5037b809,
+    0x50383821,
+    0x5038b832,
+    0x50393847,
+    0x5039b85b,
+    0x503a387b,
+    0x503ab891,
+    0x503b38a9,
+    0x503bb8bb,
+    0x503c38d7,
+    0x503cb8ee,
+    0x503d3907,
+    0x503db91d,
+    0x503e392a,
+    0x503eb940,
+    0x503f3952,
     0x503f83b3,
-    0x50403952,
-    0x5040b962,
-    0x5041397c,
-    0x5041b98b,
-    0x504239a5,
-    0x5042b9c2,
-    0x504339d2,
-    0x5043b9e2,
-    0x504439ff,
+    0x50403965,
+    0x5040b975,
+    0x5041398f,
+    0x5041b99e,
+    0x504239b8,
+    0x5042b9d5,
+    0x504339e5,
+    0x5043b9f5,
+    0x50443a12,
     0x50448469,
-    0x50453a13,
-    0x5045ba31,
-    0x50463a44,
-    0x5046ba5a,
-    0x50473a6c,
-    0x5047ba81,
-    0x50483aa7,
-    0x5048bab5,
-    0x50493ac8,
-    0x5049badd,
-    0x504a3af3,
-    0x504abb03,
-    0x504b3b23,
-    0x504bbb36,
-    0x504c3b59,
-    0x504cbb87,
-    0x504d3bb4,
-    0x504dbbd1,
-    0x504e3bec,
-    0x504ebc08,
-    0x504f3c1a,
-    0x504fbc31,
-    0x50503c40,
+    0x50453a26,
+    0x5045ba44,
+    0x50463a57,
+    0x5046ba6d,
+    0x50473a7f,
+    0x5047ba94,
+    0x50483aba,
+    0x5048bac8,
+    0x50493adb,
+    0x5049baf0,
+    0x504a3b06,
+    0x504abb16,
+    0x504b3b36,
+    0x504bbb49,
+    0x504c3b6c,
+    0x504cbb9a,
+    0x504d3bc7,
+    0x504dbbe4,
+    0x504e3bff,
+    0x504ebc1b,
+    0x504f3c2d,
+    0x504fbc44,
+    0x50503c53,
     0x50508729,
-    0x50513c53,
-    0x5051b9f1,
-    0x50523b99,
+    0x50513c66,
+    0x5051ba04,
+    0x50523bac,
     0x58321011,
     0x68320fd3,
     0x68328d2b,
@@ -812,22 +813,22 @@
     0x783d8b97,
     0x783e0aed,
     0x783e8a9f,
-    0x7c3212c8,
-    0x80321514,
+    0x7c3212db,
+    0x80321527,
     0x80328090,
-    0x8033341d,
+    0x80333430,
     0x803380b9,
-    0x8034342c,
-    0x8034b394,
-    0x803533b2,
-    0x8035b440,
-    0x803633f4,
-    0x8036b3a3,
-    0x803733e6,
-    0x8037b381,
-    0x80383407,
-    0x8038b3c3,
-    0x803933d8,
+    0x8034343f,
+    0x8034b3a7,
+    0x803533c5,
+    0x8035b453,
+    0x80363407,
+    0x8036b3b6,
+    0x803733f9,
+    0x8037b394,
+    0x8038341a,
+    0x8038b3d6,
+    0x803933eb,
     0x84320bb0,
     0x84328bc9,
 };
@@ -1071,6 +1072,7 @@
     "INVALID_SIGNATURE\0"
     "KEYS_NOT_SET\0"
     "MEMORY_LIMIT_EXCEEDED\0"
+    "MISSING_PUBLIC_KEY\0"
     "NOT_A_PRIVATE_KEY\0"
     "NOT_XOF_OR_INVALID_LENGTH\0"
     "NO_DEFAULT_DIGEST\0"
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index 91ca8e6..8dee117 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -102,6 +102,11 @@
 // otherwise.
 OPENSSL_EXPORT int EVP_PKEY_has_private(const EVP_PKEY *pkey);
 
+// EVP_PKEY_copy_public returns a newly-allocated |EVP_PKEY| that contains only
+// the public key of |pkey|, or NULL on error. Parameters, if relevant for the
+// key type, are also copied.
+OPENSSL_EXPORT EVP_PKEY *EVP_PKEY_copy_public(const EVP_PKEY *pkey);
+
 // The following constants are returned by |EVP_PKEY_id| and specify the type of
 // key.
 #define EVP_PKEY_NONE NID_undef
diff --git a/include/openssl/evp_errors.h b/include/openssl/evp_errors.h
index 4cccdb8..cc1c98c 100644
--- a/include/openssl/evp_errors.h
+++ b/include/openssl/evp_errors.h
@@ -55,5 +55,6 @@
 #define EVP_R_INVALID_BUFFER_SIZE 137
 #define EVP_R_EXPECTING_A_DH_KEY 138
 #define EVP_R_PRIVATE_KEY_WAS_NOT_SEED 139
+#define EVP_R_MISSING_PUBLIC_KEY 140
 
 #endif  // OPENSSL_HEADER_EVP_ERRORS_H
diff --git a/include/openssl/prefix_symbols.h b/include/openssl/prefix_symbols.h
index 377f868..4988bdd 100644
--- a/include/openssl/prefix_symbols.h
+++ b/include/openssl/prefix_symbols.h
@@ -1125,6 +1125,7 @@
 #pragma redefine_extname EVP_PKEY_cmp BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_cmp))
 #pragma redefine_extname EVP_PKEY_cmp_parameters BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_cmp_parameters))
 #pragma redefine_extname EVP_PKEY_copy_parameters BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_copy_parameters))
+#pragma redefine_extname EVP_PKEY_copy_public BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_copy_public))
 #pragma redefine_extname EVP_PKEY_decrypt BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_decrypt))
 #pragma redefine_extname EVP_PKEY_decrypt_init BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_decrypt_init))
 #pragma redefine_extname EVP_PKEY_derive BORINGSSL_SYMBOL(BORINGSSL_ADD_PREFIX(EVP_PKEY_derive))
@@ -4189,6 +4190,7 @@
 #define EVP_PKEY_cmp BORINGSSL_ADD_PREFIX(EVP_PKEY_cmp)
 #define EVP_PKEY_cmp_parameters BORINGSSL_ADD_PREFIX(EVP_PKEY_cmp_parameters)
 #define EVP_PKEY_copy_parameters BORINGSSL_ADD_PREFIX(EVP_PKEY_copy_parameters)
+#define EVP_PKEY_copy_public BORINGSSL_ADD_PREFIX(EVP_PKEY_copy_public)
 #define EVP_PKEY_decrypt BORINGSSL_ADD_PREFIX(EVP_PKEY_decrypt)
 #define EVP_PKEY_decrypt_init BORINGSSL_ADD_PREFIX(EVP_PKEY_decrypt_init)
 #define EVP_PKEY_derive BORINGSSL_ADD_PREFIX(EVP_PKEY_derive)