Skip to content

Commit 0917e72

Browse files
davidbensamuel40791765
authored andcommitted
Eagerly compute the cached EVP_PKEY in X509_PUBKEY
Whenever the object is mutated, we can simply refresh the cached EVP_PKEY. This aligns with OpenSSL, which computes it eagerly these days. This removes the need to lock things, and also makes it easy to implement the get0 versions of the functions from OpenSSL. Change-Id: Ib17b654af694817edc43e4742d9baf9ed05c676e Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/65050 Commit-Queue: David Benjamin <[email protected]> Reviewed-by: Bob Beck <[email protected]> (cherry picked from commit 5b3dc49c1271554f73b976c2c625600d6bd912b0)
1 parent 2854985 commit 0917e72

File tree

6 files changed

+70
-103
lines changed

6 files changed

+70
-103
lines changed

crypto/x509/internal.h

-6
Original file line numberDiff line numberDiff line change
@@ -368,12 +368,6 @@ ASN1_TYPE *ASN1_generate_v3(const char *str, const X509V3_CTX *cnf);
368368

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

371-
// X509_PUBKEY_get0 decodes the public key in |key| and returns an |EVP_PKEY|
372-
// on success, or NULL on error. It is similar to |X509_PUBKEY_get|, but it
373-
// directly returns the reference to |pkey| of |key|. This means that the
374-
// caller must not free the result after use.
375-
EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key);
376-
377371
int x509_check_cert_time(X509_STORE_CTX *ctx, X509 *x, int suppress_error);
378372

379373
// RSA-PSS functions.

crypto/x509/x509_cmp.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -225,11 +225,11 @@ EVP_PKEY *X509_get0_pubkey(const X509 *x) {
225225
return (X509_PUBKEY_get0(x->cert_info->key));
226226
}
227227

