mirror of
https://github.com/php/php-src.git
synced 2025-08-15 13:38:49 +02:00
Introduce openssl backend for v1 and v3 API separation (#16918)
The main purpose of this is to better handle the API difference and add an inital work to separate PHP and OpenSSL logic. This is really just the first step and further changes are coming after that. Closes GH-16918
This commit is contained in:
parent
cd306661a2
commit
d662ab5f08
8 changed files with 4012 additions and 3753 deletions
|
@ -10,7 +10,7 @@ if (PHP_OPENSSL != "no") {
|
|||
var ret = SETUP_OPENSSL("openssl", PHP_OPENSSL);
|
||||
|
||||
if (ret >= 2) {
|
||||
EXTENSION("openssl", "openssl.c openssl_pwhash.c xp_ssl.c");
|
||||
EXTENSION("openssl", "openssl.c openssl_pwhash.c openssl_backend_common.c openssl_backend_v1.c openssl_backend_v3.c xp_ssl.c");
|
||||
AC_DEFINE("HAVE_OPENSSL_EXT", 1, "Define to 1 if the PHP extension 'openssl' is available.");
|
||||
if (PHP_OPENSSL_LEGACY_PROVIDER != "no") {
|
||||
AC_DEFINE("LOAD_OPENSSL_LEGACY_PROVIDER", 1, "Define to 1 to load the OpenSSL legacy algorithm provider in addition to the default provider.");
|
||||
|
|
|
@ -26,7 +26,7 @@ PHP_ARG_WITH([openssl-argon2],
|
|||
|
||||
if test "$PHP_OPENSSL" != "no"; then
|
||||
PHP_NEW_EXTENSION([openssl],
|
||||
[openssl.c openssl_pwhash.c xp_ssl.c],
|
||||
[openssl.c openssl_pwhash.c openssl_backend_common.c openssl_backend_v1.c openssl_backend_v3.c xp_ssl.c],
|
||||
[$ext_shared])
|
||||
PHP_SUBST([OPENSSL_SHARED_LIBADD])
|
||||
PHP_SETUP_OPENSSL([OPENSSL_SHARED_LIBADD],
|
||||
|
|
File diff suppressed because it is too large
Load diff
2134
ext/openssl/openssl_backend_common.c
Normal file
2134
ext/openssl/openssl_backend_common.c
Normal file
File diff suppressed because it is too large
Load diff
708
ext/openssl/openssl_backend_v1.c
Normal file
708
ext/openssl/openssl_backend_v1.c
Normal file
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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/bn.h>
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/dsa.h>
|
||||
#include <openssl/dh.h>
|
||||
|
||||
#if defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_NO_ENGINE)
|
||||
#include <openssl/engine.h>
|
||||
#endif
|
||||
|
||||
/* OpenSSL compatibility functions and macros */
|
||||
#if PHP_OPENSSL_API_VERSION < 0x10100
|
||||
|
||||
#define EVP_PKEY_get0_RSA(_pkey) _pkey->pkey.rsa
|
||||
#define EVP_PKEY_get0_DH(_pkey) _pkey->pkey.dh
|
||||
#define EVP_PKEY_get0_DSA(_pkey) _pkey->pkey.dsa
|
||||
#define EVP_PKEY_get0_EC_KEY(_pkey) _pkey->pkey.ec
|
||||
|
||||
static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
{
|
||||
r->n = n;
|
||||
r->e = e;
|
||||
r->d = d;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q)
|
||||
{
|
||||
r->p = p;
|
||||
r->q = q;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp)
|
||||
{
|
||||
r->dmp1 = dmp1;
|
||||
r->dmq1 = dmq1;
|
||||
r->iqmp = iqmp;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void RSA_get0_key(const RSA *r, const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
|
||||
{
|
||||
*n = r->n;
|
||||
*e = r->e;
|
||||
*d = r->d;
|
||||
}
|
||||
|
||||
static void RSA_get0_factors(const RSA *r, const BIGNUM **p, const BIGNUM **q)
|
||||
{
|
||||
*p = r->p;
|
||||
*q = r->q;
|
||||
}
|
||||
|
||||
static void RSA_get0_crt_params(const RSA *r, const BIGNUM **dmp1, const BIGNUM **dmq1, const BIGNUM **iqmp)
|
||||
{
|
||||
*dmp1 = r->dmp1;
|
||||
*dmq1 = r->dmq1;
|
||||
*iqmp = r->iqmp;
|
||||
}
|
||||
|
||||
static void DH_get0_pqg(const DH *dh, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
*p = dh->p;
|
||||
*q = dh->q;
|
||||
*g = dh->g;
|
||||
}
|
||||
|
||||
static int DH_set0_pqg(DH *dh, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{
|
||||
dh->p = p;
|
||||
dh->q = q;
|
||||
dh->g = g;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void DH_get0_key(const DH *dh, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{
|
||||
*pub_key = dh->pub_key;
|
||||
*priv_key = dh->priv_key;
|
||||
}
|
||||
|
||||
static int DH_set0_key(DH *dh, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
dh->pub_key = pub_key;
|
||||
dh->priv_key = priv_key;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void DSA_get0_pqg(const DSA *d, const BIGNUM **p, const BIGNUM **q, const BIGNUM **g)
|
||||
{
|
||||
*p = d->p;
|
||||
*q = d->q;
|
||||
*g = d->g;
|
||||
}
|
||||
|
||||
int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g)
|
||||
{
|
||||
d->p = p;
|
||||
d->q = q;
|
||||
d->g = g;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void DSA_get0_key(const DSA *d, const BIGNUM **pub_key, const BIGNUM **priv_key)
|
||||
{
|
||||
*pub_key = d->pub_key;
|
||||
*priv_key = d->priv_key;
|
||||
}
|
||||
|
||||
int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key)
|
||||
{
|
||||
d->pub_key = pub_key;
|
||||
d->priv_key = priv_key;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1)
|
||||
{
|
||||
return M_ASN1_STRING_data(asn1);
|
||||
}
|
||||
|
||||
static int EVP_PKEY_up_ref(EVP_PKEY *pkey)
|
||||
{
|
||||
return CRYPTO_add(&pkey->references, 1, CRYPTO_LOCK_EVP_PKEY);
|
||||
}
|
||||
|
||||
#if PHP_OPENSSL_API_VERSION < 0x10002
|
||||
|
||||
static int X509_get_signature_nid(const X509 *x)
|
||||
{
|
||||
return OBJ_obj2nid(x->sig_alg->algorithm);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#define OpenSSL_version SSLeay_version
|
||||
#define OPENSSL_VERSION SSLEAY_VERSION
|
||||
#define X509_getm_notBefore X509_get_notBefore
|
||||
#define X509_getm_notAfter X509_get_notAfter
|
||||
#define EVP_CIPHER_CTX_reset EVP_CIPHER_CTX_cleanup
|
||||
|
||||
#endif
|
||||
|
||||
void php_openssl_backend_shutdown(void)
|
||||
{
|
||||
#ifdef LIBRESSL_VERSION_NUMBER
|
||||
EVP_cleanup();
|
||||
|
||||
/* prevent accessing locking callback from unloaded extension */
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
|
||||
#ifndef OPENSSL_NO_ENGINE
|
||||
/* Free engine list initialized by OPENSSL_config */
|
||||
ENGINE_cleanup();
|
||||
#endif
|
||||
|
||||
/* free allocated error strings */
|
||||
ERR_free_strings();
|
||||
CONF_modules_free();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool php_openssl_pkey_init_rsa_data(RSA *rsa, zval *data)
|
||||
{
|
||||
BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, n);
|
||||
OPENSSL_PKEY_SET_BN(data, e);
|
||||
OPENSSL_PKEY_SET_BN(data, d);
|
||||
if (!n || !d || !RSA_set0_key(rsa, n, e, d)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, p);
|
||||
OPENSSL_PKEY_SET_BN(data, q);
|
||||
if ((p || q) && !RSA_set0_factors(rsa, p, q)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, dmp1);
|
||||
OPENSSL_PKEY_SET_BN(data, dmq1);
|
||||
OPENSSL_PKEY_SET_BN(data, iqmp);
|
||||
if ((dmp1 || dmq1 || iqmp) && !RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVP_PKEY *php_openssl_pkey_init_rsa(zval *data)
|
||||
{
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (!pkey) {
|
||||
php_openssl_store_errors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RSA *rsa = RSA_new();
|
||||
if (!rsa) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!php_openssl_pkey_init_rsa_data(rsa, data) || !EVP_PKEY_assign_RSA(pkey, rsa)) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
RSA_free(rsa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
static bool php_openssl_pkey_init_dsa_data(DSA *dsa, zval *data, bool *is_private)
|
||||
{
|
||||
BIGNUM *p, *q, *g, *priv_key, *pub_key;
|
||||
const BIGNUM *priv_key_const, *pub_key_const;
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, p);
|
||||
OPENSSL_PKEY_SET_BN(data, q);
|
||||
OPENSSL_PKEY_SET_BN(data, g);
|
||||
if (!p || !q || !g || !DSA_set0_pqg(dsa, p, q, g)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, pub_key);
|
||||
OPENSSL_PKEY_SET_BN(data, priv_key);
|
||||
*is_private = priv_key != NULL;
|
||||
if (pub_key) {
|
||||
return DSA_set0_key(dsa, pub_key, priv_key);
|
||||
}
|
||||
|
||||
/* generate key */
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
if (!DSA_generate_key(dsa)) {
|
||||
php_openssl_store_errors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* if BN_mod_exp return -1, then DSA_generate_key succeed for failed key
|
||||
* so we need to double check that public key is created */
|
||||
DSA_get0_key(dsa, &pub_key_const, &priv_key_const);
|
||||
if (!pub_key_const || BN_is_zero(pub_key_const)) {
|
||||
return 0;
|
||||
}
|
||||
/* all good */
|
||||
*is_private = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVP_PKEY *php_openssl_pkey_init_dsa(zval *data, bool *is_private)
|
||||
{
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (!pkey) {
|
||||
php_openssl_store_errors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DSA *dsa = DSA_new();
|
||||
if (!dsa) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!php_openssl_pkey_init_dsa_data(dsa, data, is_private)
|
||||
|| !EVP_PKEY_assign_DSA(pkey, dsa)) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
DSA_free(dsa);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
static bool php_openssl_pkey_init_dh_data(DH *dh, zval *data, bool *is_private)
|
||||
{
|
||||
BIGNUM *p, *q, *g, *priv_key, *pub_key;
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, p);
|
||||
OPENSSL_PKEY_SET_BN(data, q);
|
||||
OPENSSL_PKEY_SET_BN(data, g);
|
||||
if (!p || !g || !DH_set0_pqg(dh, p, q, g)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, priv_key);
|
||||
OPENSSL_PKEY_SET_BN(data, pub_key);
|
||||
*is_private = priv_key != NULL;
|
||||
if (pub_key) {
|
||||
return DH_set0_key(dh, pub_key, priv_key);
|
||||
}
|
||||
if (priv_key) {
|
||||
pub_key = php_openssl_dh_pub_from_priv(priv_key, g, p);
|
||||
if (pub_key == NULL) {
|
||||
return 0;
|
||||
}
|
||||
return DH_set0_key(dh, pub_key, priv_key);
|
||||
}
|
||||
|
||||
/* generate key */
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
if (!DH_generate_key(dh)) {
|
||||
php_openssl_store_errors();
|
||||
return 0;
|
||||
}
|
||||
/* all good */
|
||||
*is_private = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private)
|
||||
{
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (!pkey) {
|
||||
php_openssl_store_errors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DH *dh = DH_new();
|
||||
if (!dh) {
|
||||
EVP_PKEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!php_openssl_pkey_init_dh_data(dh, data, is_private) || !EVP_PKEY_assign_DH(pkey, dh)) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
DH_free(dh);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
#ifdef HAVE_EVP_PKEY_EC
|
||||
static bool php_openssl_pkey_init_ec_data(EC_KEY *eckey, zval *data, bool *is_private) {
|
||||
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;
|
||||
EC_GROUP *group = NULL;
|
||||
BN_CTX *bctx = BN_CTX_new();
|
||||
|
||||
*is_private = false;
|
||||
|
||||
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) {
|
||||
int 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 clean_exit;
|
||||
}
|
||||
|
||||
if (!(group = EC_GROUP_new_by_curve_name(nid))) {
|
||||
goto clean_exit;
|
||||
}
|
||||
EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE);
|
||||
} 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 clean_exit;
|
||||
}
|
||||
|
||||
if (!(group = EC_GROUP_new_curve_GFp(p, a, b, bctx))) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (!(point_g = EC_POINT_new(group))) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
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))) {
|
||||
goto clean_exit;
|
||||
}
|
||||
} 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 clean_exit;
|
||||
}
|
||||
|
||||
if (!EC_POINT_set_affine_coordinates_GFp(group, point_g, g_x, g_y, bctx)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
}
|
||||
|
||||
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))) {
|
||||
goto clean_exit;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* OpenSSL uses 0 cofactor as a marker for "unknown cofactor".
|
||||
* So accept cofactor == NULL or cofactor >= 0.
|
||||
* Internally, the lib will check the cofactor value.
|
||||
*/
|
||||
OPENSSL_PKEY_SET_BN(data, cofactor);
|
||||
if (!EC_GROUP_set_generator(group, point_g, order, cofactor)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
EC_GROUP_set_asn1_flag(group, OPENSSL_EC_EXPLICIT_CURVE);
|
||||
}
|
||||
|
||||
EC_GROUP_set_point_conversion_form(group, POINT_CONVERSION_UNCOMPRESSED);
|
||||
|
||||
if (!EC_KEY_set_group(eckey, group)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
OPENSSL_PKEY_SET_BN(data, d);
|
||||
OPENSSL_PKEY_SET_BN(data, x);
|
||||
OPENSSL_PKEY_SET_BN(data, y);
|
||||
|
||||
if (d) {
|
||||
*is_private = true;
|
||||
if (!EC_KEY_set_private_key(eckey, d)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
point_q = EC_POINT_new(group);
|
||||
if (!point_q || !EC_POINT_mul(group, point_q, d, NULL, NULL, bctx)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
} 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_GFp(group, point_q, x, y, bctx)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (point_q != NULL) {
|
||||
if (!EC_KEY_set_public_key(eckey, point_q)) {
|
||||
goto clean_exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!EC_KEY_check_key(eckey)) {
|
||||
*is_private = true;
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
EC_KEY_generate_key(eckey);
|
||||
}
|
||||
|
||||
clean_exit:
|
||||
php_openssl_store_errors();
|
||||
BN_CTX_free(bctx);
|
||||
EC_GROUP_free(group);
|
||||
EC_POINT_free(point_g);
|
||||
EC_POINT_free(point_q);
|
||||
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 EC_KEY_check_key(eckey);
|
||||
}
|
||||
|
||||
EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private) {
|
||||
EVP_PKEY *pkey = EVP_PKEY_new();
|
||||
if (!pkey) {
|
||||
php_openssl_store_errors();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EC_KEY *ec = EC_KEY_new();
|
||||
if (!ec) {
|
||||
EVP_PKEY_free(pkey);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!php_openssl_pkey_init_ec_data(ec, data, is_private) || !EVP_PKEY_assign_EC_KEY(pkey, ec)) {
|
||||
php_openssl_store_errors();
|
||||
EVP_PKEY_free(pkey);
|
||||
EC_KEY_free(ec);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return pkey;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
zend_long php_openssl_pkey_get_details(zval *return_value, EVP_PKEY *pkey)
|
||||
{
|
||||
zend_long ktype;
|
||||
switch (EVP_PKEY_base_id(pkey)) {
|
||||
case EVP_PKEY_RSA:
|
||||
case EVP_PKEY_RSA2:
|
||||
{
|
||||
RSA *rsa = EVP_PKEY_get0_RSA(pkey);
|
||||
ktype = OPENSSL_KEYTYPE_RSA;
|
||||
|
||||
if (rsa != NULL) {
|
||||
zval z_rsa;
|
||||
const BIGNUM *n, *e, *d, *p, *q, *dmp1, *dmq1, *iqmp;
|
||||
|
||||
RSA_get0_key(rsa, &n, &e, &d);
|
||||
RSA_get0_factors(rsa, &p, &q);
|
||||
RSA_get0_crt_params(rsa, &dmp1, &dmq1, &iqmp);
|
||||
|
||||
array_init(&z_rsa);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, n);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, e);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, d);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, p);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, q);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, dmp1);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, dmq1);
|
||||
OPENSSL_PKEY_GET_BN(z_rsa, iqmp);
|
||||
add_assoc_zval(return_value, "rsa", &z_rsa);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVP_PKEY_DSA:
|
||||
case EVP_PKEY_DSA2:
|
||||
case EVP_PKEY_DSA3:
|
||||
case EVP_PKEY_DSA4:
|
||||
{
|
||||
DSA *dsa = EVP_PKEY_get0_DSA(pkey);
|
||||
ktype = OPENSSL_KEYTYPE_DSA;
|
||||
|
||||
if (dsa != NULL) {
|
||||
zval z_dsa;
|
||||
const BIGNUM *p, *q, *g, *priv_key, *pub_key;
|
||||
|
||||
DSA_get0_pqg(dsa, &p, &q, &g);
|
||||
DSA_get0_key(dsa, &pub_key, &priv_key);
|
||||
|
||||
array_init(&z_dsa);
|
||||
OPENSSL_PKEY_GET_BN(z_dsa, p);
|
||||
OPENSSL_PKEY_GET_BN(z_dsa, q);
|
||||
OPENSSL_PKEY_GET_BN(z_dsa, g);
|
||||
OPENSSL_PKEY_GET_BN(z_dsa, priv_key);
|
||||
OPENSSL_PKEY_GET_BN(z_dsa, pub_key);
|
||||
add_assoc_zval(return_value, "dsa", &z_dsa);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EVP_PKEY_DH:
|
||||
{
|
||||
DH *dh = EVP_PKEY_get0_DH(pkey);
|
||||
ktype = OPENSSL_KEYTYPE_DH;
|
||||
|
||||
if (dh != NULL) {
|
||||
zval z_dh;
|
||||
const BIGNUM *p, *q, *g, *priv_key, *pub_key;
|
||||
|
||||
DH_get0_pqg(dh, &p, &q, &g);
|
||||
DH_get0_key(dh, &pub_key, &priv_key);
|
||||
|
||||
array_init(&z_dh);
|
||||
OPENSSL_PKEY_GET_BN(z_dh, p);
|
||||
OPENSSL_PKEY_GET_BN(z_dh, g);
|
||||
OPENSSL_PKEY_GET_BN(z_dh, priv_key);
|
||||
OPENSSL_PKEY_GET_BN(z_dh, pub_key);
|
||||
add_assoc_zval(return_value, "dh", &z_dh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_EVP_PKEY_EC
|
||||
case EVP_PKEY_EC:
|
||||
ktype = OPENSSL_KEYTYPE_EC;
|
||||
if (EVP_PKEY_get0_EC_KEY(pkey) != NULL) {
|
||||
zval ec;
|
||||
const EC_GROUP *ec_group;
|
||||
const EC_POINT *pub;
|
||||
int nid;
|
||||
char *crv_sn;
|
||||
ASN1_OBJECT *obj;
|
||||
// openssl recommends a buffer length of 80
|
||||
char oir_buf[80];
|
||||
const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey);
|
||||
BIGNUM *x = BN_new();
|
||||
BIGNUM *y = BN_new();
|
||||
const BIGNUM *d;
|
||||
|
||||
ec_group = EC_KEY_get0_group(ec_key);
|
||||
|
||||
array_init(&ec);
|
||||
|
||||
/** Curve nid (numerical identifier) used for ASN1 mapping */
|
||||
nid = EC_GROUP_get_curve_name(ec_group);
|
||||
if (nid != NID_undef) {
|
||||
crv_sn = (char*) OBJ_nid2sn(nid);
|
||||
if (crv_sn != NULL) {
|
||||
add_assoc_string(&ec, "curve_name", crv_sn);
|
||||
}
|
||||
|
||||
obj = OBJ_nid2obj(nid);
|
||||
if (obj != NULL) {
|
||||
int oir_len = OBJ_obj2txt(oir_buf, sizeof(oir_buf), obj, 1);
|
||||
add_assoc_stringl(&ec, "curve_oid", (char*) oir_buf, oir_len);
|
||||
ASN1_OBJECT_free(obj);
|
||||
}
|
||||
}
|
||||
|
||||
pub = EC_KEY_get0_public_key(ec_key);
|
||||
|
||||
if (EC_POINT_get_affine_coordinates_GFp(ec_group, pub, x, y, NULL)) {
|
||||
php_openssl_add_bn_to_array(&ec, x, "x");
|
||||
php_openssl_add_bn_to_array(&ec, y, "y");
|
||||
} else {
|
||||
php_openssl_store_errors();
|
||||
}
|
||||
|
||||
if ((d = EC_KEY_get0_private_key(EVP_PKEY_get0_EC_KEY(pkey))) != NULL) {
|
||||
php_openssl_add_bn_to_array(&ec, d, "d");
|
||||
}
|
||||
|
||||
add_assoc_zval(return_value, "ec", &ec);
|
||||
|
||||
BN_free(x);
|
||||
BN_free(y);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
ktype = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ktype;
|
||||
}
|
||||
|
||||
zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pub_len)
|
||||
{
|
||||
DH *dh = EVP_PKEY_get0_DH(pkey);
|
||||
if (dh == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
BIGNUM *pub = BN_bin2bn((unsigned char*)pub_str, (int)pub_len, NULL);
|
||||
zend_string *data = zend_string_alloc(DH_size(dh), 0);
|
||||
int len = DH_compute_key((unsigned char*)ZSTR_VAL(data), pub, dh);
|
||||
BN_free(pub);
|
||||
|
||||
if (len < 0) {
|
||||
php_openssl_store_errors();
|
||||
zend_string_release_ex(data, 0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ZSTR_LEN(data) = len;
|
||||
ZSTR_VAL(data)[len] = 0;
|
||||
return data;
|
||||
}
|
||||
|
||||
void php_openssl_get_cipher_methods(zval *return_value, bool aliases)
|
||||
{
|
||||
array_init(return_value);
|
||||
OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH,
|
||||
aliases ? php_openssl_add_method_or_alias : php_openssl_add_method,
|
||||
return_value);
|
||||
}
|
||||
|
||||
#endif
|
705
ext/openssl/openssl_backend_v3.c
Normal file
705
ext/openssl/openssl_backend_v3.c
Normal file
|
@ -0,0 +1,705 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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>
|
||||
|
||||
void php_openssl_backend_shutdown(void)
|
||||
{
|
||||
(void) 0;
|
||||
}
|
||||
|
||||
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 = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
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 = EVP_PKEY_CTX_new_id(EVP_PKEY_DSA, NULL);
|
||||
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;
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
ctx = EVP_PKEY_CTX_new(param_key, NULL);
|
||||
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 = EVP_PKEY_CTX_new_id(EVP_PKEY_DH, NULL);
|
||||
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;
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
ctx = EVP_PKEY_CTX_new(param_key, NULL);
|
||||
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 = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
|
||||
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(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;
|
||||
PHP_OPENSSL_RAND_ADD_TIME();
|
||||
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, 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;
|
||||
|
||||
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 = EVP_PKEY_CTX_new_id(key_type, NULL);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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(NULL,
|
||||
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);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -92,6 +92,8 @@ ZEND_TSRMLS_CACHE_EXTERN();
|
|||
php_stream_transport_factory_func php_openssl_ssl_socket_factory;
|
||||
|
||||
void php_openssl_store_errors(void);
|
||||
void php_openssl_errors_set_mark(void);
|
||||
void php_openssl_errors_restore_mark(void);
|
||||
|
||||
/* openssl file path extra */
|
||||
bool php_openssl_check_path_ex(
|
||||
|
@ -145,6 +147,8 @@ PHP_OPENSSL_API zend_string* php_openssl_decrypt(
|
|||
|
||||
/* OpenSSLCertificate class */
|
||||
|
||||
#include <openssl/x509.h>
|
||||
|
||||
typedef struct _php_openssl_certificate_object {
|
||||
X509 *x509;
|
||||
zend_object std;
|
||||
|
@ -158,6 +162,40 @@ static inline php_openssl_certificate_object *php_openssl_certificate_from_obj(z
|
|||
|
||||
#define Z_OPENSSL_CERTIFICATE_P(zv) php_openssl_certificate_from_obj(Z_OBJ_P(zv))
|
||||
|
||||
bool php_openssl_is_certificate_ce(zval *val);
|
||||
|
||||
/* OpenSSLCertificateSigningRequest class */
|
||||
|
||||
typedef struct _php_openssl_x509_request_object {
|
||||
X509_REQ *csr;
|
||||
zend_object std;
|
||||
} php_openssl_request_object;
|
||||
|
||||
static inline php_openssl_request_object *php_openssl_request_from_obj(zend_object *obj) {
|
||||
return (php_openssl_request_object *)((char *)(obj) - XtOffsetOf(php_openssl_request_object, std));
|
||||
}
|
||||
|
||||
#define Z_OPENSSL_REQUEST_P(zv) php_openssl_request_from_obj(Z_OBJ_P(zv))
|
||||
|
||||
bool php_openssl_is_request_ce(zval *val);
|
||||
|
||||
/* OpenSSLAsymmetricKey class */
|
||||
|
||||
typedef struct _php_openssl_pkey_object {
|
||||
EVP_PKEY *pkey;
|
||||
bool is_private;
|
||||
zend_object std;
|
||||
} php_openssl_pkey_object;
|
||||
|
||||
static inline php_openssl_pkey_object *php_openssl_pkey_from_obj(zend_object *obj) {
|
||||
return (php_openssl_pkey_object *)((char *)(obj) - XtOffsetOf(php_openssl_pkey_object, std));
|
||||
}
|
||||
|
||||
#define Z_OPENSSL_PKEY_P(zv) php_openssl_pkey_from_obj(Z_OBJ_P(zv))
|
||||
|
||||
bool php_openssl_is_pkey_ce(zval *val);
|
||||
void php_openssl_pkey_object_init(zval *zv, EVP_PKEY *pkey, bool is_private);
|
||||
|
||||
#if defined(HAVE_OPENSSL_ARGON2)
|
||||
|
||||
/**
|
||||
|
|
380
ext/openssl/php_openssl_backend.h
Normal file
380
ext/openssl/php_openssl_backend.h
Normal file
|
@ -0,0 +1,380 @@
|
|||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| 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> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef PHP_OPENSSL_BACKEND_H
|
||||
#define PHP_OPENSSL_BACKEND_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include "php.h"
|
||||
#include "php_openssl.h"
|
||||
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/x509v3.h>
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/conf.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/pkcs12.h>
|
||||
#include <openssl/cms.h>
|
||||
|
||||
/* number conversion flags checks */
|
||||
#define PHP_OPENSSL_CHECK_NUMBER_CONVERSION(_cond, _name, _arg_num) \
|
||||
do { \
|
||||
if (_cond) { \
|
||||
zend_argument_value_error((_arg_num), #_name" is too long"); \
|
||||
RETURN_THROWS(); \
|
||||
} \
|
||||
} while(0)
|
||||
#define PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(_cond, _name) \
|
||||
do { \
|
||||
if (_cond) { \
|
||||
zend_value_error(#_name" is too long"); \
|
||||
return NULL; \
|
||||
} \
|
||||
} while(0)
|
||||
/* check if size_t can be safely casted to int */
|
||||
#define PHP_OPENSSL_CHECK_SIZE_T_TO_INT(_var, _name, _arg_num) \
|
||||
PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_INT_OVFL(_var), _name, _arg_num)
|
||||
#define PHP_OPENSSL_CHECK_SIZE_T_TO_INT_NULL_RETURN(_var, _name) \
|
||||
PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_SIZE_T_INT_OVFL(_var), _name)
|
||||
/* check if size_t can be safely casted to unsigned int */
|
||||
#define PHP_OPENSSL_CHECK_SIZE_T_TO_UINT(_var, _name, _arg_num) \
|
||||
PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_SIZE_T_UINT_OVFL(_var), _name, _arg_num)
|
||||
/* check if long can be safely casted to int */
|
||||
#define PHP_OPENSSL_CHECK_LONG_TO_INT(_var, _name, _arg_num) \
|
||||
PHP_OPENSSL_CHECK_NUMBER_CONVERSION(ZEND_LONG_EXCEEDS_INT(_var), _name, _arg_num)
|
||||
#define PHP_OPENSSL_CHECK_LONG_TO_INT_NULL_RETURN(_var, _name) \
|
||||
PHP_OPENSSL_CHECK_NUMBER_CONVERSION_NULL_RETURN(ZEND_LONG_EXCEEDS_INT(_var), _name)
|
||||
|
||||
/* FIXME: Use the openssl constants instead of
|
||||
* enum. It is now impossible to match real values
|
||||
* against php constants. Also sorry to break the
|
||||
* enum principles here, BC...
|
||||
*/
|
||||
enum php_openssl_key_type {
|
||||
OPENSSL_KEYTYPE_RSA,
|
||||
OPENSSL_KEYTYPE_DSA,
|
||||
OPENSSL_KEYTYPE_DH,
|
||||
OPENSSL_KEYTYPE_DEFAULT = OPENSSL_KEYTYPE_RSA,
|
||||
OPENSSL_KEYTYPE_EC = OPENSSL_KEYTYPE_DH +1,
|
||||
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,
|
||||
};
|
||||
|
||||
enum php_openssl_cipher_type {
|
||||
PHP_OPENSSL_CIPHER_RC2_40,
|
||||
PHP_OPENSSL_CIPHER_RC2_128,
|
||||
PHP_OPENSSL_CIPHER_RC2_64,
|
||||
PHP_OPENSSL_CIPHER_DES,
|
||||
PHP_OPENSSL_CIPHER_3DES,
|
||||
PHP_OPENSSL_CIPHER_AES_128_CBC,
|
||||
PHP_OPENSSL_CIPHER_AES_192_CBC,
|
||||
PHP_OPENSSL_CIPHER_AES_256_CBC,
|
||||
|
||||
PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_AES_128_CBC
|
||||
};
|
||||
|
||||
/* Add some encoding rules. This is normally handled through filters
|
||||
* in the OpenSSL code, but we will do that part as if we were one
|
||||
* of the OpenSSL binaries along the lines of -outform {DER|CMS|PEM}
|
||||
*/
|
||||
enum php_openssl_encoding {
|
||||
ENCODING_DER,
|
||||
ENCODING_SMIME,
|
||||
ENCODING_PEM,
|
||||
};
|
||||
|
||||
|
||||
#define MIN_KEY_LENGTH 384
|
||||
|
||||
/* constants used in ext/phar/util.c, keep in sync */
|
||||
#define OPENSSL_ALGO_SHA1 1
|
||||
#define OPENSSL_ALGO_MD5 2
|
||||
#ifndef OPENSSL_NO_MD4
|
||||
#define OPENSSL_ALGO_MD4 3
|
||||
#endif
|
||||
#ifndef OPENSSL_NO_MD2
|
||||
#define OPENSSL_ALGO_MD2 4
|
||||
#endif
|
||||
#if PHP_OPENSSL_API_VERSION < 0x10100
|
||||
#define OPENSSL_ALGO_DSS1 5
|
||||
#endif
|
||||
#define OPENSSL_ALGO_SHA224 6
|
||||
#define OPENSSL_ALGO_SHA256 7
|
||||
#define OPENSSL_ALGO_SHA384 8
|
||||
#define OPENSSL_ALGO_SHA512 9
|
||||
#ifndef OPENSSL_NO_RMD160
|
||||
#define OPENSSL_ALGO_RMD160 10
|
||||
#endif
|
||||
#define DEBUG_SMIME 0
|
||||
|
||||
#if !defined(OPENSSL_NO_EC) && defined(EVP_PKEY_EC)
|
||||
#define HAVE_EVP_PKEY_EC 1
|
||||
|
||||
/* the OPENSSL_EC_EXPLICIT_CURVE value was added
|
||||
* in OpenSSL 1.1.0; previous versions should
|
||||
* use 0 instead.
|
||||
*/
|
||||
#ifndef OPENSSL_EC_EXPLICIT_CURVE
|
||||
#define OPENSSL_EC_EXPLICIT_CURVE 0x000
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct php_x509_request {
|
||||
CONF *global_config; /* Global SSL config */
|
||||
CONF *req_config; /* SSL config for this request */
|
||||
const EVP_MD * md_alg;
|
||||
const EVP_MD * digest;
|
||||
char * section_name,
|
||||
* config_filename,
|
||||
* digest_name,
|
||||
* extensions_section,
|
||||
* request_extensions_section;
|
||||
int priv_key_bits;
|
||||
int priv_key_type;
|
||||
|
||||
int priv_key_encrypt;
|
||||
|
||||
#ifdef HAVE_EVP_PKEY_EC
|
||||
int curve_name;
|
||||
#endif
|
||||
|
||||
EVP_PKEY * priv_key;
|
||||
|
||||
const EVP_CIPHER * priv_key_encrypt_cipher;
|
||||
};
|
||||
|
||||
void php_openssl_add_assoc_name_entry(zval * val, char * key, X509_NAME * name, int shortname);
|
||||
void php_openssl_add_assoc_asn1_string(zval * val, char * key, ASN1_STRING * str);
|
||||
time_t php_openssl_asn1_time_to_time_t(ASN1_UTCTIME * timestr);
|
||||
int php_openssl_config_check_syntax(const char * section_label, const char * config_filename, const char * section, CONF *config);
|
||||
char *php_openssl_conf_get_string(CONF *conf, const char *group, const char *name);
|
||||
long php_openssl_conf_get_number(CONF *conf, const char *group, const char *name);
|
||||
int php_openssl_add_oid_section(struct php_x509_request * req);
|
||||
int php_openssl_spki_cleanup(const char *src, char *dest);
|
||||
|
||||
X509 *php_openssl_x509_from_param(
|
||||
zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num);
|
||||
X509 *php_openssl_x509_from_zval(
|
||||
zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name);
|
||||
X509_REQ *php_openssl_csr_from_param(
|
||||
zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num);
|
||||
EVP_PKEY *php_openssl_pkey_from_zval(
|
||||
zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num);
|
||||
|
||||
X509_STORE * php_openssl_setup_verify(zval * calist, uint32_t arg_num);
|
||||
STACK_OF(X509) * php_openssl_load_all_certs_from_file(
|
||||
char *cert_file, size_t cert_file_len, uint32_t arg_num);
|
||||
EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req);
|
||||
zend_string *php_openssl_pkey_derive(EVP_PKEY *key, EVP_PKEY *peer_key, size_t key_size);
|
||||
|
||||
#define PHP_SSL_REQ_INIT(req) memset(req, 0, sizeof(*req))
|
||||
#define PHP_SSL_REQ_DISPOSE(req) php_openssl_dispose_config(req)
|
||||
#define PHP_SSL_REQ_PARSE(req, zval) php_openssl_parse_config(req, zval)
|
||||
|
||||
#define PHP_SSL_CONFIG_SYNTAX_CHECK(var) if (req->var && php_openssl_config_check_syntax(#var, \
|
||||
req->config_filename, req->var, req->req_config) == FAILURE) return FAILURE
|
||||
|
||||
#define SET_OPTIONAL_STRING_ARG(key, varname, defval) \
|
||||
do { \
|
||||
if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_STRING) { \
|
||||
varname = Z_STRVAL_P(item); \
|
||||
} else { \
|
||||
varname = defval; \
|
||||
if (varname == NULL) { \
|
||||
php_openssl_store_errors(); \
|
||||
} \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define SET_OPTIONAL_LONG_ARG(key, varname, defval) \
|
||||
if (optional_args && (item = zend_hash_str_find(Z_ARRVAL_P(optional_args), key, sizeof(key)-1)) != NULL && Z_TYPE_P(item) == IS_LONG) \
|
||||
varname = (int)Z_LVAL_P(item); \
|
||||
else \
|
||||
varname = defval
|
||||
|
||||
const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo);
|
||||
|
||||
int php_openssl_parse_config(struct php_x509_request * req, zval * optional_args);
|
||||
void php_openssl_dispose_config(struct php_x509_request * req);
|
||||
|
||||
|
||||
#if defined(PHP_WIN32) || PHP_OPENSSL_API_VERSION >= 0x10100
|
||||
#define PHP_OPENSSL_RAND_ADD_TIME() ((void) 0)
|
||||
#else
|
||||
#define PHP_OPENSSL_RAND_ADD_TIME() php_openssl_rand_add_timeval()
|
||||
|
||||
static inline void php_openssl_rand_add_timeval(void) /* {{{ */
|
||||
{
|
||||
struct timeval tv;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
RAND_add(&tv, sizeof(tv), 0.0);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
#endif
|
||||
|
||||
int php_openssl_load_rand_file(const char * file, int *egdsocket, int *seeded);
|
||||
int php_openssl_write_rand_file(const char * file, int egdsocket, int seeded);
|
||||
|
||||
EVP_MD * php_openssl_get_evp_md_from_algo(zend_long algo);
|
||||
const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(zend_long algo);
|
||||
|
||||
void php_openssl_backend_init(void);
|
||||
void php_openssl_backend_shutdown(void);
|
||||
|
||||
const char *php_openssl_get_conf_filename(void);
|
||||
|
||||
void php_openssl_set_cert_locations(zval *return_value);
|
||||
|
||||
X509 *php_openssl_x509_from_str(
|
||||
zend_string *cert_str, uint32_t arg_num, bool is_from_array, const char *option_name);
|
||||
|
||||
X509 *php_openssl_x509_from_param(
|
||||
zend_object *cert_obj, zend_string *cert_str, uint32_t arg_num);
|
||||
|
||||
X509 *php_openssl_x509_from_zval(
|
||||
zval *val, bool *free_cert, uint32_t arg_num, bool is_from_array, const char *option_name);
|
||||
|
||||
zend_string* php_openssl_x509_fingerprint(X509 *peer, const char *method, bool raw);
|
||||
|
||||
int openssl_x509v3_subjectAltName(BIO *bio, X509_EXTENSION *extension);
|
||||
|
||||
STACK_OF(X509) *php_openssl_load_all_certs_from_file(
|
||||
char *cert_file, size_t cert_file_len, uint32_t arg_num);
|
||||
|
||||
int php_openssl_check_cert(X509_STORE *ctx, X509 *x, STACK_OF(X509) *untrustedchain, int purpose);
|
||||
|
||||
X509_STORE *php_openssl_setup_verify(zval *calist, uint32_t arg_num);
|
||||
|
||||
void php_openssl_sk_X509_free(STACK_OF(X509) * sk);
|
||||
STACK_OF(X509) *php_openssl_array_to_X509_sk(zval * zcerts, uint32_t arg_num, const char *option_name);
|
||||
|
||||
zend_result php_openssl_csr_add_subj_entry(zval *item, X509_NAME *subj, int nid);
|
||||
zend_result php_openssl_csr_make(struct php_x509_request * req, X509_REQ * csr, zval * dn, zval * attribs);
|
||||
X509_REQ *php_openssl_csr_from_str(zend_string *csr_str, uint32_t arg_num);
|
||||
X509_REQ *php_openssl_csr_from_param(
|
||||
zend_object *csr_obj, zend_string *csr_str, uint32_t arg_num);
|
||||
|
||||
#if PHP_OPENSSL_API_VERSION >= 0x10100 && !defined (LIBRESSL_VERSION_NUMBER)
|
||||
#define PHP_OPENSSL_ASN1_INTEGER_set ASN1_INTEGER_set_int64
|
||||
#else
|
||||
#define PHP_OPENSSL_ASN1_INTEGER_set ASN1_INTEGER_set
|
||||
#endif
|
||||
|
||||
EVP_PKEY *php_openssl_extract_public_key(EVP_PKEY *priv_key);
|
||||
|
||||
struct php_openssl_pem_password {
|
||||
char *key;
|
||||
int len;
|
||||
};
|
||||
|
||||
int php_openssl_pem_password_cb(char *buf, int size, int rwflag, void *userdata);
|
||||
EVP_PKEY *php_openssl_pkey_from_zval(
|
||||
zval *val, int public_key, char *passphrase, size_t passphrase_len, uint32_t arg_num);
|
||||
int php_openssl_get_evp_pkey_type(int key_type);
|
||||
EVP_PKEY *php_openssl_generate_private_key(struct php_x509_request * req);
|
||||
void php_openssl_add_bn_to_array(zval *ary, const BIGNUM *bn, const char *name);
|
||||
|
||||
void php_openssl_add_bn_to_array(zval *ary, const BIGNUM *bn, const char *name);
|
||||
|
||||
#define OPENSSL_PKEY_GET_BN(_type, _name) php_openssl_add_bn_to_array(&_type, _name, #_name)
|
||||
|
||||
#define OPENSSL_PKEY_SET_BN(_data, _name) do { \
|
||||
zval *bn; \
|
||||
if ((bn = zend_hash_str_find(Z_ARRVAL_P(_data), #_name, sizeof(#_name)-1)) != NULL && \
|
||||
Z_TYPE_P(bn) == IS_STRING) { \
|
||||
_name = BN_bin2bn( \
|
||||
(unsigned char*)Z_STRVAL_P(bn), \
|
||||
(int)Z_STRLEN_P(bn), NULL); \
|
||||
} else { \
|
||||
_name = NULL; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
|
||||
EVP_PKEY *php_openssl_pkey_init_rsa(zval *data);
|
||||
EVP_PKEY *php_openssl_pkey_init_dsa(zval *data, bool *is_private);
|
||||
BIGNUM *php_openssl_dh_pub_from_priv(BIGNUM *priv_key, BIGNUM *g, BIGNUM *p);
|
||||
EVP_PKEY *php_openssl_pkey_init_dh(zval *data, bool *is_private);
|
||||
EVP_PKEY *php_openssl_pkey_init_ec(zval *data, bool *is_private);
|
||||
void php_openssl_pkey_object_curve_25519_448(zval *return_value, int key_type, zval *data);
|
||||
#if PHP_OPENSSL_API_VERSION >= 0x30000
|
||||
void php_openssl_pkey_object_curve_25519_448(zval *return_value, int key_type, zval *data);
|
||||
#endif
|
||||
zend_long php_openssl_pkey_get_details(zval *return_value, EVP_PKEY *pkey);
|
||||
|
||||
zend_string *php_openssl_dh_compute_key(EVP_PKEY *pkey, char *pub_str, size_t pub_len);
|
||||
|
||||
BIO *php_openssl_bio_new_file(
|
||||
const char *filename, size_t filename_len, uint32_t arg_num, const char *mode);
|
||||
|
||||
void php_openssl_add_method_or_alias(const OBJ_NAME *name, void *arg);
|
||||
void php_openssl_add_method(const OBJ_NAME *name, void *arg);
|
||||
|
||||
void php_openssl_get_md_methods(zval *return_value, bool aliases);
|
||||
void php_openssl_get_cipher_methods(zval *return_value, bool aliases);
|
||||
|
||||
/* Cipher mode info */
|
||||
struct php_openssl_cipher_mode {
|
||||
bool is_aead;
|
||||
bool is_single_run_aead;
|
||||
bool set_tag_length_always;
|
||||
bool set_tag_length_when_encrypting;
|
||||
int aead_get_tag_flag;
|
||||
int aead_set_tag_flag;
|
||||
int aead_ivlen_flag;
|
||||
};
|
||||
|
||||
#if PHP_OPENSSL_API_VERSION >= 0x10100
|
||||
static inline void php_openssl_set_aead_flags(struct php_openssl_cipher_mode *mode) {
|
||||
mode->is_aead = true;
|
||||
mode->aead_get_tag_flag = EVP_CTRL_AEAD_GET_TAG;
|
||||
mode->aead_set_tag_flag = EVP_CTRL_AEAD_SET_TAG;
|
||||
mode->aead_ivlen_flag = EVP_CTRL_AEAD_SET_IVLEN;
|
||||
}
|
||||
#endif
|
||||
|
||||
void php_openssl_load_cipher_mode(struct php_openssl_cipher_mode *mode, const EVP_CIPHER *cipher_type);
|
||||
int php_openssl_validate_iv(const char **piv, size_t *piv_len, size_t iv_required_len,
|
||||
bool *free_iv, EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode);
|
||||
|
||||
int php_openssl_cipher_init(const EVP_CIPHER *cipher_type,
|
||||
EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
|
||||
const char **ppassword, size_t *ppassword_len, bool *free_password,
|
||||
const char **piv, size_t *piv_len, bool *free_iv,
|
||||
const char *tag, int tag_len, zend_long options, int enc);
|
||||
|
||||
int php_openssl_cipher_update(const EVP_CIPHER *cipher_type,
|
||||
EVP_CIPHER_CTX *cipher_ctx, struct php_openssl_cipher_mode *mode,
|
||||
zend_string **poutbuf, int *poutlen, const char *data, size_t data_len,
|
||||
const char *aad, size_t aad_len, int enc);
|
||||
|
||||
const EVP_CIPHER *php_openssl_get_evp_cipher_by_name(const char *method);
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue