Fix X509_PUBKEY_set0_param to clear the cached EVP_PKEY

Change-Id: I661c1284ba23138074339aec712ee6ba86905518
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65048
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/x509/x509_test.cc b/crypto/x509/x509_test.cc
index 8b9cd64..7d46a9d 100644
--- a/crypto/x509/x509_test.cc
+++ b/crypto/x509/x509_test.cc
@@ -6993,3 +6993,24 @@
     EXPECT_EQ(X509_VERIFY_PARAM_get_depth(dest.get()), 10);
   }
 }
+
+TEST(X509Test, PublicKeyCache) {
+  bssl::UniquePtr<EVP_PKEY> key = PrivateKeyFromPEM(kP256Key);
+  ASSERT_TRUE(key);
+
+  X509_PUBKEY *pub = nullptr;
+  ASSERT_TRUE(X509_PUBKEY_set(&pub, key.get()));
+  bssl::UniquePtr<X509_PUBKEY> free_pub(pub);
+
+  bssl::UniquePtr<EVP_PKEY> key2(X509_PUBKEY_get(pub));
+  ASSERT_TRUE(key2);
+  EXPECT_EQ(1, EVP_PKEY_cmp(key.get(), key2.get()));
+
+  // Replace |pub| with different (garbage) values.
+  ASSERT_TRUE(X509_PUBKEY_set0_param(pub, OBJ_nid2obj(NID_subject_alt_name),
+                                     V_ASN1_NULL, nullptr, nullptr, 0));
+
+  // The cached key should no longer be returned.
+  key2.reset(X509_PUBKEY_get(pub));
+  EXPECT_FALSE(key2);
+}
diff --git a/crypto/x509/x_pubkey.c b/crypto/x509/x_pubkey.c
index cd0cfef..67ce464 100644
--- a/crypto/x509/x_pubkey.c
+++ b/crypto/x509/x_pubkey.c
@@ -70,6 +70,15 @@
 #include "../internal.h"
 #include "internal.h"
 
+
+static void x509_pubkey_changed(X509_PUBKEY *pub) {
+  // TODO(davidben): Instead of just dropping the key, also compute the new
+  // cached key. This will let us implement |X509_get0_pubkey| and remove the
+  // need for a mutex.
+  EVP_PKEY_free(pub->pkey);
+  pub->pkey = NULL;
+}
+
 // Minor tweak to operation: free up EVP_PKEY
 static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
                      void *exarg) {
@@ -190,6 +199,8 @@
   // Set the number of unused bits to zero.
   pub->public_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
   pub->public_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+  x509_pubkey_changed(pub);
   return 1;
 }