EVP_PKEY_copy_parameters: Allocate new receiving key if not present

For DSA and DH key types, allocate a new receiving key to copy the
parameters into, if the passed EVP_PKEY doesn't have one (e.g. it starts
empty). This matches code we already had for EC keys.

Change-Id: I433ee847f72d5db59fd644019e34f7206a6a6964
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/93687
Auto-Submit: Lily Chen <chlily@google.com>
Commit-Queue: Lily Chen <chlily@google.com>
Presubmit-BoringSSL-Verified: boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com <boringssl-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/evp/evp_extra_test.cc b/crypto/evp/evp_extra_test.cc
index d78c82e..27a918b 100644
--- a/crypto/evp/evp_extra_test.cc
+++ b/crypto/evp/evp_extra_test.cc
@@ -1255,6 +1255,34 @@
   EXPECT_EQ(EVP_PKEY_RSA, EVP_PKEY_id(rsa.get()));
 }
 
+// Test copying parameters into an empty key. This should result in allocating a
+// key for the receiving key, which did not already have one.
+TEST(EVPExtraTest, CopyParamsIntoEmptyKey) {
+  UniquePtr<EVP_PKEY> dsa = ParsePrivateKey(EVP_PKEY_DSA, kExampleDSAKeyDER);
+  UniquePtr<EVP_PKEY> ec = ParsePrivateKey(EVP_PKEY_EC, kExampleECKeyDER);
+
+  for (const EVP_PKEY* from_pkey : {dsa.get(), ec.get()}) {
+    SCOPED_TRACE(EVP_PKEY_id(from_pkey));
+    ASSERT_TRUE(from_pkey);
+    ASSERT_FALSE(EVP_PKEY_missing_parameters(from_pkey));
+
+    UniquePtr<EVP_PKEY> to_pkey(EVP_PKEY_new());
+    ASSERT_TRUE(to_pkey);
+    ASSERT_EQ(EVP_PKEY_NONE, EVP_PKEY_id(to_pkey.get()));
+    // EVP_PKEY_NONE has no parameters, so the params are not missing.
+    ASSERT_FALSE(EVP_PKEY_missing_parameters(to_pkey.get()));
+    ASSERT_FALSE(EVP_PKEY_has_private(to_pkey.get()));
+    ASSERT_FALSE(EVP_PKEY_has_public(to_pkey.get()));
+
+    EXPECT_TRUE(EVP_PKEY_copy_parameters(to_pkey.get(), from_pkey));
+    EXPECT_EQ(EVP_PKEY_id(to_pkey.get()), EVP_PKEY_id(from_pkey));
+    EXPECT_FALSE(EVP_PKEY_missing_parameters(to_pkey.get()));
+    // Only params are copied, not keys.
+    EXPECT_FALSE(EVP_PKEY_has_private(to_pkey.get()));
+    EXPECT_FALSE(EVP_PKEY_has_public(to_pkey.get()));
+  }
+}
+
 TEST(EVPExtraTest, CompareDifferentTypes) {
   UniquePtr<EVP_PKEY> rsa = ParsePrivateKey(EVP_PKEY_RSA, kExampleRSAKeyDER);
   UniquePtr<EVP_PKEY> dsa = ParsePrivateKey(EVP_PKEY_DSA, kExampleDSAKeyDER);
diff --git a/crypto/evp/p_dh.cc b/crypto/evp/p_dh.cc
index ab62dc7..26c4770 100644
--- a/crypto/evp/p_dh.cc
+++ b/crypto/evp/p_dh.cc
@@ -57,6 +57,12 @@
     OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
     return 0;
   }
+  if (to->pkey == nullptr) {
+    to->pkey = DH_new();
+    if (to->pkey == nullptr) {
+      return 0;
+    }
+  }
 
   const DH *dh = reinterpret_cast<DH *>(from->pkey);
   const BIGNUM *q_old = DH_get0_q(dh);
diff --git a/crypto/evp/p_dsa.cc b/crypto/evp/p_dsa.cc
index 2ea241f..6b6201c 100644
--- a/crypto/evp/p_dsa.cc
+++ b/crypto/evp/p_dsa.cc
@@ -177,6 +177,12 @@
 }
 
 static int dsa_copy_parameters(EvpPkey *to, const EvpPkey *from) {
+  if (to->pkey == nullptr) {
+    to->pkey = DSA_new();
+    if (to->pkey == nullptr) {
+      return 0;
+    }
+  }
   DSAImpl *to_dsa = reinterpret_cast<DSAImpl *>(to->pkey);
   const DSAImpl *from_dsa = reinterpret_cast<const DSAImpl *>(from->pkey);
   if (!dup_bn_into(&to_dsa->p, from_dsa->p.get()) ||