mirror of
https://github.com/php/php-src.git
synced 2025-08-16 05:58:45 +02:00
834 lines
24 KiB
C
834 lines
24 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 3.01 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available through the world-wide-web at the following url: |
|
|
| https://www.php.net/license/3_01.txt |
|
|
| If you did not receive a copy of the PHP license and are unable to |
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
| license@php.net so we can mail you a copy immediately. |
|
|
+----------------------------------------------------------------------+
|
|
| Authors: Jakub Zelenka <bukka@php.net> |
|
|
+----------------------------------------------------------------------+
|
|
*/
|
|
|
|
#include "php_openssl_backend.h"
|
|
|
|
#if PHP_OPENSSL_API_VERSION >= 0x30000
|
|
|
|
#include <openssl/core_names.h>
|
|
#include <openssl/param_build.h>
|
|
#include <openssl/provider.h>
|
|
|
|
ZEND_EXTERN_MODULE_GLOBALS(openssl)
|
|
|
|
void php_openssl_backend_shutdown(void)
|
|
{
|
|
(void) 0;
|
|
}
|
|
|
|
#define PHP_OPENSSL_DEFAULT_CONF_MFLAGS \
|
|
(CONF_MFLAGS_DEFAULT_SECTION | CONF_MFLAGS_IGNORE_MISSING_FILE | CONF_MFLAGS_IGNORE_RETURN_CODES)
|
|
|
|
void php_openssl_backend_init_libctx(struct php_openssl_libctx *ctx)
|
|
{
|
|
ctx->default_libctx = OSSL_LIB_CTX_get0_global_default();
|
|
ctx->custom_libctx = OSSL_LIB_CTX_new();
|
|
if (ctx->custom_libctx != NULL) {
|
|
/* This is not being checked because there is not much that can be done. */
|
|
CONF_modules_load_file_ex(ctx->custom_libctx, NULL, NULL,
|
|
PHP_OPENSSL_DEFAULT_CONF_MFLAGS);
|
|
#ifdef LOAD_OPENSSL_LEGACY_PROVIDER
|
|
OSSL_PROVIDER_load(ctx->custom_libctx, "legacy");
|
|
OSSL_PROVIDER_load(ctx->custom_libctx, "default");
|
|
#endif
|
|
ctx->libctx = ctx->custom_libctx;
|
|
} else {
|
|
/* If creation fails, just fallback to default */
|
|
ctx->libctx = ctx->default_libctx;
|
|
}
|
|
ctx->propq = NULL;
|
|
}
|
|
|
|
void php_openssl_backend_destroy_libctx(struct php_openssl_libctx *ctx)
|
|
{
|
|
if (ctx->custom_libctx != NULL) {
|
|
OSSL_LIB_CTX_free(ctx->custom_libctx);
|
|
}
|
|
if (ctx->propq != NULL) {
|
|
free(ctx->propq);
|
|
}
|
|
}
|
|
|
|
EVP_PKEY_CTX *php_openssl_pkey_new_from_name(const char *name, int id)
|
|
{
|
|
return EVP_PKEY_CTX_new_from_name(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
|
|
}
|
|
|
|
EVP_PKEY_CTX *php_openssl_pkey_new_from_pkey(EVP_PKEY *pkey)
|
|
{
|
|
return EVP_PKEY_CTX_new_from_pkey(PHP_OPENSSL_LIBCTX, pkey, PHP_OPENSSL_PROPQ);
|
|
}
|
|
|
|
EVP_PKEY *php_openssl_pkey_init_rsa(zval *data)
|
|
{
|
|
BIGNUM *n = NULL, *e = NULL, *d = NULL, *p = NULL, *q = NULL;
|
|
BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
|
|
EVP_PKEY *pkey = NULL;
|
|
EVP_PKEY_CTX *ctx = php_openssl_pkey_new_from_name("RSA", EVP_PKEY_RSA);
|
|
OSSL_PARAM *params = NULL;
|
|
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
|
|
|
|
OPENSSL_PKEY_SET_BN(data, n);
|
|
OPENSSL_PKEY_SET_BN(data, e);
|
|
OPENSSL_PKEY_SET_BN(data, d);
|
|
OPENSSL_PKEY_SET_BN(data, p);
|
|
OPENSSL_PKEY_SET_BN(data, q);
|
|
OPENSSL_PKEY_SET_BN(data, dmp1);
|
|
OPENSSL_PKEY_SET_BN(data, dmq1);
|
|
OPENSSL_PKEY_SET_BN(data, iqmp);
|
|
|
|
if (!ctx || !bld || !n || !d) {
|
|
goto cleanup;
|
|
}
|
|
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_N, n);
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_D, d);
|
|
if (e) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_E, e);
|
|
}
|
|
if (p) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR1, p);
|
|
}
|
|
if (q) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_FACTOR2, q);
|
|
}
|
|
if (dmp1) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT1, dmp1);
|
|
}
|
|
if (dmq1) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_EXPONENT2, dmq1);
|
|
}
|
|
if (iqmp) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, iqmp);
|
|
}
|
|
|
|
params = OSSL_PARAM_BLD_to_param(bld);
|
|
if (!params) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
|
|
EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
php_openssl_store_errors();
|
|
EVP_PKEY_CTX_free(ctx);
|
|
OSSL_PARAM_free(params);
|
|
OSSL_PARAM_BLD_free(bld);
|
|
BN_free(n);
|
|
BN_free(e);
|
|
BN_free(d);
|
|
BN_free(p);
|
|
BN_free(q);
|
|
BN_free(dmp1);
|
|
BN_free(dmq1);
|
|
BN_free(iqmp);
|
|
return pkey;
|
|
}
|
|
|
|
EVP_PKEY *php_openssl_pkey_init_dsa(zval *data, bool *is_private)
|
|
{
|
|
BIGNUM *p = NULL, *q = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL;
|
|
EVP_PKEY *param_key = NULL, *pkey = NULL;
|
|
EVP_PKEY_CTX *ctx = php_openssl_pkey_new_from_name("DSA", EVP_PKEY_DSA);
|
|
OSSL_PARAM *params = NULL;
|
|
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
|
|
|
|
OPENSSL_PKEY_SET_BN(data, p);
|
|
OPENSSL_PKEY_SET_BN(data, q);
|
|
OPENSSL_PKEY_SET_BN(data, g);
|
|
OPENSSL_PKEY_SET_BN(data, priv_key);
|
|
OPENSSL_PKEY_SET_BN(data, pub_key);
|
|
|
|
*is_private = false;
|
|
|
|
if (!ctx || !bld || !p || !q || !g) {
|
|
goto cleanup;
|
|
}
|
|
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p);
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q);
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g);
|
|
// TODO: We silently ignore priv_key if pub_key is not given, unlike in the DH case.
|
|
if (pub_key) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key);
|
|
if (priv_key) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_key);
|
|
}
|
|
}
|
|
|
|
params = OSSL_PARAM_BLD_to_param(bld);
|
|
if (!params) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
|
|
EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pub_key) {
|
|
*is_private = priv_key != NULL;
|
|
EVP_PKEY_up_ref(param_key);
|
|
pkey = param_key;
|
|
} else {
|
|
*is_private = true;
|
|
EVP_PKEY_CTX_free(ctx);
|
|
ctx = php_openssl_pkey_new_from_pkey(param_key);
|
|
if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
php_openssl_store_errors();
|
|
EVP_PKEY_free(param_key);
|
|
EVP_PKEY_CTX_free(ctx);
|
|
OSSL_PARAM_free(params);
|
|
OSSL_PARAM_BLD_free(bld);
|
|
BN_free(p);
|
|
BN_free(q);
|
|
BN_free(g);
|
|
BN_free(priv_key);
|
|
BN_free(pub_key);
|
|
return pkey;
|
|
}
|
|
|
|
EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private)
|
|
{
|
|
BIGNUM *p = NULL, *q = NULL, *g = NULL, *priv_key = NULL, *pub_key = NULL;
|
|
EVP_PKEY *param_key = NULL, *pkey = NULL;
|
|
EVP_PKEY_CTX *ctx = php_openssl_pkey_new_from_name("DH", EVP_PKEY_DH);
|
|
OSSL_PARAM *params = NULL;
|
|
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
|
|
|
|
OPENSSL_PKEY_SET_BN(data, p);
|
|
OPENSSL_PKEY_SET_BN(data, q);
|
|
OPENSSL_PKEY_SET_BN(data, g);
|
|
OPENSSL_PKEY_SET_BN(data, priv_key);
|
|
OPENSSL_PKEY_SET_BN(data, pub_key);
|
|
|
|
*is_private = false;
|
|
|
|
if (!ctx || !bld || !p || !g) {
|
|
goto cleanup;
|
|
}
|
|
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_P, p);
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_G, g);
|
|
if (q) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_FFC_Q, q);
|
|
}
|
|
if (priv_key) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, priv_key);
|
|
if (!pub_key) {
|
|
pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
|
|
if (!pub_key) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
if (pub_key) {
|
|
OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PUB_KEY, pub_key);
|
|
}
|
|
|
|
params = OSSL_PARAM_BLD_to_param(bld);
|
|
if (!params) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
|
|
EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (pub_key || priv_key) {
|
|
*is_private = priv_key != NULL;
|
|
EVP_PKEY_up_ref(param_key);
|
|
pkey = param_key;
|
|
} else {
|
|
*is_private = true;
|
|
EVP_PKEY_CTX_free(ctx);
|
|
ctx = php_openssl_pkey_new_from_pkey(param_key);
|
|
if (EVP_PKEY_keygen_init(ctx) <= 0 || EVP_PKEY_keygen(ctx, &pkey) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
php_openssl_store_errors();
|
|
EVP_PKEY_free(param_key);
|
|
EVP_PKEY_CTX_free(ctx);
|
|
OSSL_PARAM_free(params);
|
|
OSSL_PARAM_BLD_free(bld);
|
|
BN_free(p);
|
|
BN_free(q);
|
|
BN_free(g);
|
|
BN_free(priv_key);
|
|
BN_free(pub_key);
|
|
return pkey;
|
|
}
|
|
|
|
#ifdef HAVE_EVP_PKEY_EC
|
|
EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
|
|
int nid = NID_undef;
|
|
BIGNUM *p = NULL, *a = NULL, *b = NULL, *order = NULL, *g_x = NULL, *g_y = NULL, *cofactor = NULL;
|
|
BIGNUM *x = NULL, *y = NULL, *d = NULL;
|
|
EC_POINT *point_g = NULL;
|
|
EC_POINT *point_q = NULL;
|
|
unsigned char *point_g_buf = NULL;
|
|
unsigned char *point_q_buf = NULL;
|
|
EC_GROUP *group = NULL;
|
|
EVP_PKEY *param_key = NULL, *pkey = NULL;
|
|
EVP_PKEY_CTX *ctx = php_openssl_pkey_new_from_name("EC", EVP_PKEY_EC);
|
|
BN_CTX *bctx = BN_CTX_new();
|
|
OSSL_PARAM *params = NULL;
|
|
OSSL_PARAM_BLD *bld = OSSL_PARAM_BLD_new();
|
|
|
|
*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));
|
|
if (nid == NID_undef) {
|
|
php_error_docref(NULL, E_WARNING, "Unknown elliptic curve (short) name %s", Z_STRVAL_P(curve_name_zv));
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(group = EC_GROUP_new_by_curve_name_ex(PHP_OPENSSL_LIBCTX, PHP_OPENSSL_PROPQ, nid))) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_GROUP_NAME, Z_STRVAL_P(curve_name_zv), Z_STRLEN_P(curve_name_zv))) {
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
OPENSSL_PKEY_SET_BN(data, p);
|
|
OPENSSL_PKEY_SET_BN(data, a);
|
|
OPENSSL_PKEY_SET_BN(data, b);
|
|
OPENSSL_PKEY_SET_BN(data, order);
|
|
|
|
if (!(p && a && b && order)) {
|
|
if (!p && !a && !b && !order) {
|
|
php_error_docref(NULL, E_WARNING, "Missing params: curve_name");
|
|
} else {
|
|
php_error_docref(NULL, E_WARNING, "Missing params: curve_name or p, a, b, order");
|
|
}
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_P, p) ||
|
|
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_A, a) ||
|
|
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_B, b) ||
|
|
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_ORDER, order) ||
|
|
!OSSL_PARAM_BLD_push_utf8_string(bld, OSSL_PKEY_PARAM_EC_FIELD_TYPE, SN_X9_62_prime_field, 0)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(group = EC_GROUP_new_curve_GFp(p, a, b, bctx))) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!(point_g = EC_POINT_new(group))) {
|
|
goto cleanup;
|
|
}
|
|
|
|
zval *generator_zv = zend_hash_str_find(Z_ARRVAL_P(data), "generator", sizeof("generator") - 1);
|
|
if (generator_zv && Z_TYPE_P(generator_zv) == IS_STRING && Z_STRLEN_P(generator_zv) > 0) {
|
|
if (!EC_POINT_oct2point(group, point_g, (unsigned char *)Z_STRVAL_P(generator_zv), Z_STRLEN_P(generator_zv), bctx) ||
|
|
!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, Z_STRVAL_P(generator_zv), Z_STRLEN_P(generator_zv))) {
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
OPENSSL_PKEY_SET_BN(data, g_x);
|
|
OPENSSL_PKEY_SET_BN(data, g_y);
|
|
|
|
if (!g_x || !g_y) {
|
|
php_error_docref(
|
|
NULL, E_WARNING, "Missing params: generator or g_x and g_y");
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!EC_POINT_set_affine_coordinates(group, point_g, g_x, g_y, bctx)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
size_t point_g_buf_len =
|
|
EC_POINT_point2buf(group, point_g, POINT_CONVERSION_COMPRESSED, &point_g_buf, bctx);
|
|
if (!point_g_buf_len) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_GENERATOR, point_g_buf, point_g_buf_len)) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
zval *seed_zv = zend_hash_str_find(Z_ARRVAL_P(data), "seed", sizeof("seed") - 1);
|
|
if (seed_zv && Z_TYPE_P(seed_zv) == IS_STRING && Z_STRLEN_P(seed_zv) > 0) {
|
|
if (!EC_GROUP_set_seed(group, (unsigned char *)Z_STRVAL_P(seed_zv), Z_STRLEN_P(seed_zv)) ||
|
|
!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_EC_SEED, Z_STRVAL_P(seed_zv), Z_STRLEN_P(seed_zv))) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
OPENSSL_PKEY_SET_BN(data, cofactor);
|
|
if (!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_EC_COFACTOR, cofactor) ||
|
|
!EC_GROUP_set_generator(group, point_g, order, cofactor)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
nid = EC_GROUP_check_named_curve(group, 0, bctx);
|
|
}
|
|
|
|
/* custom params not supported with SM2, SKIP */
|
|
if (nid != NID_sm2) {
|
|
OPENSSL_PKEY_SET_BN(data, d);
|
|
OPENSSL_PKEY_SET_BN(data, x);
|
|
OPENSSL_PKEY_SET_BN(data, y);
|
|
|
|
if (d) {
|
|
point_q = EC_POINT_new(group);
|
|
if (!point_q || !EC_POINT_mul(group, point_q, d, NULL, NULL, bctx) ||
|
|
!OSSL_PARAM_BLD_push_BN(bld, OSSL_PKEY_PARAM_PRIV_KEY, d)) {
|
|
goto cleanup;
|
|
}
|
|
} else if (x && y) {
|
|
/* OpenSSL does not allow setting EC_PUB_X/EC_PUB_Y, so convert to encoded format. */
|
|
point_q = EC_POINT_new(group);
|
|
if (!point_q || !EC_POINT_set_affine_coordinates(group, point_q, x, y, bctx)) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (point_q) {
|
|
size_t point_q_buf_len =
|
|
EC_POINT_point2buf(group, point_q, POINT_CONVERSION_COMPRESSED, &point_q_buf, bctx);
|
|
if (!point_q_buf_len ||
|
|
!OSSL_PARAM_BLD_push_octet_string(bld, OSSL_PKEY_PARAM_PUB_KEY, point_q_buf, point_q_buf_len)) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
params = OSSL_PARAM_BLD_to_param(bld);
|
|
if (!params) {
|
|
goto cleanup;
|
|
}
|
|
|
|
if (d || (x && y)) {
|
|
if (EVP_PKEY_fromdata_init(ctx) <= 0 ||
|
|
EVP_PKEY_fromdata(ctx, ¶m_key, EVP_PKEY_KEYPAIR, params) <= 0) {
|
|
goto cleanup;
|
|
}
|
|
EVP_PKEY_CTX_free(ctx);
|
|
ctx = EVP_PKEY_CTX_new(param_key, NULL);
|
|
}
|
|
|
|
if (EVP_PKEY_check(ctx) || EVP_PKEY_public_check_quick(ctx)) {
|
|
*is_private = d != NULL;
|
|
EVP_PKEY_up_ref(param_key);
|
|
pkey = param_key;
|
|
} else {
|
|
*is_private = true;
|
|
if (EVP_PKEY_keygen_init(ctx) != 1 ||
|
|
EVP_PKEY_CTX_set_params(ctx, params) != 1 ||
|
|
EVP_PKEY_generate(ctx, &pkey) != 1) {
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
cleanup:
|
|
php_openssl_store_errors();
|
|
EVP_PKEY_free(param_key);
|
|
EVP_PKEY_CTX_free(ctx);
|
|
BN_CTX_free(bctx);
|
|
OSSL_PARAM_free(params);
|
|
OSSL_PARAM_BLD_free(bld);
|
|
EC_GROUP_free(group);
|
|
EC_POINT_free(point_g);
|
|
EC_POINT_free(point_q);
|
|
OPENSSL_free(point_g_buf);
|
|
OPENSSL_free(point_q_buf);
|
|
BN_free(p);
|
|
BN_free(a);
|
|
BN_free(b);
|
|
BN_free(order);
|
|
BN_free(g_x);
|
|
BN_free(g_y);
|
|
BN_free(cofactor);
|
|
BN_free(d);
|
|
BN_free(x);
|
|
BN_free(y);
|
|
return pkey;
|
|
}
|
|
#endif
|
|
|
|
void php_openssl_pkey_object_curve_25519_448(zval *return_value, const char *name, 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;
|
|
|
|
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))) {
|
|
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);
|
|
ctx = php_openssl_pkey_new_from_name(name, 0);
|
|
if (!params || !ctx) {
|
|
goto cleanup;
|
|
}
|
|
|
|
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;
|
|
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);
|
|
}
|
|
|
|
static void php_openssl_copy_bn_param(
|
|
zval *ary, EVP_PKEY *pkey, const char *param, const char *name) {
|
|
BIGNUM *bn = NULL;
|
|
if (EVP_PKEY_get_bn_param(pkey, param, &bn) > 0) {
|
|
php_openssl_add_bn_to_array(ary, bn, name);
|
|
BN_free(bn);
|
|
}
|
|
}
|
|
|
|
#ifdef HAVE_EVP_PKEY_EC
|
|
static zend_string *php_openssl_get_utf8_param(
|
|
EVP_PKEY *pkey, const char *param, const char *name) {
|
|
char buf[64];
|
|
size_t len;
|
|
if (EVP_PKEY_get_utf8_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';
|
|
return str;
|
|
}
|
|
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");
|
|
}
|
|
|
|
zend_long php_openssl_pkey_get_details(zval *return_value, EVP_PKEY *pkey)
|
|
{
|
|
zval ary;
|
|
int base_id = 0;
|
|
zend_long ktype;
|
|
|
|
if (EVP_PKEY_id(pkey) != EVP_PKEY_KEYMGMT) {
|
|
base_id = EVP_PKEY_base_id(pkey);
|
|
} else {
|
|
const char *type_name = EVP_PKEY_get0_type_name(pkey);
|
|
if (type_name) {
|
|
int nid = OBJ_txt2nid(type_name);
|
|
if (nid != NID_undef) {
|
|
base_id = EVP_PKEY_type(nid);
|
|
}
|
|
}
|
|
}
|
|
|
|
switch (base_id) {
|
|
case EVP_PKEY_RSA:
|
|
ktype = OPENSSL_KEYTYPE_RSA;
|
|
array_init(&ary);
|
|
add_assoc_zval(return_value, "rsa", &ary);
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_N, "n");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_E, "e");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_D, "d");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_FACTOR1, "p");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_FACTOR2, "q");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_EXPONENT1, "dmp1");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_EXPONENT2, "dmq1");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_RSA_COEFFICIENT1, "iqmp");
|
|
break;
|
|
case EVP_PKEY_DSA:
|
|
ktype = OPENSSL_KEYTYPE_DSA;
|
|
array_init(&ary);
|
|
add_assoc_zval(return_value, "dsa", &ary);
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_P, "p");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_Q, "q");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_G, "g");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "priv_key");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PUB_KEY, "pub_key");
|
|
break;
|
|
case EVP_PKEY_DH:
|
|
ktype = OPENSSL_KEYTYPE_DH;
|
|
array_init(&ary);
|
|
add_assoc_zval(return_value, "dh", &ary);
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_P, "p");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_FFC_G, "g");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "priv_key");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PUB_KEY, "pub_key");
|
|
break;
|
|
#ifdef HAVE_EVP_PKEY_EC
|
|
case EVP_PKEY_EC: {
|
|
ktype = OPENSSL_KEYTYPE_EC;
|
|
array_init(&ary);
|
|
add_assoc_zval(return_value, "ec", &ary);
|
|
|
|
zend_string *curve_name = php_openssl_get_utf8_param(
|
|
pkey, OSSL_PKEY_PARAM_GROUP_NAME, "curve_name");
|
|
if (curve_name) {
|
|
add_assoc_str(&ary, "curve_name", curve_name);
|
|
|
|
int nid = OBJ_sn2nid(ZSTR_VAL(curve_name));
|
|
if (nid != NID_undef) {
|
|
ASN1_OBJECT *obj = OBJ_nid2obj(nid);
|
|
if (obj) {
|
|
// OpenSSL recommends a buffer length of 80.
|
|
char oir_buf[80];
|
|
int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
|
|
add_assoc_stringl(&ary, "curve_oid", oir_buf, oir_len);
|
|
ASN1_OBJECT_free(obj);
|
|
}
|
|
}
|
|
}
|
|
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_EC_PUB_X, "x");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_EC_PUB_Y, "y");
|
|
php_openssl_copy_bn_param(&ary, pkey, OSSL_PKEY_PARAM_PRIV_KEY, "d");
|
|
break;
|
|
}
|
|
#endif
|
|
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;
|
|
}
|
|
default:
|
|
ktype = -1;
|
|
break;
|
|
}
|
|
|
|
return ktype;
|
|
}
|
|
|
|
zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pub_len)
|
|
{
|
|
EVP_PKEY *peer_key = EVP_PKEY_new();
|
|
if (!peer_key || EVP_PKEY_copy_parameters(peer_key, pkey) <= 0 ||
|
|
EVP_PKEY_set1_encoded_public_key(peer_key, (unsigned char *) pub_str, pub_len) <= 0) {
|
|
php_openssl_store_errors();
|
|
EVP_PKEY_free(peer_key);
|
|
return NULL;
|
|
}
|
|
|
|
zend_string *result = php_openssl_pkey_derive(pkey, peer_key, 0);
|
|
EVP_PKEY_free(peer_key);
|
|
return result;
|
|
}
|
|
|
|
const EVP_MD *php_openssl_get_evp_md_by_name(const char *name)
|
|
{
|
|
return EVP_MD_fetch(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
|
|
}
|
|
|
|
static const char *php_openssl_digest_names[] = {
|
|
[OPENSSL_ALGO_SHA1] = "SHA1",
|
|
[OPENSSL_ALGO_MD5] = "MD5",
|
|
#ifndef OPENSSL_NO_MD4
|
|
[OPENSSL_ALGO_MD4] = "MD4",
|
|
#endif
|
|
#ifndef OPENSSL_NO_MD2
|
|
[OPENSSL_ALGO_MD2] = "MD2",
|
|
#endif
|
|
[OPENSSL_ALGO_SHA224] = "SHA224",
|
|
[OPENSSL_ALGO_SHA256] = "SHA256",
|
|
[OPENSSL_ALGO_SHA384] = "SHA384",
|
|
[OPENSSL_ALGO_SHA512] = "SHA512",
|
|
#ifndef OPENSSL_NO_RMD160
|
|
[OPENSSL_ALGO_RMD160] = "RIPEMD160",
|
|
#endif
|
|
};
|
|
|
|
const EVP_MD *php_openssl_get_evp_md_from_algo(zend_long algo)
|
|
{
|
|
if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_digest_names) / sizeof(*php_openssl_digest_names))) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *name = php_openssl_digest_names[algo];
|
|
if (!name) {
|
|
return NULL;
|
|
}
|
|
|
|
return php_openssl_get_evp_md_by_name(name);
|
|
}
|
|
|
|
void php_openssl_release_evp_md(const EVP_MD *md)
|
|
{
|
|
if (md != NULL) {
|
|
// It is fine to remove const as the md is from EVP_MD_fetch
|
|
EVP_MD_free((EVP_MD *) md);
|
|
}
|
|
}
|
|
|
|
static const char *php_openssl_cipher_names[] = {
|
|
[PHP_OPENSSL_CIPHER_RC2_40] = "RC2-40-CBC",
|
|
[PHP_OPENSSL_CIPHER_RC2_128] = "RC2-CBC",
|
|
[PHP_OPENSSL_CIPHER_RC2_64] = "RC2-64-CBC",
|
|
[PHP_OPENSSL_CIPHER_DES] = "DES-CBC",
|
|
[PHP_OPENSSL_CIPHER_3DES] = "DES-EDE3-CBC",
|
|
[PHP_OPENSSL_CIPHER_AES_128_CBC]= "AES-128-CBC",
|
|
[PHP_OPENSSL_CIPHER_AES_192_CBC]= "AES-192-CBC",
|
|
[PHP_OPENSSL_CIPHER_AES_256_CBC]= "AES-256-CBC",
|
|
};
|
|
|
|
const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *name)
|
|
{
|
|
return EVP_CIPHER_fetch(PHP_OPENSSL_LIBCTX, name, PHP_OPENSSL_PROPQ);
|
|
}
|
|
|
|
const EVP_CIPHER *php_openssl_get_evp_cipher_from_algo(zend_long algo)
|
|
{
|
|
if (algo < 0 || algo >= (zend_long)(sizeof(php_openssl_cipher_names) / sizeof(*php_openssl_cipher_names))) {
|
|
return NULL;
|
|
}
|
|
|
|
const char *name = php_openssl_cipher_names[algo];
|
|
if (!name) {
|
|
return NULL;
|
|
}
|
|
|
|
return php_openssl_get_evp_cipher_by_name(name);
|
|
}
|
|
|
|
void php_openssl_release_evp_cipher(const EVP_CIPHER *cipher)
|
|
{
|
|
if (cipher != NULL) {
|
|
// It is fine to remove const as the cipher is from EVP_CIPHER_fetch
|
|
EVP_CIPHER_free((EVP_CIPHER *) cipher);
|
|
}
|
|
}
|
|
|
|
static void php_openssl_add_cipher_name(const char *name, void *arg)
|
|
{
|
|
size_t len = strlen(name);
|
|
zend_string *str = zend_string_alloc(len, 0);
|
|
zend_str_tolower_copy(ZSTR_VAL(str), name, len);
|
|
add_next_index_str((zval*)arg, str);
|
|
}
|
|
|
|
static void php_openssl_add_cipher_or_alias(EVP_CIPHER *cipher, void *arg)
|
|
{
|
|
EVP_CIPHER_names_do_all(cipher, php_openssl_add_cipher_name, arg);
|
|
}
|
|
|
|
static void php_openssl_add_cipher(EVP_CIPHER *cipher, void *arg)
|
|
{
|
|
php_openssl_add_cipher_name(EVP_CIPHER_get0_name(cipher), arg);
|
|
}
|
|
|
|
static int php_openssl_compare_func(Bucket *a, Bucket *b)
|
|
{
|
|
return string_compare_function(&a->val, &b->val);
|
|
}
|
|
|
|
void php_openssl_get_cipher_methods(zval *return_value, bool aliases)
|
|
{
|
|
array_init(return_value);
|
|
EVP_CIPHER_do_all_provided(PHP_OPENSSL_LIBCTX,
|
|
aliases ? php_openssl_add_cipher_or_alias : php_openssl_add_cipher,
|
|
return_value);
|
|
zend_hash_sort(Z_ARRVAL_P(return_value), php_openssl_compare_func, 1);
|
|
}
|
|
|
|
CONF *php_openssl_nconf_new(void)
|
|
{
|
|
return NCONF_new_ex(PHP_OPENSSL_LIBCTX, NULL);
|
|
}
|
|
|
|
#endif
|