Skip to content

Commit f878bbd

Browse files
committed
Store whether pkey object contains private key
Rather than querying whether the EVP_PKEY contains private key information, determine this at time of construction and store it in the PHP object. OpenSSL doesn't provide an API for this purpose, and seems somewhat reluctant to add one, see openssl/openssl#9467. To avoid using deprecated low-level APIs to determine whether something is a private key ourselves, remember it at the point of construction.
1 parent cd8bf0b commit f878bbd

File tree

1 file changed

+31
-124
lines changed

1 file changed

+31
-124
lines changed

ext/openssl/openssl.c

Lines changed: 31 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ static void php_openssl_request_free_obj(zend_object *object)
200200

201201
typedef struct _php_openssl_pkey_object {
202202
EVP_PKEY *pkey;
203+
bool is_private;
203204
zend_object std;
204205
} php_openssl_pkey_object;
205206

@@ -223,6 +224,13 @@ static zend_object *php_openssl_pkey_create_object(zend_class_entry *class_type)
223224
return &intern->std;
224225
}
225226

227+
static void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private) {
228+
object_init_ex(zv, php_openssl_pkey_ce);
229+
php_openssl_pkey_object *obj = Z_OPENSSL_PKEY_P(zv);
230+
obj->pkey = pkey;
231+
obj->is_private = is_private;
232+
}
233+
226234
static zend_function *php_openssl_pkey_get_constructor(zend_object *object) {
227235
zend_throw_error(NULL, "Cannot directly construct OpenSSLAsymmetricKey, use openssl_pkey_new() instead");
228236
return NULL;
@@ -516,7 +524,6 @@ static X509 *php_openssl_x509_from_zval(zval *val, bool *free_cert);
516524
static X509_REQ *php_openssl_csr_from_param(zend_object *csr_obj, zend_string *csr_str);
517525
static EVP_PKEY *php_openssl_pkey_from_zval(zval *val, int public_key, char *passphrase, size_t passphrase_len);
518526

519-
static int php_openssl_is_private_key(EVP_PKEY* pkey);
520527
static X509_STORE * php_openssl_setup_verify(zval * calist);
521528
static STACK_OF(X509) * php_openssl_load_all_certs_from_file(char *certfile);
522529
static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
@@ -3337,11 +3344,8 @@ PHP_FUNCTION(openssl_csr_new)
33373344
if (we_made_the_key) {
33383345
/* and an object for the private key */
33393346
zval zkey_object;
3340-
php_openssl_pkey_object *key_object;
3341-
object_init_ex(&zkey_object, php_openssl_pkey_ce);
3342-
key_object = Z_OPENSSL_PKEY_P(&zkey_object);
3343-
key_object->pkey = req.priv_key;
3344-
3347+
php_openssl_pkey_object_init(
3348+
&zkey_object, req.priv_key, /* is_private */ true);
33453349
ZEND_TRY_ASSIGN_REF_TMP(out_pkey, &zkey_object);
33463350
req.priv_key = NULL; /* make sure the cleanup code doesn't zap it! */
33473351
}
@@ -3399,7 +3403,6 @@ PHP_FUNCTION(openssl_csr_get_public_key)
33993403
zend_string *csr_str;
34003404
bool use_shortnames = 1;
34013405

3402-
php_openssl_pkey_object *key_object;
34033406
EVP_PKEY *tpubkey;
34043407

34053408
ZEND_PARSE_PARAMETERS_START(1, 2)
@@ -3442,9 +3445,7 @@ PHP_FUNCTION(openssl_csr_get_public_key)
34423445
RETURN_FALSE;
34433446
}
34443447

3445-
object_init_ex(return_value, php_openssl_pkey_ce);
3446-
key_object = Z_OPENSSL_PKEY_P(return_value);
3447-
key_object->pkey = tpubkey;
3448+
php_openssl_pkey_object_init(return_value, tpubkey, /* is_private */ false);
34483449
}
34493450
/* }}} */
34503451

@@ -3520,10 +3521,9 @@ static EVP_PKEY *php_openssl_pkey_from_zval(zval *val, int public_key, char *pas
35203521
}
35213522

