Deprecate EVP_PKEY_set_type This function is practically useless. It makes a typed but empty key, which is somehow distinct from an untyped and empty key. Looking for callers within our tests and externally, the patterns are: - EVP_PKEY_set_type(EVP_PKEY_X25519) followed by EVP_PKEY_set1_tls_encodedpoint. This is regrettably needed for that API to work, but we've also marked EVP_PKEY_set1_tls_encodedpoint as deprecated, I think because of this weird half-empty state. Sadly this means we can never totally remove this, and we're stuck with some of these half-empty states. - EVP_PKEY_set_type(EVP_PKEY_NONE). This is used in some tests by callers who try to test their "unknown type" codepaths. The usual pattern is they load a key and then set the type to EVP_PKEY_NONE. The result of this is an empty EVP_PKEY, identical to just calling EVP_PKEY_new. Loading the key was never useful. It's also an error! The function fails because EVP_PKEY_NONE is not a supported type. Callers were relying on a quirk that we clear the key before noticing the parameters are wrong. Write a test and update the comment so we don't forget this behavior is depended on. (Though I'm going to try to fix everyone relying on this.) - EVP_PKEY_set_type(EVP_PKEY_RSA), etc. This does not produce a valid key. It's done in some test code to avoid loading a real key. Our own tests do this too, though it's also to test that EVP_PKEY_missing_parameters works for this weird empty state, that ideally wouldn't be possible in the first place. The immediate motivation is that the work to make EVP more static-linker-friendly collides a bit with EVP_PKEY_set_type(EVP_PKEY_EC). If we're trying to treat P-256 and P-384 as distinct EVP_PKEY types, it's unclear how that operation is supposed to work. Bug: 42290409 Change-Id: Id4368e049abea698f0eaed1419cb366a5fe3a424 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81269 Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Lily Chen <chlily@google.com> Commit-Queue: Lily Chen <chlily@google.com> Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/evp/evp.cc b/crypto/evp/evp.cc index 82a98dd..806c34c 100644 --- a/crypto/evp/evp.cc +++ b/crypto/evp/evp.cc
@@ -210,9 +210,8 @@ int EVP_PKEY_set_type(EVP_PKEY *pkey, int type) { 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. + // Some callers rely on |pkey| getting cleared even if |type| is + // unsupported, usually setting |type| to |EVP_PKEY_NONE|. free_it(pkey); }
diff --git a/crypto/evp/evp_extra_test.cc b/crypto/evp/evp_extra_test.cc index eac46e8..f39f493 100644 --- a/crypto/evp/evp_extra_test.cc +++ b/crypto/evp/evp_extra_test.cc
@@ -1325,3 +1325,16 @@ ASSERT_TRUE(EVP_PKEY_CTX_get_rsa_pss_saltlen(pctx, &salt_len)); EXPECT_EQ(salt_len, RSA_PSS_SALTLEN_DIGEST); } + +// Test that setting a key to type none will clear it. Some calling code has +// unit tests that rely on this, usually as part of a roundabout way to get a +// key of the "wrong" type to test with. (In reality, |EVP_PKEY_new| will +// produce the same object.) +TEST(EVPExtraTest, SetNoneClearsKey) { + bssl::UniquePtr<EVP_PKEY> pkey = LoadExampleRSAKey(); + ASSERT_TRUE(pkey); + // EVP_PKEY_NONE is not a known type, so this should fail. + EXPECT_FALSE(EVP_PKEY_set_type(pkey.get(), EVP_PKEY_NONE)); + // However, it still resets the key to the initial state. + EXPECT_EQ(EVP_PKEY_id(pkey.get()), EVP_PKEY_NONE); +}
diff --git a/include/openssl/evp.h b/include/openssl/evp.h index 13bf55e..805aed6 100644 --- a/include/openssl/evp.h +++ b/include/openssl/evp.h
@@ -139,11 +139,6 @@ #define EVP_PKEY_HKDF NID_hkdf #define EVP_PKEY_DH NID_dhKeyAgreement -// 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. -OPENSSL_EXPORT int EVP_PKEY_set_type(EVP_PKEY *pkey, int type); - // EVP_PKEY_cmp_parameters compares the parameters of |a| and |b|. It returns // one if they match, zero if not, or a negative number of on error. // @@ -903,6 +898,23 @@ OPENSSL_EXPORT int EVP_PKEY_CTX_set_ec_param_enc(EVP_PKEY_CTX *ctx, int encoding); +// 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. +// +// There are very few cases where this function is useful. Changing |pkey|'s +// type clears any previously stored keys, so there is no benefit to loading a +// key and then changing its type. Although |pkey| is left with a type +// configured, it has no key, and functions which set a key, such as +// |EVP_PKEY_set1_RSA|, will configure a type anyway. If writing unit tests that +// are only sensitive to the type of a key, it is preferable to construct a real +// key, so that tests are more representative of production code. +// +// The only API pattern which requires this function is +// |EVP_PKEY_set1_tls_encodedpoint| with X25519, which requires a half-empty +// |EVP_PKEY| that was first configured with |EVP_PKEY_X25519|. +OPENSSL_EXPORT int EVP_PKEY_set_type(EVP_PKEY *pkey, int type); + // EVP_PKEY_set1_tls_encodedpoint replaces |pkey| with a public key encoded by // |in|. It returns one on success and zero on error. //