Don't expose EVP_PKEY internal representation through EVP_PKEY_assign

While EVP_PKEY_RSA, EVP_PKEY_DSA, and EVP_PKEY_EC have publicly-exposed
internaly representations, other EVP_PKEY types to not. EVP_PKEY_assign
should not allow callers to manipulate those representations.

As part of this, teach EVP_PKEY_assign_RSA, etc. to find their method
tables directly, rather than indirecting through an integer. This makes
those EVP APIs static-linker-friendly.

Bug: 618, 497
Change-Id: Ic45a7514e9a3adc505759f2327129f13faf03a65
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60645
Auto-Submit: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
Commit-Queue: Bob Beck <bbe@google.com>
diff --git a/crypto/evp/evp.c b/crypto/evp/evp.c
index 8383d2e..37b3631 100644
--- a/crypto/evp/evp.c
+++ b/crypto/evp/evp.c
@@ -229,6 +229,13 @@
   }
 }
 
+static void evp_pkey_set_method(EVP_PKEY *pkey,
+                                const EVP_PKEY_ASN1_METHOD *method) {
+  free_it(pkey);
+  pkey->ameth = method;
+  pkey->type = pkey->ameth->pkey_id;
+}
+
 int EVP_PKEY_type(int nid) {
   const EVP_PKEY_ASN1_METHOD *meth = evp_pkey_asn1_find(nid);
   if (meth == NULL) {
@@ -246,7 +253,9 @@
 }
 
 int EVP_PKEY_assign_RSA(EVP_PKEY *pkey, RSA *key) {
-  return EVP_PKEY_assign(pkey, EVP_PKEY_RSA, key);
+  evp_pkey_set_method(pkey, &rsa_asn1_meth);
+  pkey->pkey = key;
+  return key != NULL;
 }
 
 RSA *EVP_PKEY_get0_RSA(const EVP_PKEY *pkey) {
@@ -274,7 +283,9 @@
 }
 
 int EVP_PKEY_assign_DSA(EVP_PKEY *pkey, DSA *key) {
-  return EVP_PKEY_assign(pkey, EVP_PKEY_DSA, key);
+  evp_pkey_set_method(pkey, &dsa_asn1_meth);
+  pkey->pkey = key;
+  return key != NULL;
 }
 
 DSA *EVP_PKEY_get0_DSA(const EVP_PKEY *pkey) {
@@ -302,7 +313,9 @@
 }
 
 int EVP_PKEY_assign_EC_KEY(EVP_PKEY *pkey, EC_KEY *key) {
-  return EVP_PKEY_assign(pkey, EVP_PKEY_EC, key);
+  evp_pkey_set_method(pkey, &ec_asn1_meth);
+  pkey->pkey = key;
+  return key != NULL;
 }
 
 EC_KEY *EVP_PKEY_get0_EC_KEY(const EVP_PKEY *pkey) {
@@ -325,21 +338,32 @@
 DH *EVP_PKEY_get1_DH(const EVP_PKEY *pkey) { return NULL; }
 
 int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key) {
-  if (!EVP_PKEY_set_type(pkey, type)) {
-    return 0;
+  // This function can only be used to assign RSA, DSA, and EC keys. Other key
+  // types have internal representations which are not exposed through the
+  // public API.
+  switch (type) {
+    case EVP_PKEY_RSA:
+      return EVP_PKEY_assign_RSA(pkey, key);
+    case EVP_PKEY_DSA:
+      return EVP_PKEY_assign_DSA(pkey, key);
+    case EVP_PKEY_EC:
+      return EVP_PKEY_assign_EC_KEY(pkey, key);
   }
-  pkey->pkey = key;
-  return key != NULL;
+
+  OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
+  ERR_add_error_dataf("algorithm %d", type);
+  return 0;
 }
 
 int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) {
-  const EVP_PKEY_ASN1_METHOD *ameth;
-
   if (pkey && pkey->pkey) {
+    // This isn't strictly necessary, but historically |EVP_PKEY_set_type| would
+    // clear |pkey| even if |evp_pkey_asn1_find| failed, so we preserve that
+    // behavior.
     free_it(pkey);
   }
 
-  ameth = evp_pkey_asn1_find(type);
+  const EVP_PKEY_ASN1_METHOD *ameth = evp_pkey_asn1_find(type);
   if (ameth == NULL) {
     OPENSSL_PUT_ERROR(EVP, EVP_R_UNSUPPORTED_ALGORITHM);
     ERR_add_error_dataf("algorithm %d", type);
@@ -347,8 +371,7 @@
   }
 
   if (pkey) {
-    pkey->ameth = ameth;
-    pkey->type = pkey->ameth->pkey_id;
+    evp_pkey_set_method(pkey, ameth);
   }
 
   return 1;
diff --git a/include/openssl/evp.h b/include/openssl/evp.h
index f69cf75..1cdaca2 100644
--- a/include/openssl/evp.h
+++ b/include/openssl/evp.h
@@ -180,11 +180,6 @@
 #define EVP_PKEY_X25519 NID_X25519
 #define EVP_PKEY_HKDF NID_hkdf
 
-// EVP_PKEY_assign sets the underlying key of |pkey| to |key|, which must be of
-// the given type. It returns one if successful or zero if the |type| argument
-// is not one of the |EVP_PKEY_*| values or if |key| is NULL.
-OPENSSL_EXPORT int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
-
 // EVP_PKEY_set_type sets the type of |pkey| to |type|. It returns one if
 // successful or zero if the |type| argument is not one of the |EVP_PKEY_*|
 // values. If |pkey| is NULL, it simply reports whether the type is known.
@@ -1032,6 +1027,15 @@
 OPENSSL_EXPORT int EVP_PKEY_CTX_set_dsa_paramgen_q_bits(EVP_PKEY_CTX *ctx,
                                                         int qbits);
 
+// EVP_PKEY_assign sets the underlying key of |pkey| to |key|, which must be of
+// the given type. If successful, it returns one. If the |type| argument
+// is not one of |EVP_PKEY_RSA|, |EVP_PKEY_DSA|, or |EVP_PKEY_EC| values or if
+// |key| is NULL, it returns zero. This function may not be used with other
+// |EVP_PKEY_*| types.
+//
+// Use the |EVP_PKEY_assign_*| functions instead.
+OPENSSL_EXPORT int EVP_PKEY_assign(EVP_PKEY *pkey, int type, void *key);
+
 
 // Preprocessor compatibility section (hidden).
 //