35223523
if (Z_TYPE_P(val) == IS_OBJECT && Z_OBJCE_P(val) == php_openssl_pkey_ce) {
3523-
int is_priv;
3524-
3525-
key = php_openssl_pkey_from_obj(Z_OBJ_P(val))->pkey;
3526-
is_priv = php_openssl_is_private_key(key);
3524+
php_openssl_pkey_object *obj = php_openssl_pkey_from_obj(Z_OBJ_P(val));
3525+
key = obj->pkey;
3526+
bool is_priv = obj->is_private;
35273527

35283528
/* check whether it is actually a private key if requested */
35293529
if (!public_key && !is_priv) {
@@ -3758,85 +3758,6 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req
37583758
}
37593759
/* }}} */
37603760

3761-
/* {{{ php_openssl_is_private_key
3762-
Check whether the supplied key is a private key by checking if the secret prime factors are set */
3763-
static int php_openssl_is_private_key(EVP_PKEY* pkey)
3764-
{
3765-
assert(pkey != NULL);
3766-
3767-
switch (EVP_PKEY_id(pkey)) {
3768-
case EVP_PKEY_RSA:
3769-
case EVP_PKEY_RSA2:
3770-
{
3771-
RSA *rsa = EVP_PKEY_get0_RSA(pkey);
3772-
if (rsa != NULL) {
3773-
const BIGNUM *p, *q;
3774-
3775-
RSA_get0_factors(rsa, &p, &q);
3776-
if (p == NULL || q == NULL) {
3777-
return 0;
3778-
}
3779-
}
3780-
}
3781-
break;
3782-
case EVP_PKEY_DSA:
3783-
case EVP_PKEY_DSA1:
3784-
case EVP_PKEY_DSA2:
3785-
case EVP_PKEY_DSA3:
3786-
case EVP_PKEY_DSA4:
3787-
{
3788-
DSA *dsa = EVP_PKEY_get0_DSA(pkey);
3789-
if (dsa != NULL) {
3790-
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
3791-
3792-
DSA_get0_pqg(dsa, &p, &q, &g);
3793-
if (p == NULL || q == NULL) {
3794-
return 0;
3795-
}
3796-
3797-
DSA_get0_key(dsa, &pub_key, &priv_key);
3798-
if (priv_key == NULL) {
3799-
return 0;
3800-
}
3801-
}
3802-
}
3803-
break;
3804-
case EVP_PKEY_DH:
3805-
{
3806-
DH *dh = EVP_PKEY_get0_DH(pkey);
3807-
if (dh != NULL) {
3808-
const BIGNUM *p, *q, *g, *pub_key, *priv_key;
3809-
3810-
DH_get0_pqg(dh, &p, &q, &g);
3811-
if (p == NULL) {
3812-
return 0;
3813-
}
3814-
3815-
DH_get0_key(dh, &pub_key, &priv_key);
3816-
if (priv_key == NULL) {
3817-
return 0;
3818-
}
3819-
}
3820-
}
3821-
break;
3822-
#ifdef HAVE_EVP_PKEY_EC
3823-
case EVP_PKEY_EC:
3824-
{
3825-
EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
3826-
if (ec != NULL && NULL == EC_KEY_get0_private_key(ec)) {
3827-
return 0;
3828-
}
3829-
}
3830-
break;
3831-
#endif
3832-
default:
3833-
php_error_docref(NULL, E_WARNING, "Key type not supported in this PHP build!");
3834-
break;
3835-
}
3836-
return 1;
3837-
}
3838-
/* }}} */
3839-
38403761
#define OPENSSL_GET_BN(_array, _bn, _name) do { \
38413762
if (_bn != NULL) { \
38423763
int len = BN_num_bytes(_bn); \
@@ -3895,7 +3816,7 @@ static bool php_openssl_pkey_init_and_assign_rsa(EVP_PKEY *pkey, RSA *rsa, zval
38953816
}
38963817

38973818
/* {{{ php_openssl_pkey_init_dsa */
3898-
static bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
3819+
static bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data, bool *is_private)
38993820
{
39003821
BIGNUM *p, *q, *g, *priv_key, *pub_key;
39013822
const BIGNUM *priv_key_const, *pub_key_const;
@@ -3909,6 +3830,7 @@ static bool php_openssl_pkey_init_dsa(DSA *dsa, zval *data)
39093830

39103831
OPENSSL_PKEY_SET_BN(data, pub_key);
39113832
OPENSSL_PKEY_SET_BN(data, priv_key);
3833+
*is_private = priv_key != NULL;
39123834
if (pub_key) {
39133835
return DSA_set0_key(dsa, pub_key, priv_key);
39143836
}
@@ -3973,7 +3895,7 @@ static BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM
39733895
/* }}} */
39743896

39753897
/* {{{ php_openssl_pkey_init_dh */
3976-
static bool php_openssl_pkey_init_dh(DH *dh, zval *data)
3898+
static bool php_openssl_pkey_init_dh(DH *dh, zval *data, bool *is_private)
39773899
{
39783900
BIGNUM *p, *q, *g, *priv_key, *pub_key;
39793901

@@ -3986,6 +3908,7 @@ static bool php_openssl_pkey_init_dh(DH *dh, zval *data)
39863908

39873909
OPENSSL_PKEY_SET_BN(data, priv_key);
39883910
OPENSSL_PKEY_SET_BN(data, pub_key);
3911+
*is_private = priv_key != NULL;
39893912
if (pub_key) {
39903913
return DH_set0_key(dh, pub_key, priv_key);
39913914
}
@@ -4014,7 +3937,6 @@ PHP_FUNCTION(openssl_pkey_new)
40143937
struct php_x509_request req;
40153938
zval * args = NULL;
40163939
zval *data;
4017-
php_openssl_pkey_object *key_object;
40183940

40193941
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|a!", &args) == FAILURE) {
40203942
RETURN_THROWS();
@@ -4031,9 +3953,7 @@ PHP_FUNCTION(openssl_pkey_new)
40313953
RSA *rsa = RSA_new();
40323954
if (rsa) {
40333955
if (php_openssl_pkey_init_and_assign_rsa(pkey, rsa, data)) {
4034-
object_init_ex(return_value, php_openssl_pkey_ce);
4035-
key_object = Z_OPENSSL_PKEY_P(return_value);
4036-
key_object->pkey = pkey;
3956+
php_openssl_pkey_object_init(return_value, pkey, /* is_private */ true);
40373957
return;
40383958
}
40393959
RSA_free(rsa);
@@ -4051,11 +3971,10 @@ PHP_FUNCTION(openssl_pkey_new)
40513971
if (pkey) {
40523972
DSA *dsa = DSA_new();
40533973
if (dsa) {
4054-
if (php_openssl_pkey_init_dsa(dsa, data)) {
3974+
bool is_private;
3975+
if (php_openssl_pkey_init_dsa(dsa, data, &is_private)) {
40553976
if (EVP_PKEY_assign_DSA(pkey, dsa)) {
4056-
object_init_ex(return_value, php_openssl_pkey_ce);
4057-
key_object = Z_OPENSSL_PKEY_P(return_value);
4058-
key_object->pkey = pkey;
3977+
php_openssl_pkey_object_init(return_value, pkey, is_private);
40593978
return;
40603979
} else {
40613980
php_openssl_store_errors();
@@ -4076,13 +3995,10 @@ PHP_FUNCTION(openssl_pkey_new)
40763995
if (pkey) {
40773996
DH *dh = DH_new();
40783997
if (dh) {
4079-
if (php_openssl_pkey_init_dh(dh, data)) {
3998+
bool is_private;
3999+
if (php_openssl_pkey_init_dh(dh, data, &is_private)) {
40804000
if (EVP_PKEY_assign_DH(pkey, dh)) {
4081-
php_openssl_pkey_object *key_object;
4082-
4083-
object_init_ex(return_value, php_openssl_pkey_ce);
4084-
key_object = Z_OPENSSL_PKEY_P(return_value);
4085-
key_object->pkey = pkey;
4001+
php_openssl_pkey_object_init(return_value, pkey, is_private);
40864002
return;
40874003
} else {
40884004
php_openssl_store_errors();
@@ -4108,6 +4024,7 @@ PHP_FUNCTION(openssl_pkey_new)
41084024
if (pkey) {
41094025
eckey = EC_KEY_new();
41104026
if (eckey) {
4027+
bool is_private = false;
41114028
EC_GROUP *group = NULL;
41124029
zval *bn;
41134030
zval *x;
@@ -4139,6 +4056,7 @@ PHP_FUNCTION(openssl_pkey_new)
41394056
// The public key 'pnt' can be calculated from 'd' or is defined by 'x' and 'y'
41404057
if ((bn = zend_hash_str_find(Z_ARRVAL_P(data), "d", sizeof("d") - 1)) != NULL &&
41414058
Z_TYPE_P(bn) == IS_STRING) {
4059+
is_private = true;
41424060
d = BN_bin2bn((unsigned char*) Z_STRVAL_P(bn), Z_STRLEN_P(bn), NULL);
41434061
if (!EC_KEY_set_private_key(eckey, d)) {
41444062
php_openssl_store_errors();
@@ -4186,10 +4104,7 @@ PHP_FUNCTION(openssl_pkey_new)
41864104
}
41874105
if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) {
41884106
EC_GROUP_free(group);
4189-
4190-
object_init_ex(return_value, php_openssl_pkey_ce);
4191-
key_object = Z_OPENSSL_PKEY_P(return_value);
4192-
key_object->pkey = pkey;
4107+
php_openssl_pkey_object_init(return_value, pkey, is_private);
41934108
return;
41944109
} else {
41954110
php_openssl_store_errors();
@@ -4224,9 +4139,7 @@ PHP_FUNCTION(openssl_pkey_new)
42244139
if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) {
42254140
if (php_openssl_generate_private_key(&req)) {
42264141
/* pass back a key resource */
4227-
object_init_ex(return_value, php_openssl_pkey_ce);
4228-
key_object = Z_OPENSSL_PKEY_P(return_value);
4229-
key_object->pkey = req.priv_key;
4142+
php_openssl_pkey_object_init(return_value, req.priv_key, /* is_private */ true);
42304143
/* make sure the cleanup code doesn't zap it! */
42314144
req.priv_key = NULL;
42324145
}
@@ -4399,7 +4312,6 @@ PHP_FUNCTION(openssl_pkey_get_public)
43994312
{
44004313
zval *cert;
44014314
EVP_PKEY *pkey;
4402-
php_openssl_pkey_object *key_object;
44034315

44044316
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z", &cert) == FAILURE) {
44054317
RETURN_THROWS();
@@ -4409,9 +4321,7 @@ PHP_FUNCTION(openssl_pkey_get_public)
44094321
RETURN_FALSE;
44104322
}
44114323

4412-
object_init_ex(return_value, php_openssl_pkey_ce);
4413-
key_object = Z_OPENSSL_PKEY_P(return_value);
4414-
key_object->pkey = pkey;
4324+
php_openssl_pkey_object_init(return_value, pkey, /* is_private */ false);
44154325
}
44164326
/* }}} */
44174327

@@ -4433,7 +4343,6 @@ PHP_FUNCTION(openssl_pkey_get_private)
44334343
EVP_PKEY *pkey;
44344344
char * passphrase = "";
44354345
size_t passphrase_len = sizeof("")-1;
4436-
php_openssl_pkey_object *key_object;
44374346

44384347
if (zend_parse_parameters(ZEND_NUM_ARGS(), "z|s!", &cert, &passphrase, &passphrase_len) == FAILURE) {
44394348
RETURN_THROWS();
@@ -4448,9 +4357,7 @@ PHP_FUNCTION(openssl_pkey_get_private)
44484357
RETURN_FALSE;
44494358
}
44504359

4451-
object_init_ex(return_value, php_openssl_pkey_ce);
4452-
key_object = Z_OPENSSL_PKEY_P(return_value);
4453-
key_object->pkey = pkey;
4360+
php_openssl_pkey_object_init(return_value, pkey, /* is_private */ true);
44544361
}
44554362

44564363
/* }}} */

0 commit comments

Comments
 (0)