228-
EVP_PKEY *X509_get_pubkey(X509 *x) {
228+
EVP_PKEY *X509_get_pubkey(const X509 *x) {
229229
if ((x == NULL) || (x->cert_info == NULL)) {
230230
return NULL;
231231
}
232-
return (X509_PUBKEY_get(x->cert_info->key));
232+
return X509_PUBKEY_get(x->cert_info->key);
233233
}
234234

235235
ASN1_BIT_STRING *X509_get0_pubkey_bitstr(const X509 *x) {

crypto/x509/x509_req.c

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,15 @@ X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req) {
7676
return req->req_info->subject;
7777
}
7878

79-
EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req) {
79+
EVP_PKEY *X509_REQ_get_pubkey(const X509_REQ *req) {
8080
if ((req == NULL) || (req->req_info == NULL)) {
8181
OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
8282
return NULL;
8383
}
8484
return (X509_PUBKEY_get(req->req_info->pubkey));
8585
}
8686

87-
EVP_PKEY *X509_REQ_get0_pubkey(X509_REQ *req) {
87+
EVP_PKEY *X509_REQ_get0_pubkey(const X509_REQ *req) {
8888
if ((req == NULL) || (req->req_info == NULL)) {
8989
OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
9090
return NULL;

crypto/x509/x509spki.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ int NETSCAPE_SPKI_set_pubkey(NETSCAPE_SPKI *x, EVP_PKEY *pkey) {
6868
return (X509_PUBKEY_set(&(x->spkac->pubkey), pkey));
6969
}
7070

71-
EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *x) {
71+
EVP_PKEY *NETSCAPE_SPKI_get_pubkey(const NETSCAPE_SPKI *x) {
7272
if ((x == NULL) || (x->spkac == NULL)) {
7373
return NULL;
7474
}

crypto/x509/x_pubkey.c

+36-67
Original file line numberDiff line numberDiff line change
@@ -65,26 +65,46 @@
6565
#include <openssl/evp.h>
6666
#include <openssl/mem.h>
6767
#include <openssl/obj.h>
68-
#include <openssl/thread.h>
6968

7069
#include "../internal.h"
7170
#include "internal.h"
7271

7372

7473
static void x509_pubkey_changed(X509_PUBKEY *pub) {
75-
// TODO(davidben): Instead of just dropping the key, also compute the new
76-
// cached key. This will let us implement |X509_get0_pubkey| and remove the
77-
// need for a mutex.
7874
EVP_PKEY_free(pub->pkey);
7975
pub->pkey = NULL;
76+
77+
// Re-encode the |X509_PUBKEY| to DER and parse it with EVP's APIs.
78+
uint8_t *spki = NULL;
79+
int spki_len = i2d_X509_PUBKEY(pub, &spki);
80+
if (spki_len < 0) {
81+
goto err;
82+
}
83+
84+
CBS cbs;
85+
CBS_init(&cbs, spki, (size_t)spki_len);
86+
EVP_PKEY *pkey = EVP_parse_public_key(&cbs);
87+
if (pkey == NULL || CBS_len(&cbs) != 0) {
88+
EVP_PKEY_free(pkey);
89+
goto err;
90+
}
91+
92+
pub->pkey = pkey;
93+
94+
err:
95+
OPENSSL_free(spki);
96+
// If the operation failed, clear errors. An |X509_PUBKEY| whose key we cannot
97+
// parse is still a valid SPKI. It just cannot be converted to an |EVP_PKEY|.
98+
ERR_clear_error();
8099
}
81100

82-
// Minor tweak to operation: free up EVP_PKEY
83101
static int pubkey_cb(int operation, ASN1_VALUE **pval, const ASN1_ITEM *it,
84102
void *exarg) {
103+
X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval;
85104
if (operation == ASN1_OP_FREE_POST) {
86-
X509_PUBKEY *pubkey = (X509_PUBKEY *)*pval;
87105
EVP_PKEY_free(pubkey->pkey);
106+
} else if (operation == ASN1_OP_D2I_POST) {
107+
x509_pubkey_changed(pubkey);
88108
}
89109
return 1;
90110
}
@@ -133,76 +153,25 @@ int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey) {
133153
return 0;
134154
}
135155

136-
// g_pubkey_lock is used to protect the initialisation of the |pkey| member of
137-
// |X509_PUBKEY| objects. Really |X509_PUBKEY| should have a |CRYPTO_once_t|
138-
// inside it for this, but |CRYPTO_once_t| is private and |X509_PUBKEY| is
139-
// not.
140-
static struct CRYPTO_STATIC_MUTEX g_pubkey_lock = CRYPTO_STATIC_MUTEX_INIT;
141-
142-
// x509_pubkey_decode decodes |key| into |pkey|. It returns one on success and
143-
// zero on error.
144-
static int x509_pubkey_decode(EVP_PKEY **pkey, X509_PUBKEY *key) {
145-
CRYPTO_STATIC_MUTEX_lock_read(&g_pubkey_lock);
146-
if (key->pkey != NULL) {
147-
CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);
148-
*pkey = key->pkey;
149-
return 1;
156+
EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key) {
157+
if (key == NULL) {
158+
return NULL;
150159
}
151-
CRYPTO_STATIC_MUTEX_unlock_read(&g_pubkey_lock);
152160

153-
uint8_t *spki = NULL;
154-
// Re-encode the |X509_PUBKEY| to DER and parse it.
155-
int spki_len = i2d_X509_PUBKEY(key, &spki);
156-
if (spki_len < 0) {
157-
goto error;
158-
}
159-
CBS cbs;
160-
CBS_init(&cbs, spki, (size_t)spki_len);
161-
*pkey = EVP_parse_public_key(&cbs);
162-
if (*pkey == NULL || CBS_len(&cbs) != 0) {
161+
if (key->pkey == NULL) {
163162
OPENSSL_PUT_ERROR(X509, X509_R_PUBLIC_KEY_DECODE_ERROR);
164-
goto error;
165-
}
166-
167-
// Check to see if another thread set key->pkey first
168-
CRYPTO_STATIC_MUTEX_lock_write(&g_pubkey_lock);
169-
if (key->pkey) {
170-
CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
171-
EVP_PKEY_free(*pkey);
172-
*pkey = key->pkey;
173-
} else {
174-
key->pkey = *pkey;
175-
CRYPTO_STATIC_MUTEX_unlock_write(&g_pubkey_lock);
176-
}
177-
OPENSSL_free(spki);
178-
return 1;
179-
error:
180-
OPENSSL_free(spki);
181-
return 0;
182-
}
183-
184-
EVP_PKEY *X509_PUBKEY_get0(X509_PUBKEY *key) {
185-
if (key == NULL) {
186-
OPENSSL_PUT_ERROR(X509, ERR_R_PASSED_NULL_PARAMETER);
187163
return NULL;
188164
}
189165

190-
EVP_PKEY *ret = NULL;
191-
if (!x509_pubkey_decode(&ret, key)) {
192-
EVP_PKEY_free(ret);
193-
return NULL;
194-
};
195-
return ret;
166+
return key->pkey;
196167
}
197168

198-
EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key) {
199-
EVP_PKEY *ret = X509_PUBKEY_get0(key);
200-
201-
if (ret != NULL && !EVP_PKEY_up_ref(ret)) {
202-
OPENSSL_PUT_ERROR(X509, ERR_R_INTERNAL_ERROR);
203-
ret = NULL;
169+
EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key) {
170+
EVP_PKEY *pkey = X509_PUBKEY_get0(key);
171+
if (pkey != NULL) {
172+
EVP_PKEY_up_ref(pkey);
204173
}
205-
return ret;
174+
return pkey;
206175
}
207176

208177
int X509_PUBKEY_set0_param(X509_PUBKEY *pub, ASN1_OBJECT *obj, int param_type,

include/openssl/x509.h

+29-25
Original file line numberDiff line numberDiff line change
@@ -199,17 +199,15 @@ OPENSSL_EXPORT X509_NAME *X509_get_subject_name(const X509 *x509);
199199
OPENSSL_EXPORT X509_PUBKEY *X509_get_X509_PUBKEY(const X509 *x509);
200200

201201
// X509_get0_pubkey returns |x509|'s public key as an |EVP_PKEY|, or NULL if the
202-
// public key was unsupported or could not be decoded. It is similar to
203-
// |X509_get_pubkey|, but it does not increment the reference count of the
204-
// returned |EVP_PKEY|. This means that the caller must not free the result
205-
// after use.
206-
OPENSSL_EXPORT EVP_PKEY *X509_get0_pubkey(const X509 *x);
207-
208-
// X509_get_pubkey returns |x509|'s public key as an |EVP_PKEY|, or NULL if the
209-
// public key was unsupported or could not be decoded. This function returns a
210-
// reference to the |EVP_PKEY|. The caller must release the result with
211-
// |EVP_PKEY_free| when done.
212-
OPENSSL_EXPORT EVP_PKEY *X509_get_pubkey(X509 *x509);
202+
// public key was unsupported or could not be decoded. The |EVP_PKEY| is cached
203+
// in |x509|, so callers must not mutate the result.
204+
OPENSSL_EXPORT EVP_PKEY *X509_get0_pubkey(const X509 *x509);
205+
206+
// X509_get_pubkey behaves like |X509_get0_pubkey| but increments the reference
207+
// count on the |EVP_PKEY|. The caller must release the result with
208+
// |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |x509|, so callers
209+
// must not mutate the result.
210+
OPENSSL_EXPORT EVP_PKEY *X509_get_pubkey(const X509 *x509);
213211

214212
// X509_get0_pubkey_bitstr returns the BIT STRING portion of |x509|'s public
215213
// key. Note this does not contain the AlgorithmIdentifier portion.
@@ -1152,15 +1150,16 @@ OPENSSL_EXPORT long X509_REQ_get_version(const X509_REQ *req);
11521150
// not const-correct for legacy reasons.
11531151
OPENSSL_EXPORT X509_NAME *X509_REQ_get_subject_name(const X509_REQ *req);
11541152

1155-
// X509_REQ_get_pubkey returns |req|'s public key as an |EVP_PKEY|, or NULL if
1156-
// the public key was unsupported or could not be decoded. This function returns
1157-
// a reference to the |EVP_PKEY|. The caller must release the result with
1158-
// |EVP_PKEY_free| when done.
1159-
OPENSSL_EXPORT EVP_PKEY *X509_REQ_get_pubkey(X509_REQ *req);
1153+
// X509_REQ_get0_pubkey returns |req|'s public key as an |EVP_PKEY|, or NULL if
1154+
// the public key was unsupported or could not be decoded. The |EVP_PKEY| is
1155+
// cached in |req|, so callers must not mutate the result.
1156+
OPENSSL_EXPORT EVP_PKEY *X509_REQ_get0_pubkey(const X509_REQ *req);
11601157

1161-
// X509_REQ_get0_pubkey is like |X509_REQ_get_pubkey|, but directly returns the
1162-
// reference to |req|. The caller must not free the result after use.
1163-
OPENSSL_EXPORT EVP_PKEY *X509_REQ_get0_pubkey(X509_REQ *req);
1158+
// X509_REQ_get_pubkey behaves like |X509_REQ_get0_pubkey| but increments the
1159+
// reference count on the |EVP_PKEY|. The caller must release the result with
1160+
// |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |req|, so callers must
1161+
// not mutate the result.
1162+
OPENSSL_EXPORT EVP_PKEY *X509_REQ_get_pubkey(const X509_REQ *req);
11641163

11651164
// X509_REQ_check_private_key returns one if |req|'s public key matches |pkey|
11661165
// and zero otherwise.
@@ -1643,11 +1642,16 @@ OPENSSL_EXPORT int i2d_X509_PUBKEY(const X509_PUBKEY *key, uint8_t **outp);
16431642
// object, and returns one. Otherwise, it returns zero.
16441643
OPENSSL_EXPORT int X509_PUBKEY_set(X509_PUBKEY **x, EVP_PKEY *pkey);
16451644

1646-
// X509_PUBKEY_get decodes the public key in |key| and returns an |EVP_PKEY| on
1647-
// success, or NULL on error or unrecognized algorithm. The caller must release
1648-
// the result with |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |key|,
1649-
// so callers must not mutate the result.
1650-
OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get(X509_PUBKEY *key);
1645+
// X509_PUBKEY_get0 returns |key| as an |EVP_PKEY|, or NULL if |key| either
1646+
// could not be parsed or is an unrecognized algorithm. The |EVP_PKEY| is cached
1647+
// in |key|, so callers must not mutate the result.
1648+
OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get0(const X509_PUBKEY *key);
1649+
1650+
// X509_PUBKEY_get behaves like |X509_PUBKEY_get0| but increments the reference
1651+
// count on the |EVP_PKEY|. The caller must release the result with
1652+
// |EVP_PKEY_free| when done. The |EVP_PKEY| is cached in |key|, so callers must
1653+
// not mutate the result.
1654+
OPENSSL_EXPORT EVP_PKEY *X509_PUBKEY_get(const X509_PUBKEY *key);
16511655

16521656
// X509_PUBKEY_set0_param sets |pub| to a key with AlgorithmIdentifier
16531657
// determined by |obj|, |param_type|, and |param_value|, and an encoded
@@ -2295,7 +2299,7 @@ OPENSSL_EXPORT char *NETSCAPE_SPKI_b64_encode(NETSCAPE_SPKI *spki);
22952299
// NETSCAPE_SPKI_get_pubkey decodes and returns the public key in |spki| as an
22962300
// |EVP_PKEY|, or NULL on error. The caller takes ownership of the resulting
22972301
// pointer and must call |EVP_PKEY_free| when done.
2298-
OPENSSL_EXPORT EVP_PKEY *NETSCAPE_SPKI_get_pubkey(NETSCAPE_SPKI *spki);
2302+
OPENSSL_EXPORT EVP_PKEY *NETSCAPE_SPKI_get_pubkey(const NETSCAPE_SPKI *spki);
22992303

23002304
// NETSCAPE_SPKI_set_pubkey sets |spki|'s public key to |pkey|. It returns one
23012305
// on success or zero on error. This function does not take ownership of |pkey|,

0 commit comments

Comments
 (0)