diff options
author | Dmitry Stogov <dmitry@php.net> | 2007-08-06 08:43:43 +0000 |
---|---|---|
committer | Dmitry Stogov <dmitry@php.net> | 2007-08-06 08:43:43 +0000 |
commit | a433f85ce1f4f898abb407575ec15fd6ef804130 (patch) | |
tree | 440d9831791f8a006c6e28aa759ff4809aca7a65 /ext/openssl/openssl.c | |
parent | da7792c135a1ddb80e9ad1ecf4b04257993ee7d1 (diff) | |
download | php-git-a433f85ce1f4f898abb407575ec15fd6ef804130.tar.gz |
Improved ext/openssl
. Added support for OpenSSL digest functions
. Added support for OpenSSL cipher functions
. Added access to internal values of DSA, RSA and DH keys
Diffstat (limited to 'ext/openssl/openssl.c')
-rw-r--r-- | ext/openssl/openssl.c | 575 |
1 files changed, 531 insertions, 44 deletions
diff --git a/ext/openssl/openssl.c b/ext/openssl/openssl.c index c6eee0183a..d583220965 100644 --- a/ext/openssl/openssl.c +++ b/ext/openssl/openssl.c @@ -33,6 +33,8 @@ #include "ext/standard/file.h" #include "ext/standard/info.h" #include "ext/standard/php_fopen_wrappers.h" +#include "ext/standard/md5.h" +#include "ext/standard/base64.h" /* OpenSSL includes */ #include <openssl/evp.h> @@ -88,6 +90,15 @@ enum php_openssl_cipher_type { PHP_OPENSSL_CIPHER_DEFAULT = PHP_OPENSSL_CIPHER_RC2_40 }; +PHP_FUNCTION(openssl_get_md_methods); +PHP_FUNCTION(openssl_get_cipher_methods); + +PHP_FUNCTION(openssl_digest); +PHP_FUNCTION(openssl_encrypt); +PHP_FUNCTION(openssl_decrypt); + +PHP_FUNCTION(openssl_dh_compute_key); + /* {{{ openssl_functions[] */ zend_function_entry openssl_functions[] = { @@ -126,10 +137,13 @@ zend_function_entry openssl_functions[] = { PHP_FE(openssl_csr_get_subject, NULL) PHP_FE(openssl_csr_get_public_key, NULL) - PHP_FE(openssl_sign, second_arg_force_ref) - PHP_FE(openssl_verify, NULL) - PHP_FE(openssl_seal, arg2and3_force_ref) - PHP_FE(openssl_open, second_arg_force_ref) + PHP_FE(openssl_digest, NULL) + PHP_FE(openssl_encrypt, NULL) + PHP_FE(openssl_decrypt, NULL) + PHP_FE(openssl_sign, second_arg_force_ref) + PHP_FE(openssl_verify, NULL) + PHP_FE(openssl_seal, arg2and3_force_ref) + PHP_FE(openssl_open, second_arg_force_ref) /* for S/MIME handling */ PHP_FE(openssl_pkcs7_verify, NULL) @@ -142,6 +156,11 @@ zend_function_entry openssl_functions[] = { PHP_FE(openssl_public_encrypt, second_arg_force_ref) PHP_FE(openssl_public_decrypt, second_arg_force_ref) + PHP_FE(openssl_get_md_methods, NULL) + PHP_FE(openssl_get_cipher_methods, NULL) + + PHP_FE(openssl_dh_compute_key, NULL) + PHP_FE(openssl_error_string, NULL) {NULL, NULL, NULL} }; @@ -645,6 +664,34 @@ static EVP_MD * php_openssl_get_evp_md_from_algo(long algo) { /* {{{ */ return mdtype; } /* }}} */ + +static const EVP_CIPHER * php_openssl_get_evp_cipher_from_algo(long algo) { /* {{{ */ + switch (algo) { +#ifndef OPENSSL_NO_RC2 + case PHP_OPENSSL_CIPHER_RC2_40: + return EVP_rc2_40_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_64: + return EVP_rc2_64_cbc(); + break; + case PHP_OPENSSL_CIPHER_RC2_128: + return EVP_rc2_cbc(); + break; +#endif + +#ifndef OPENSSL_NO_DES + case PHP_OPENSSL_CIPHER_DES: + return EVP_des_cbc(); + break; + case PHP_OPENSSL_CIPHER_3DES: + return EVP_des_ede3_cbc(); + break; +#endif + default: + return NULL; + break; + } +} /* }}} */ /* {{{ PHP_MINIT_FUNCTION @@ -2476,6 +2523,25 @@ static EVP_PKEY * php_openssl_generate_private_key(struct php_x509_request * req } break; #endif +#if !defined(NO_DH) + case OPENSSL_KEYTYPE_DH: + { + DH *dhpar = DH_generate_parameters(req->priv_key_bits, 2, NULL, NULL); + int codes = 0; + + if (dhpar) { + DH_set_method(dhpar, DH_get_default_method()); + if (DH_check(dhpar, &codes) && codes == 0 && DH_generate_key(dhpar)) { + if (EVP_PKEY_assign_DH(req->priv_key, dhpar)) { + return_val = req->priv_key; + } + } else { + DH_free(dhpar); + } + } + } + break; +#endif default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type"); } @@ -2540,18 +2606,111 @@ static int php_openssl_is_private_key(EVP_PKEY* pkey TSRMLS_DC) } /* }}} */ +#define OPENSSL_PKEY_GET_BN(_type, _name) do { \ + if (pkey->pkey._type->_name != NULL) { \ + int len = BN_num_bytes(pkey->pkey._type->_name); \ + char *str = emalloc(len + 1); \ + BN_bn2bin(pkey->pkey._type->_name, (unsigned char*)str); \ + str[len] = 0; \ + add_assoc_stringl(_type, #_name, str, len, 0); \ + } \ + } while (0) + +#define OPENSSL_PKEY_SET_BN(_ht, _type, _name) do { \ + zval **bn; \ + if (zend_hash_find(_ht, #_name, sizeof(#_name), (void**)&bn) == SUCCESS && \ + Z_TYPE_PP(bn) == IS_STRING) { \ + _type->_name = BN_bin2bn( \ + (unsigned char*)Z_STRVAL_PP(bn), \ + Z_STRLEN_PP(bn), NULL); \ + } \ + } while (0); + + /* {{{ proto resource openssl_pkey_new([array configargs]) Generates a new private key */ PHP_FUNCTION(openssl_pkey_new) { struct php_x509_request req; zval * args = NULL; + zval **data; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|a!", &args) == FAILURE) { return; } RETVAL_FALSE; - + + if (args && Z_TYPE_P(args) == IS_ARRAY) { + EVP_PKEY *pkey; + + if (zend_hash_find(Z_ARRVAL_P(args), "rsa", sizeof("rsa"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + RSA *rsa = RSA_new(); + if (rsa) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, n); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, e); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, d); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, q); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmp1); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, dmq1); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), rsa, iqmp); + if (EVP_PKEY_assign_RSA(pkey, rsa)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key)); + } + RSA_free(rsa); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } else if (zend_hash_find(Z_ARRVAL_P(args), "dsa", sizeof("dsa"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + DSA *dsa = DSA_new(); + if (dsa) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, q); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, g); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, priv_key); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dsa, pub_key); + if (!dsa->priv_key && !dsa->pub_key) { + DSA_generate_key(dsa); + } + if (EVP_PKEY_assign_DSA(pkey, dsa)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key)); + } + DSA_free(dsa); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } else if (zend_hash_find(Z_ARRVAL_P(args), "dh", sizeof("dh"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + DH *dh = DH_new(); + if (dh) { + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, p); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, g); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, priv_key); + OPENSSL_PKEY_SET_BN(Z_ARRVAL_PP(data), dh, pub_key); + if (!dh->priv_key && !dh->pub_key) { + DH_generate_key(dh); + } + if (EVP_PKEY_assign_DH(pkey, dh)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key)); + } + DH_free(dh); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } + } + PHP_SSL_REQ_INIT(&req); if (PHP_SSL_REQ_PARSE(&req, args) == SUCCESS) @@ -2769,15 +2928,59 @@ PHP_FUNCTION(openssl_pkey_get_details) case EVP_PKEY_RSA: case EVP_PKEY_RSA2: ktype = OPENSSL_KEYTYPE_RSA; + + if (pkey->pkey.rsa != NULL) { + zval *rsa; + + ALLOC_INIT_ZVAL(rsa); + array_init(rsa); + OPENSSL_PKEY_GET_BN(rsa, n); + OPENSSL_PKEY_GET_BN(rsa, e); + OPENSSL_PKEY_GET_BN(rsa, d); + OPENSSL_PKEY_GET_BN(rsa, p); + OPENSSL_PKEY_GET_BN(rsa, q); + OPENSSL_PKEY_GET_BN(rsa, dmp1); + OPENSSL_PKEY_GET_BN(rsa, dmq1); + OPENSSL_PKEY_GET_BN(rsa, iqmp); + add_assoc_zval(return_value, "rsa", rsa); + } + break; case EVP_PKEY_DSA: case EVP_PKEY_DSA2: case EVP_PKEY_DSA3: case EVP_PKEY_DSA4: ktype = OPENSSL_KEYTYPE_DSA; + + if (pkey->pkey.dsa != NULL) { + zval *dsa; + + ALLOC_INIT_ZVAL(dsa); + array_init(dsa); + OPENSSL_PKEY_GET_BN(dsa, p); + OPENSSL_PKEY_GET_BN(dsa, q); + OPENSSL_PKEY_GET_BN(dsa, g); + OPENSSL_PKEY_GET_BN(dsa, priv_key); + OPENSSL_PKEY_GET_BN(dsa, pub_key); + add_assoc_zval(return_value, "dsa", dsa); + } break; case EVP_PKEY_DH: + ktype = OPENSSL_KEYTYPE_DH; + + if (pkey->pkey.dh != NULL) { + zval *dh; + + ALLOC_INIT_ZVAL(dh); + array_init(dh); + OPENSSL_PKEY_GET_BN(dh, p); + OPENSSL_PKEY_GET_BN(dh, g); + OPENSSL_PKEY_GET_BN(dh, priv_key); + OPENSSL_PKEY_GET_BN(dh, pub_key); + add_assoc_zval(return_value, "dh", dh); + } + break; #ifdef EVP_PKEY_EC case EVP_PKEY_EC: @@ -2994,32 +3197,7 @@ PHP_FUNCTION(openssl_pkcs7_encrypt) } /* sanity check the cipher */ - switch (cipherid) { -#ifndef OPENSSL_NO_RC2 - case PHP_OPENSSL_CIPHER_RC2_40: - cipher = EVP_rc2_40_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_64: - cipher = EVP_rc2_64_cbc(); - break; - case PHP_OPENSSL_CIPHER_RC2_128: - cipher = EVP_rc2_cbc(); - break; -#endif - -#ifndef OPENSSL_NO_DES - case PHP_OPENSSL_CIPHER_DES: - cipher = EVP_des_cbc(); - break; - case PHP_OPENSSL_CIPHER_3DES: - cipher = EVP_des_ede3_cbc(); - break; -#endif - - default: - php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid cipher type `%ld'", cipherid); - goto clean_exit; - } + cipher = php_openssl_get_evp_cipher_from_algo(cipherid); if (cipher == NULL) { /* shouldn't happen */ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to get cipher"); @@ -3518,7 +3696,7 @@ PHP_FUNCTION(openssl_error_string) } /* }}} */ -/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, int signature_alg]) +/* {{{ proto bool openssl_sign(string data, &string signature, mixed key[, mixed method]) Signs data */ PHP_FUNCTION(openssl_sign) { @@ -3530,10 +3708,11 @@ PHP_FUNCTION(openssl_sign) char * data; int data_len; EVP_MD_CTX md_ctx; + zval *method = NULL; long signature_algo = OPENSSL_ALGO_SHA1; - EVP_MD *mdtype; + const EVP_MD *mdtype; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|l", &data, &data_len, &signature, &key, &signature_algo) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szZ|z", &data, &data_len, &signature, &key, &method) == FAILURE) { return; } pkey = php_openssl_evp_from_zval(key, 0, "", 0, &keyresource TSRMLS_CC); @@ -3542,7 +3721,17 @@ PHP_FUNCTION(openssl_sign) RETURN_FALSE; } - mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + if (method == NULL || Z_TYPE_P(method) == IS_LONG) { + if (Z_TYPE_P(method) == IS_LONG) { + signature_algo = Z_LVAL_P(method); + } + mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + } else if (Z_TYPE_P(method) == IS_STRING) { + mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } if (!mdtype) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); RETURN_FALSE; @@ -3568,7 +3757,7 @@ PHP_FUNCTION(openssl_sign) } /* }}} */ -/* {{{ proto int openssl_verify(string data, string signature, mixed key [, int signature_algo]) +/* {{{ proto int openssl_verify(string data, string signature, mixed key[, mixed method]) Verifys data */ PHP_FUNCTION(openssl_verify) { @@ -3576,17 +3765,28 @@ PHP_FUNCTION(openssl_verify) EVP_PKEY *pkey; int err; EVP_MD_CTX md_ctx; - EVP_MD *mdtype; + const EVP_MD *mdtype; long keyresource = -1; char * data; int data_len; char * signature; int signature_len; + zval *method = NULL; long signature_algo = OPENSSL_ALGO_SHA1; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|l", &data, &data_len, &signature, &signature_len, &key, &signature_algo) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssZ|z", &data, &data_len, &signature, &signature_len, &key, &method) == FAILURE) { return; } - mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + if (method == NULL || Z_TYPE_P(method) == IS_LONG) { + if (Z_TYPE_P(method) == IS_LONG) { + signature_algo = Z_LVAL_P(method); + } + mdtype = php_openssl_get_evp_md_from_algo(signature_algo); + } else if (Z_TYPE_P(method) == IS_STRING) { + mdtype = EVP_get_digestbyname(Z_STRVAL_P(method)); + } else { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } if (!mdtype) { php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); RETURN_FALSE; @@ -3621,9 +3821,12 @@ PHP_FUNCTION(openssl_seal) int i, len1, len2, *eksl, nkeys; unsigned char *buf = NULL, **eks; char * data; int data_len; + char *method =NULL; + int method_len; + const EVP_CIPHER *cipher; EVP_CIPHER_CTX ctx; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/", &data, &data_len, &sealdata, &ekeys, &pubkeys) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szza/|s", &data, &data_len, &sealdata, &ekeys, &pubkeys, &method, &method_len) == FAILURE) { return; } @@ -3634,6 +3837,16 @@ PHP_FUNCTION(openssl_seal) RETURN_FALSE; } + if (method) { + cipher = EVP_get_cipherbyname(method); + if (!cipher) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + } else { + cipher = EVP_rc4(); + } + pkeys = safe_emalloc(nkeys, sizeof(*pkeys), 0); eksl = safe_emalloc(nkeys, sizeof(*eksl), 0); eks = safe_emalloc(nkeys, sizeof(*eks), 0); @@ -3657,7 +3870,7 @@ PHP_FUNCTION(openssl_seal) i++; } - if (!EVP_EncryptInit(&ctx,EVP_rc4(),NULL,NULL)) { + if (!EVP_EncryptInit(&ctx,cipher,NULL,NULL)) { RETVAL_FALSE; goto clean_exit; } @@ -3670,7 +3883,7 @@ PHP_FUNCTION(openssl_seal) /* allocate one byte extra to make room for \0 */ buf = emalloc(data_len + EVP_CIPHER_CTX_block_size(&ctx)); - if (!EVP_SealInit(&ctx, EVP_rc4(), eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { + if (!EVP_SealInit(&ctx, cipher, eks, eksl, NULL, pkeys, nkeys) || !EVP_SealUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { RETVAL_FALSE; efree(buf); goto clean_exit; @@ -3734,8 +3947,11 @@ PHP_FUNCTION(openssl_open) EVP_CIPHER_CTX ctx; char * data; int data_len; char * ekey; int ekey_len; + char *method =NULL; + int method_len; + const EVP_CIPHER *cipher; - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szsZ|s", &data, &data_len, &opendata, &ekey, &ekey_len, &privkey, &method, &method_len) == FAILURE) { return; } @@ -3744,9 +3960,20 @@ PHP_FUNCTION(openssl_open) php_error_docref(NULL TSRMLS_CC, E_WARNING, "unable to coerce parameter 4 into a private key"); RETURN_FALSE; } + + if (method) { + cipher = EVP_get_cipherbyname(method); + if (!cipher) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm."); + RETURN_FALSE; + } + } else { + cipher = EVP_rc4(); + } + buf = emalloc(data_len + 1); - if (EVP_OpenInit(&ctx, EVP_rc4(), (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { + if (EVP_OpenInit(&ctx, cipher, (unsigned char *)ekey, ekey_len, NULL, pkey) && EVP_OpenUpdate(&ctx, buf, &len1, (unsigned char *)data, data_len)) { if (!EVP_OpenFinal(&ctx, buf + len1, &len2) || (len1 + len2 == 0)) { efree(buf); if (keyresource == -1) { @@ -3996,6 +4223,266 @@ SSL *php_SSL_new_from_context(SSL_CTX *ctx, php_stream *stream TSRMLS_DC) /* {{{ } /* }}} */ +static void openssl_add_method_or_alias(const OBJ_NAME *name, void *arg) /* {{{ */ +{ + add_next_index_string((zval*)arg, (char*)name->name, 1); +} +/* }}} */ + +static void openssl_add_method(const OBJ_NAME *name, void *arg) /* {{{ */ +{ + if (name->alias == 0) { + add_next_index_string((zval*)arg, (char*)name->name, 1); + } +} +/* }}} */ + +/* {{{ proto array openssl_get_md_methods([bool aliases = false]) + Return array of available digest methods */ +PHP_FUNCTION(openssl_get_md_methods) +{ + zend_bool aliases = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { + return; + } + array_init(return_value); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_MD_METH, + aliases ? openssl_add_method_or_alias: openssl_add_method, + return_value); +} +/* }}} */ + +/* {{{ proto array openssl_get_cipher_methods([bool aliases = false]) + Return array of available cipher methods */ +PHP_FUNCTION(openssl_get_cipher_methods) +{ + zend_bool aliases = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &aliases) == FAILURE) { + return; + } + array_init(return_value); + OBJ_NAME_do_all_sorted(OBJ_NAME_TYPE_CIPHER_METH, + aliases ? openssl_add_method_or_alias: openssl_add_method, + return_value); +} +/* }}} */ + +/* {{{ proto string openssl_digest(string data, string method [, bool raw_output=false]) + Computes digest hash value for given data using given method, returns raw or binhex encoded string */ +PHP_FUNCTION(openssl_digest) +{ + zend_bool raw_output = 0; + char *data, *method; + int data_len, method_len; + const EVP_MD *mdtype; + EVP_MD_CTX md_ctx; + int siglen; + unsigned char *sigbuf; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &data, &data_len, &method, &method_len, &raw_output) == FAILURE) { + return; + } + mdtype = EVP_get_digestbyname(method); + if (!mdtype) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm"); + RETURN_FALSE; + } + + siglen = EVP_MD_size(mdtype); + sigbuf = emalloc(siglen + 1); + + EVP_DigestInit(&md_ctx, mdtype); + EVP_DigestUpdate(&md_ctx, (unsigned char *)data, data_len); + if (EVP_DigestFinal (&md_ctx, (unsigned char *)sigbuf, (unsigned int *)&siglen)) { + if (raw_output) { + sigbuf[siglen] = '\0'; + RETVAL_STRINGL((char *)sigbuf, siglen, 0); + } else { + int digest_str_len = siglen * 2; + char *digest_str = emalloc(digest_str_len + 1); + + make_digest_ex(digest_str, sigbuf, siglen); + efree(sigbuf); + RETVAL_STRINGL(digest_str, digest_str_len, 0); + } + } else { + efree(sigbuf); + RETVAL_FALSE; + } +} +/* }}} */ + +/* {{{ proto string openssl_encrypt(string data, string method, string password [, bool raw_output=false]) + Encrypts given data with given method and key, returns raw or base64 encoded string */ +PHP_FUNCTION(openssl_encrypt) +{ + zend_bool raw_output = 0; + char *data, *method, *password; + int data_len, method_len, password_len; + const EVP_CIPHER *cipher_type; + EVP_CIPHER_CTX cipher_ctx; + int i, outlen, keylen, ivlen; + unsigned char *outbuf, *key, *iv; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|b", &data, &data_len, &method, &method_len, &password, &password_len, &raw_output) == FAILURE) { + return; + } + cipher_type = EVP_get_cipherbyname(method); + if (!cipher_type) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + keylen = EVP_CIPHER_key_length(cipher_type); + if (keylen > password_len) { + key = emalloc(keylen); + memset(key, 0, keylen); + memcpy(key, password, password_len); + } else { + key = (unsigned char*)password; + } + + ivlen = EVP_CIPHER_iv_length(cipher_type); + iv = emalloc(ivlen); + memset(iv, 0, ivlen); + + outlen = data_len + EVP_CIPHER_block_size(cipher_type); + outbuf = emalloc(outlen + 1); + + EVP_EncryptInit(&cipher_ctx, cipher_type, key, iv); + EVP_EncryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); + outlen = i; + if (EVP_EncryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { + outlen += i; + if (raw_output) { + outbuf[outlen] = '\0'; + RETVAL_STRINGL((char *)outbuf, outlen, 0); + } else { + int base64_str_len; + char *base64_str; + + base64_str = (char*)php_base64_encode(outbuf, outlen, &base64_str_len); + efree(outbuf); + RETVAL_STRINGL(base64_str, base64_str_len, 0); + } + } else { + efree(outbuf); + RETVAL_FALSE; + } + if (key != (unsigned char*)password) { + efree(key); + } + efree(iv); +} +/* }}} */ + +/* {{{ proto string openssl_decrypt(string data, string method, string password [, bool raw_input=false]) + Takes raw or base64 encoded string and dectupt it using given method and key */ +PHP_FUNCTION(openssl_decrypt) +{ + zend_bool raw_input = 0; + char *data, *method, *password; + int data_len, method_len, password_len; + const EVP_CIPHER *cipher_type; + EVP_CIPHER_CTX cipher_ctx; + int i, outlen, keylen, ivlen; + unsigned char *outbuf, *key, *iv; + int base64_str_len; + char *base64_str = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sss|b", &data, &data_len, &method, &method_len, &password, &password_len, &raw_input) == FAILURE) { + return; + } + + if (!raw_input) { + base64_str = (char*)php_base64_decode((unsigned char*)data, data_len, &base64_str_len); + data_len = base64_str_len; + data = base64_str; + } + + cipher_type = EVP_get_cipherbyname(method); + if (!cipher_type) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown cipher algorithm"); + RETURN_FALSE; + } + + keylen = EVP_CIPHER_key_length(cipher_type); + if (keylen > password_len) { + key = emalloc(keylen); + memset(key, 0, keylen); + memcpy(key, password, password_len); + } else { + key = (unsigned char*)password; + } + + ivlen = EVP_CIPHER_iv_length(cipher_type); + iv = emalloc(ivlen); + memset(iv, 0, ivlen); + + outlen = data_len + EVP_CIPHER_block_size(cipher_type); + outbuf = emalloc(outlen + 1); + + EVP_DecryptInit(&cipher_ctx, cipher_type, key, iv); + EVP_DecryptUpdate(&cipher_ctx, outbuf, &i, (unsigned char *)data, data_len); + outlen = i; + if (EVP_DecryptFinal(&cipher_ctx, (unsigned char *)outbuf + i, &i)) { + outlen += i; + outbuf[outlen] = '\0'; + RETVAL_STRINGL((char *)outbuf, outlen, 0); + } else { + efree(outbuf); + RETVAL_FALSE; + } + if (key != (unsigned char*)password) { + efree(key); + } + efree(iv); + if (base64_str) { + efree(base64_str); + } +} +/* }}} */ + + +/* {{{ proto string openssl_dh_compute_key(string pub_key, resource dh_key) + Computes shared sicret for public value of remote DH key and local DH key */ +PHP_FUNCTION(openssl_dh_compute_key) +{ + zval *key; + char *pub_str; + int pub_len; + EVP_PKEY *pkey; + BIGNUM *pub; + char *data; + int len; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sr", &pub_str, &pub_len, &key) == FAILURE) { + return; + } + ZEND_FETCH_RESOURCE(pkey, EVP_PKEY *, &key, -1, "OpenSSL key", le_key); + if (!pkey || EVP_PKEY_type(pkey->type) != EVP_PKEY_DH || !pkey->pkey.dh) { + RETURN_FALSE; + } + + pub = BN_bin2bn((unsigned char*)pub_str, pub_len, NULL); + + data = emalloc(DH_size(pkey->pkey.dh) + 1); + len = DH_compute_key((unsigned char*)data, pub, pkey->pkey.dh); + + if (len >= 0) { + data[len] = 0; + RETVAL_STRINGL(data, len, 0); + } else { + efree(data); + RETVAL_FALSE; + } + + BN_free(pub); +} +/* }}} */ + /* * Local variables: * tab-width: 8 |