Skip to content

Commit

Permalink
add support for X509_get0_pubkey
Browse files Browse the repository at this point in the history
  • Loading branch information
samuel40791765 committed May 8, 2023
1 parent d1552fa commit 8aca04c
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 21 deletions.
5 changes: 5 additions & 0 deletions crypto/x509/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,11 @@ ASN1_TYPE *ASN1_generate_v3(const char *str, const X509V3_CTX *cnf);

int X509_CERT_AUX_print(BIO *bp, X509_CERT_AUX *x, int indent);

// X509_PUBKEY_get0 decodes the public key in |key| and returns an |EVP_PKEY|
// on success, or NULL on error. It is similar to |X509_PUBKEY_get|, but it
// directly returns the reference to |pkey| of |key|. This means that the
// caller must not free the result after use.
EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key);

// RSA-PSS functions.

Expand Down
7 changes: 7 additions & 0 deletions crypto/x509/x509_cmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,13 @@ X509 *X509_find_by_subject(const STACK_OF(X509) *sk, X509_NAME *name) {
return NULL;
}

EVP_PKEY *X509_get0_pubkey(const X509 *x) {
if ((x == NULL) || (x->cert_info == NULL)) {
return NULL;
}
return (X509_PUBKEY_get0(x->cert_info->key));
}

EVP_PKEY *X509_get_pubkey(X509 *x) {
if ((x == NULL) || (x->cert_info == NULL)) {
return NULL;
Expand Down
17 changes: 17 additions & 0 deletions crypto/x509/x509_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6093,3 +6093,20 @@ TEST(X509Test, AddUnserializableExtension) {
ASSERT_TRUE(X509_EXTENSION_set_object(ext.get(), OBJ_nid2obj(NID_undef)));
EXPECT_FALSE(X509_add_ext(x509.get(), ext.get(), /*loc=*/-1));
}

TEST(X509Test, TestDecode) {
bssl::UniquePtr<X509> cert(CertFromPEM(kExamplePSSCert));
ASSERT_TRUE(cert);

// |X509_get0_pubkey| directly returns a reference to the decoded |pkey|, so
// it can't be freed.
EVP_PKEY *pkey1 = X509_get0_pubkey(cert.get());
ASSERT_TRUE(pkey1);
ASSERT_TRUE(X509_verify(cert.get(), pkey1));

// |X509_get_pubkey| returns the decoded |pkey| with its reference count
// updated, so we must free it.
bssl::UniquePtr<EVP_PKEY> pkey2(X509_get_pubkey(cert.get()));
ASSERT_TRUE(pkey2);
ASSERT_TRUE(X509_verify(cert.get(), pkey2.get()));
}
58 changes: 37 additions & 21 deletions crypto/x509/x_pubkey.c
Original file line number Diff line number Diff line change
Expand Up @@ -130,31 +130,27 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) {
// not.
static struct CRYPTO_STATIC_MUTEX g_pubkey_lock = CRYPTO_STATIC_MUTEX_INIT;

EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) {
EVP_PKEY *ret = NULL;
uint8_t *spki = NULL;

if (key == NULL) {
goto error;
}

// x509_pubkey_decode decodes |key| into |pkey|. It returns one on success and
// zero on error.
static int x509_pubkey_decode(EVP_PKEY **pkey, X509_PUBKEY *key) {
CRYPTO_STATIC_MUTEX_lock_read(&g_pubkey_lock);
if (key->pkey != NULL) {
CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);
EVP_PKEY_up_ref(key->pkey);
return key->pkey;
*pkey = key->pkey;
return 1;
}
CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);

uint8_t *spki = NULL;
// Re-encode the |X509_PUBKEY| to DER and parse it.
int spki_len = i2d_X509_PUBKEY(key, &spki);
if (spki_len < 0) {
goto error;
}
CBS cbs;
CBS_init(&cbs, spki, (size_t)spki_len);
ret = EVP_parse_public_key(&cbs);
if (ret == NULL || CBS_len(&cbs) != 0) {
*pkey = EVP_parse_public_key(&cbs);
if (*pkey == NULL || CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
goto error;
}
Expand All @@ -163,21 +159,41 @@ EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) {
CRYPTO_STATIC_MUTEX_lock_write(&g_pubkey_lock);
if (key->pkey) {
CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
EVP_PKEY_free(ret);
ret = key->pkey;
EVP_PKEY_free(*pkey);
*pkey = key->pkey;
} else {
key->pkey = ret;
key->pkey = *pkey;
CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
}

OPENSSL_free(spki);
EVP_PKEY_up_ref(ret);
return ret;

return 1;
error:
OPENSSL_free(spki);
EVP_PKEY_free(ret);
return NULL;
return 0;
}

EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) {
if (key == NULL) {
OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
return NULL;
}

EVP_PKEY *ret = NULL;
if (!x509_pubkey_decode(&ret, key)) {
EVP_PKEY_free(ret);
return NULL;
};
return ret;
}

EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) {
EVP_PKEY *ret = X509_PUBKEY_get0(key);

if (ret != NULL && !EVP_PKEY_up_ref(ret)) {
OPENSSL_PUT_ERROR(X509, ERR_R_INTERNAL_ERROR);
ret = NULL;
}
return ret;
}

int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *obj, int param_type,
Expand Down
7 changes: 7 additions & 0 deletions include/openssl/x509.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,13 @@ OPENSSL_EXPORT X509_NAME *X509_get_subject_name(const X509 *x509);
// object.
OPENSSL_EXPORT X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x509);

// X509_get0_pubkey returns |x509|'s public key as an |EVP_PKEY|, or NULL if the
// public key was unsupported or could not be decoded. It is similar to
// |X509_get_pubkey|, but it does not increment the reference count of the
// returned |EVP_PKEY|. This means that the caller must not free the result after
// use.
OPENSSL_EXPORT EVP_PKEY *X509_get0_pubkey(const X509 *x);

// X509_get_pubkey returns |x509|'s public key as an |EVP_PKEY|, or NULL if the
// public key was unsupported or could not be decoded. This function returns a
// reference to the |EVP_PKEY|. The caller must release the result with
Expand Down

0 comments on commit 8aca04c

Please sign in to comment.