From 2cf02ea3b3e0e6bfd1f22e381d0e44c2b3c20ef1 Mon Sep 17 00:00:00 2001 From: Manuel Mausz Date: Fri, 26 Apr 2024 13:26:46 +0200 Subject: [PATCH 1/3] Add support for Curve25519 + Curve448 based keys For openssl_pkey_get_details we export the priv+pub parameters. ED25519/ED448 do not support streaming, so we need to use EVP_Digest{Sign,Verify} instead. In general the older EVP_{Sign,Verify} interface should be avoided as the key is passed very late. See BUGS section in OpenSSL manpages of EVP_{Sign,Verify}Final Additionally per requirement we need to allow sign/verify without digest. So we need to allow passing 0 as digest. In OpenSSL 3.0+ this also corresponds to the default digest (see EVP_PKEY_get_default_digest_name). For CSR creation we need to allow "null" as digest_alg option. --- ext/openssl/openssl.c | 181 +++++++++++++++++- ext/openssl/openssl.stub.php | 22 +++ ext/openssl/tests/ed25519.phpt | 122 ++++++++++++ ext/openssl/tests/ed448.phpt | 122 ++++++++++++ .../tests/openssl_pkey_derive-x25519.phpt | 24 +++ .../tests/openssl_pkey_derive-x448.phpt | 26 +++ 6 files changed, 487 insertions(+), 10 deletions(-) create mode 100644 ext/openssl/tests/ed25519.phpt create mode 100644 ext/openssl/tests/ed448.phpt create mode 100644 ext/openssl/tests/openssl_pkey_derive-x25519.phpt create mode 100644 ext/openssl/tests/openssl_pkey_derive-x448.phpt diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index 949f5d76245e..d5191242184d 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -120,7 +120,13 @@ enum php_openssl_key_type { OPENSSL_KEYTYPE_DH, OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA, #ifdef HAVE_EVP_PKEY_EC - OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1 + OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1, +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + OPENSSL_KEYTYPE_X25519 = OPENSSL_KEYTYPE_DH +2, + OPENSSL_KEYTYPE_ED25519 = OPENSSL_KEYTYPE_DH +3, + OPENSSL_KEYTYPE_X448 = OPENSSL_KEYTYPE_DH +4, + OPENSSL_KEYTYPE_ED448 = OPENSSL_KEYTYPE_DH +5, #endif }; @@ -1011,7 +1017,11 @@ static int php_openssl_parse_config(struct php_x509_request * req, zval * option req->digest_name = php_openssl_conf_get_string(req->req_config, req->section_name, "default_md"); } if (req->digest_name != NULL) { - req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + if (strcmp(req->digest_name, "null") == 0) { + req->digest = req->md_alg = EVP_md_null(); + } else { + req->digest = req->md_alg = EVP_get_digestbyname(req->digest_name); + } } if (req->md_alg == NULL) { req->md_alg = req->digest = EVP_sha1(); @@ -3732,6 +3742,16 @@ static int php_openssl_get_evp_pkey_type(int key_type) { #ifdef HAVE_EVP_PKEY_EC case OPENSSL_KEYTYPE_EC: return EVP_PKEY_EC; +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + case OPENSSL_KEYTYPE_X25519: + return EVP_PKEY_X25519; + case OPENSSL_KEYTYPE_ED25519: + return EVP_PKEY_ED25519; + case OPENSSL_KEYTYPE_X448: + return EVP_PKEY_X448; + case OPENSSL_KEYTYPE_ED448: + return EVP_PKEY_ED448; #endif default: return -1; @@ -3802,6 +3822,16 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req goto cleanup; } break; +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + case EVP_PKEY_X25519: + break; + case EVP_PKEY_ED25519: + break; + case EVP_PKEY_X448: + break; + case EVP_PKEY_ED448: + break; #endif EMPTY_SWITCH_DEFAULT_CASE() } @@ -4669,6 +4699,62 @@ static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { } #endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 +static void php_openssl_pkey_object_curve_25519_448(zval *return_value, int key_type, zval *data) { + EVP_PKEY *pkey = NULL; + EVP_PKEY_CTX *ctx = NULL; + OSSL_PARAM *params = NULL; + OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new(); + bool is_private; + + RETVAL_FALSE; + + zval *priv_key = zend_hash_str_find(Z_ARRVAL_P(data), "priv_key", sizeof("priv_key") - 1); + if (priv_key && Z_TYPE_P(priv_key) == IS_STRING && Z_STRLEN_P(priv_key) > 0) { + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PRIV_KEY, Z_STRVAL_P(priv_key), Z_STRLEN_P(priv_key))) { + goto cleanup; + } + } + + zval *pub_key = zend_hash_str_find(Z_ARRVAL_P(data), "pub_key", sizeof("pub_key") - 1); + if (pub_key && Z_TYPE_P(pub_key) == IS_STRING && Z_STRLEN_P(pub_key) > 0) { + if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, Z_STRVAL_P(pub_key), Z_STRLEN_P(pub_key))) { + goto cleanup; + } + } + + params = OSSL_PARAM_BLD_to_param(bld); + if (!params) { + goto cleanup; + } + + ctx = EVP_PKEY_CTX_new_id(key_type, NULL); + if (pub_key || priv_key) { + if (EVP_PKEY_fromdata_init(ctx) <= 0 || + EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) { + goto cleanup; + } + is_private = priv_key != NULL; + } else { + is_private = true; + PHP_OPENSSL_RAND_ADD_TIME(); + if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) { + goto cleanup; + } + } + + if (pkey) { + php_openssl_pkey_object_init(return_value, pkey, is_private); + } + +cleanup: + php_openssl_store_errors(); + EVP_PKEY_CTX_free(ctx); + OSSL_PARAM_free(params); + OSSL_PARAM_BLD_free(bld); +} +#endif + /* {{{ Generates a new private key */ PHP_FUNCTION(openssl_pkey_new) { @@ -4720,6 +4806,24 @@ PHP_FUNCTION(openssl_pkey_new) } php_openssl_pkey_object_init(return_value, pkey, is_private); return; +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "x25519", sizeof("x25519") - 1)) != NULL && + Z_TYPE_P(data) == IS_ARRAY) { + php_openssl_pkey_object_curve_25519_448(return_value, EVP_PKEY_X25519, data); + return; + } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ed25519", sizeof("ed25519") - 1)) != NULL && + Z_TYPE_P(data) == IS_ARRAY) { + php_openssl_pkey_object_curve_25519_448(return_value, EVP_PKEY_ED25519, data); + return; + } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "x448", sizeof("x448") - 1)) != NULL && + Z_TYPE_P(data) == IS_ARRAY) { + php_openssl_pkey_object_curve_25519_448(return_value, EVP_PKEY_X448, data); + return; + } else if ((data = zend_hash_str_find(Z_ARRVAL_P(args), "ed448", sizeof("ed448") - 1)) != NULL && + Z_TYPE_P(data) == IS_ARRAY) { + php_openssl_pkey_object_curve_25519_448(return_value, EVP_PKEY_ED448, data); + return; #endif } } @@ -4952,6 +5056,27 @@ static zend_string *php_openssl_get_utf8_param( return NULL; } #endif + +static void php_openssl_copy_octet_string_param( + zval *ary, EVP_PKEY *pkey, const char *param, const char *name) { + unsigned char buf[64]; + size_t len; + if (EVP_PKEY_get_octet_string_param(pkey, param, buf, sizeof(buf), &len) > 0) { + zend_string *str = zend_string_alloc(len, 0); + memcpy(ZSTR_VAL(str), buf, len); + ZSTR_VAL(str)[len] = '\0'; + add_assoc_str(ary, name, str); + } +} + +static void php_openssl_copy_curve_25519_448_params( + zval *return_value, const char *assoc_name, EVP_PKEY *pkey) { + zval ary; + array_init(&ary); + add_assoc_zval(return_value, assoc_name, &ary); + php_openssl_copy_octet_string_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "priv_key"); + php_openssl_copy_octet_string_param(&ary, pkey, OSSL_PKEY_PARAM_PUB_KEY, "pub_key"); +} #endif /* {{{ returns an array with the key details (bits, pkey, type)*/ @@ -5060,6 +5185,28 @@ PHP_FUNCTION(openssl_pkey_get_details) php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "d"); break; } +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + case EVP_PKEY_X25519: { + ktype = OPENSSL_KEYTYPE_X25519; + php_openssl_copy_curve_25519_448_params(return_value, "x25519", pkey); + break; + } + case EVP_PKEY_ED25519: { + ktype = OPENSSL_KEYTYPE_ED25519; + php_openssl_copy_curve_25519_448_params(return_value, "ed25519", pkey); + break; + } + case EVP_PKEY_X448: { + ktype = OPENSSL_KEYTYPE_X448; + php_openssl_copy_curve_25519_448_params(return_value, "x448", pkey); + break; + } + case EVP_PKEY_ED448: { + ktype = OPENSSL_KEYTYPE_ED448; + php_openssl_copy_curve_25519_448_params(return_value, "ed448", pkey); + break; + } #endif default: ktype = -1; @@ -6937,14 +7084,14 @@ PHP_FUNCTION(openssl_sign) { zval *key, *signature; EVP_PKEY *pkey; - unsigned int siglen; - zend_string *sigbuf; + zend_string *sigbuf = NULL; char * data; size_t data_len; EVP_MD_CTX *md_ctx; zend_string *method_str = NULL; zend_long method_long = OPENSSL_ALGO_SHA1; const EVP_MD *mdtype; + bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(data, data_len) @@ -6967,19 +7114,27 @@ PHP_FUNCTION(openssl_sign) } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } - if (!mdtype) { + if (!mdtype && (!can_default_digest || method_long != 0)) { php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } - siglen = EVP_PKEY_size(pkey); - sigbuf = zend_string_alloc(siglen, 0); - md_ctx = EVP_MD_CTX_create(); + size_t siglen; +#if PHP_OPENSSL_API_VERSION >= 0x10100 + if (md_ctx != NULL && + EVP_DigestSignInit(md_ctx, NULL, mdtype, NULL, pkey) && + EVP_DigestSign(md_ctx, NULL, &siglen, (unsigned char*)data, data_len) && + (sigbuf = zend_string_alloc(siglen, 0)) != NULL && + EVP_DigestSign(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, (unsigned char*)data, data_len)) { +#else if (md_ctx != NULL && EVP_SignInit(md_ctx, mdtype) && EVP_SignUpdate(md_ctx, data, data_len) && - EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), &siglen, pkey)) { + (siglen = EVP_PKEY_size(pkey)) && + (sigbuf = zend_string_alloc(siglen, 0)) != NULL && + EVP_SignFinal(md_ctx, (unsigned char*)ZSTR_VAL(sigbuf), (unsigned int*)&siglen, pkey)) { +#endif ZSTR_VAL(sigbuf)[siglen] = '\0'; ZSTR_LEN(sigbuf) = siglen; ZEND_TRY_ASSIGN_REF_NEW_STR(signature, sigbuf); @@ -7008,6 +7163,7 @@ PHP_FUNCTION(openssl_verify) size_t signature_len; zend_string *method_str = NULL; zend_long method_long = OPENSSL_ALGO_SHA1; + bool can_default_digest = ZEND_THREEWAY_COMPARE(PHP_OPENSSL_API_VERSION, 0x30000) >= 0; ZEND_PARSE_PARAMETERS_START(3, 4) Z_PARAM_STRING(data, data_len) @@ -7024,7 +7180,7 @@ PHP_FUNCTION(openssl_verify) } else { mdtype = php_openssl_get_evp_md_from_algo(method_long); } - if (!mdtype) { + if (!mdtype && (!can_default_digest || method_long != 0)) { php_error_docref(NULL, E_WARNING, "Unknown digest algorithm"); RETURN_FALSE; } @@ -7039,9 +7195,14 @@ PHP_FUNCTION(openssl_verify) md_ctx = EVP_MD_CTX_create(); if (md_ctx == NULL || +#if PHP_OPENSSL_API_VERSION >= 0x10100 + !EVP_DigestVerifyInit(md_ctx, NULL, mdtype, NULL, pkey) || + (err = EVP_DigestVerify(md_ctx, (unsigned char *)signature, signature_len, (unsigned char*)data, data_len)) < 0) { +#else !EVP_VerifyInit (md_ctx, mdtype) || !EVP_VerifyUpdate (md_ctx, data, data_len) || (err = EVP_VerifyFinal(md_ctx, (unsigned char *)signature, (unsigned int)signature_len, pkey)) < 0) { +#endif php_openssl_store_errors(); } EVP_MD_CTX_destroy(md_ctx); diff --git a/ext/openssl/openssl.stub.php b/ext/openssl/openssl.stub.php index a7e963aec455..9757a15bd546 100644 --- a/ext/openssl/openssl.stub.php +++ b/ext/openssl/openssl.stub.php @@ -331,6 +331,28 @@ */ const OPENSSL_KEYTYPE_EC = UNKNOWN; #endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 +/** + * @var int + * @cvalue OPENSSL_KEYTYPE_X25519 + */ +const OPENSSL_KEYTYPE_X25519 = UNKNOWN; +/** + * @var int + * @cvalue OPENSSL_KEYTYPE_ED25519 + */ +const OPENSSL_KEYTYPE_ED25519 = UNKNOWN; +/** + * @var int + * @cvalue OPENSSL_KEYTYPE_X448 + */ +const OPENSSL_KEYTYPE_X448 = UNKNOWN; +/** + * @var int + * @cvalue OPENSSL_KEYTYPE_ED448 + */ +const OPENSSL_KEYTYPE_ED448 = UNKNOWN; +#endif /** * @var int diff --git a/ext/openssl/tests/ed25519.phpt b/ext/openssl/tests/ed25519.phpt new file mode 100644 index 000000000000..f31ff99d5350 --- /dev/null +++ b/ext/openssl/tests/ed25519.phpt @@ -0,0 +1,122 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_ED25519 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + OPENSSL_KEYTYPE_ED25519 ]); +var_dump($key1); + +$d1 = openssl_pkey_get_details($key1); +var_dump($d1["bits"] === 256); +var_dump(strlen($d1["key"]) === 113); +var_dump(strlen($d1["ed25519"]["priv_key"]) === 32); +var_dump($d1["type"] === OPENSSL_KEYTYPE_ED25519); + +$key2 = openssl_pkey_new($d1); +var_dump($key2); + +$d2 = openssl_pkey_get_details($key2); +// Compare array +var_dump($d1 == $d2); + +// Check that the public key info is computed from the private key if it is missing. +$d1_priv = $d1; +unset($d1_priv["ed25519"]["pub_key"]); +$key3 = openssl_pkey_new($d1_priv); +var_dump($key3); +$d3 = openssl_pkey_get_details($key3); +var_dump($d1 == $d3); + +// create public key from private +$d1_pub = $d1; +unset($d1_pub["ed25519"]["priv_key"]); +$pubkey1 = openssl_pkey_new($d1_pub); +var_dump($pubkey1); +$pubkey1_d = openssl_pkey_get_details($pubkey1); +var_dump($d1_pub == $pubkey1_d); + +// sign and verify +echo "Testing openssl_sign and openssl_verify\n"; +$payload = "somedata"; +var_dump(openssl_sign($payload, $signature, $key1, 0)); +var_dump(strlen($signature) === 64); +var_dump(openssl_verify($payload, $signature, $pubkey1, 0)); + + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" +); +$config = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'; +$args = array( + "private_key_type" => OPENSSL_KEYTYPE_ED25519, + "config" => $config, + "digest_alg" => "null", +); + +// openssl_csr_new creates a new public key pair if the key argument is null +echo "Testing openssl_csr_new with key generation\n"; +$keyGenerate = null; +$csr = openssl_csr_new($dn, $keyGenerate, $args); +var_dump($keyGenerate); +var_dump($csr); + +echo "Testing openssl_csr_new with existing key\n"; +$csr = openssl_csr_new($dn, $key1, $args); +$pubkey_csr = openssl_pkey_get_details(openssl_csr_get_public_key($csr)); +var_dump($pubkey_csr == $pubkey1_d); + +echo "Testing openssl_csr_sign\n"; +$x509 = openssl_csr_sign($csr, null, $key1, 365, $args); +var_dump($x509); + +echo "Testing openssl_x509_{verify,check_private_key}\n"; +var_dump(openssl_x509_check_private_key($x509, $key1)); +var_dump(openssl_x509_verify($x509, $pubkey1)); +var_dump(openssl_x509_check_private_key($x509, $keyGenerate)); + +?> +--EXPECTF-- +Testing openssl_pkey_new +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +bool(true) +bool(true) +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +Testing openssl_sign and openssl_verify +bool(true) +bool(true) +int(1) +Testing openssl_csr_new with key generation +object(OpenSSLAsymmetricKey)#%d (0) { +} +object(OpenSSLCertificateSigningRequest)#%d (0) { +} +Testing openssl_csr_new with existing key +bool(true) +Testing openssl_csr_sign +object(OpenSSLCertificate)#%d (0) { +} +Testing openssl_x509_{verify,check_private_key} +bool(true) +int(1) +bool(false) diff --git a/ext/openssl/tests/ed448.phpt b/ext/openssl/tests/ed448.phpt new file mode 100644 index 000000000000..4f00fd6a7637 --- /dev/null +++ b/ext/openssl/tests/ed448.phpt @@ -0,0 +1,122 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_ED448 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + OPENSSL_KEYTYPE_ED448 ]); +var_dump($key1); + +$d1 = openssl_pkey_get_details($key1); +var_dump($d1["bits"] === 456); +var_dump(strlen($d1["key"]) === 146); +var_dump(strlen($d1["ed448"]["priv_key"]) === 57); +var_dump($d1["type"] === OPENSSL_KEYTYPE_ED448); + +$key2 = openssl_pkey_new($d1); +var_dump($key2); + +$d2 = openssl_pkey_get_details($key2); +// Compare array +var_dump($d1 == $d2); + +// Check that the public key info is computed from the private key if it is missing. +$d1_priv = $d1; +unset($d1_priv["ed448"]["pub_key"]); +$key3 = openssl_pkey_new($d1_priv); +var_dump($key3); +$d3 = openssl_pkey_get_details($key3); +var_dump($d1 == $d3); + +// create public key from private +$d1_pub = $d1; +unset($d1_pub["ed448"]["priv_key"]); +$pubkey1 = openssl_pkey_new($d1_pub); +var_dump($pubkey1); +$pubkey1_d = openssl_pkey_get_details($pubkey1); +var_dump($d1_pub == $pubkey1_d); + +// sign and verify +echo "Testing openssl_sign and openssl_verify\n"; +$payload = "somedata"; +var_dump(openssl_sign($payload, $signature, $key1, 0)); +var_dump(strlen($signature) === 114); +var_dump(openssl_verify($payload, $signature, $pubkey1, 0)); + + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" +); +$config = __DIR__ . DIRECTORY_SEPARATOR . 'openssl.cnf'; +$args = array( + "private_key_type" => OPENSSL_KEYTYPE_ED448, + "config" => $config, + "digest_alg" => "null", +); + +// openssl_csr_new creates a new public key pair if the key argument is null +echo "Testing openssl_csr_new with key generation\n"; +$keyGenerate = null; +$csr = openssl_csr_new($dn, $keyGenerate, $args); +var_dump($keyGenerate); +var_dump($csr); + +echo "Testing openssl_csr_new with existing key\n"; +$csr = openssl_csr_new($dn, $key1, $args); +$pubkey_csr = openssl_pkey_get_details(openssl_csr_get_public_key($csr)); +var_dump($pubkey_csr == $pubkey1_d); + +echo "Testing openssl_csr_sign\n"; +$x509 = openssl_csr_sign($csr, null, $key1, 365, $args); +var_dump($x509); + +echo "Testing openssl_x509_{verify,check_private_key}\n"; +var_dump(openssl_x509_check_private_key($x509, $key1)); +var_dump(openssl_x509_verify($x509, $pubkey1)); +var_dump(openssl_x509_check_private_key($x509, $keyGenerate)); + +?> +--EXPECTF-- +Testing openssl_pkey_new +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +bool(true) +bool(true) +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +object(OpenSSLAsymmetricKey)#%d (0) { +} +bool(true) +Testing openssl_sign and openssl_verify +bool(true) +bool(true) +int(1) +Testing openssl_csr_new with key generation +object(OpenSSLAsymmetricKey)#%d (0) { +} +object(OpenSSLCertificateSigningRequest)#%d (0) { +} +Testing openssl_csr_new with existing key +bool(true) +Testing openssl_csr_sign +object(OpenSSLCertificate)#%d (0) { +} +Testing openssl_x509_{verify,check_private_key} +bool(true) +int(1) +bool(false) diff --git a/ext/openssl/tests/openssl_pkey_derive-x25519.phpt b/ext/openssl/tests/openssl_pkey_derive-x25519.phpt new file mode 100644 index 000000000000..5b273da8898d --- /dev/null +++ b/ext/openssl/tests/openssl_pkey_derive-x25519.phpt @@ -0,0 +1,24 @@ +--TEST-- +openssl_pkey_derive() X25519 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +76feeefed1d6bc01f3406d87759db371893ec6dc2cb912a130b33c3c91abcb21 diff --git a/ext/openssl/tests/openssl_pkey_derive-x448.phpt b/ext/openssl/tests/openssl_pkey_derive-x448.phpt new file mode 100644 index 000000000000..c33de6484b54 --- /dev/null +++ b/ext/openssl/tests/openssl_pkey_derive-x448.phpt @@ -0,0 +1,26 @@ +--TEST-- +openssl_pkey_derive() X448 +--EXTENSIONS-- +openssl +--SKIPIF-- + +--FILE-- + +--EXPECT-- +f57eea2028d773685334f3117d87d7f3b97d99373ff532a3ab50fbab8002c2e55bb4b39c11fc835d59e83d3d00c649e8566c013e0062fe9b From 8f01cb2dcf06e61c5544f2a37d6d7beff72d4b6b Mon Sep 17 00:00:00 2001 From: Manuel Mausz Date: Sun, 28 Apr 2024 21:28:40 +0200 Subject: [PATCH 2/3] Check for possible allocation failures --- ext/openssl/openssl.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index d5191242184d..e767fdd38c41 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -4498,6 +4498,10 @@ static EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) { *is_private = false; + if (!ctx || !bld || !bctx) { + goto cleanup; + } + zval *curve_name_zv = zend_hash_str_find(Z_ARRVAL_P(data), "curve_name", sizeof("curve_name") - 1); if (curve_name_zv && Z_TYPE_P(curve_name_zv) == IS_STRING && Z_STRLEN_P(curve_name_zv) > 0) { nid = OBJ_sn2nid(Z_STRVAL_P(curve_name_zv)); @@ -4709,6 +4713,10 @@ static void php_openssl_pkey_object_curve_25519_448(zval *return_value, int key_ RETVAL_FALSE; + if (!bld) { + goto cleanup; + } + zval *priv_key = zend_hash_str_find(Z_ARRVAL_P(data), "priv_key", sizeof("priv_key") - 1); if (priv_key && Z_TYPE_P(priv_key) == IS_STRING && Z_STRLEN_P(priv_key) > 0) { if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PRIV_KEY, Z_STRVAL_P(priv_key), Z_STRLEN_P(priv_key))) { @@ -4724,11 +4732,11 @@ static void php_openssl_pkey_object_curve_25519_448(zval *return_value, int key_ } params = OSSL_PARAM_BLD_to_param(bld); - if (!params) { + ctx = EVP_PKEY_CTX_new_id(key_type, NULL); + if (!params || !ctx) { goto cleanup; } - ctx = EVP_PKEY_CTX_new_id(key_type, NULL); if (pub_key || priv_key) { if (EVP_PKEY_fromdata_init(ctx) <= 0 || EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) { From 0153a5557873fe66a0028e2e7d9c8c281665dbe7 Mon Sep 17 00:00:00 2001 From: Manuel Mausz Date: Sun, 28 Apr 2024 21:35:15 +0200 Subject: [PATCH 3/3] Update openssl_arginfo.h --- ext/openssl/openssl_arginfo.h | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/ext/openssl/openssl_arginfo.h b/ext/openssl/openssl_arginfo.h index e48381624eb4..26cf850981b5 100644 --- a/ext/openssl/openssl_arginfo.h +++ b/ext/openssl/openssl_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 295c11739a3588a8254db2fe904be6f9911f85b3 */ + * Stub hash: 1867498c895461f81aad433c21cf899ffdb65b3d */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_openssl_x509_export_to_file, 0, 2, _IS_BOOL, 0) ZEND_ARG_OBJ_TYPE_MASK(0, certificate, OpenSSLCertificate, MAY_BE_STRING, NULL) @@ -622,6 +622,18 @@ static void register_openssl_symbols(int module_number) REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_DH", OPENSSL_KEYTYPE_DH, CONST_PERSISTENT); #if defined(HAVE_EVP_PKEY_EC) REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_EC", OPENSSL_KEYTYPE_EC, CONST_PERSISTENT); +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_X25519", OPENSSL_KEYTYPE_X25519, CONST_PERSISTENT); +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_ED25519", OPENSSL_KEYTYPE_ED25519, CONST_PERSISTENT); +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_X448", OPENSSL_KEYTYPE_X448, CONST_PERSISTENT); +#endif +#if PHP_OPENSSL_API_VERSION >= 0x30000 + REGISTER_LONG_CONSTANT("OPENSSL_KEYTYPE_ED448", OPENSSL_KEYTYPE_ED448, CONST_PERSISTENT); #endif REGISTER_LONG_CONSTANT("OPENSSL_RAW_DATA", OPENSSL_RAW_DATA, CONST_PERSISTENT); REGISTER_LONG_CONSTANT("OPENSSL_ZERO_PADDING", OPENSSL_ZERO_PADDING, CONST_PERSISTENT);