I'd like to tuck the attached patch into the PHP 5 branch.
It provides the following functions, and does not modify the behavior
of any of the others:
openssl_bignum_from_bin
openssl_bignum_from_hex
openssl_bignum_from_dec
openssl_bignum_to_string
openssl_dh_generate_key
openssl_dh_compute_key
openssl_dh_get_params
openssl_dh_generate_parameters
openssl_dsa_verify
These functions allow one to implement OpenID and TypeKey
authentication schemes without resorting to writing crypto code in
user-space PHP--aside from the speed advantage, you have the benefit
of using the tried and trusted OpenSSL for your DH kex.
--Wez.
Index: openssl.c
===================================================================
RCS file: /repository/php-src/ext/openssl/openssl.c,v
retrieving revision 1.98.2.5.2.27
diff -u -p -r1.98.2.5.2.27 openssl.c
--- openssl.c 20 Jan 2007 22:08:29 -0000 1.98.2.5.2.27
+++ openssl.c 10 Feb 2007 04:02:04 -0000
@@ -42,6 +42,8 @@
#include <openssl/conf.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
+#include <openssl/dh.h>
+#include <openssl/dsa.h>
#define DEFAULT_KEY_LENGTH 512
#define MIN_KEY_LENGTH 384
@@ -135,6 +137,17 @@ zend_function_entry openssl_functions[]
PHP_FE(openssl_public_decrypt, second_arg_force_ref)
PHP_FE(openssl_error_string, NULL)
+
+ PHP_FE(openssl_bignum_from_bin, NULL)
+ PHP_FE(openssl_bignum_from_hex, NULL)
+ PHP_FE(openssl_bignum_from_dec, NULL)
+ PHP_FE(openssl_bignum_to_string, NULL)
+ PHP_FE(openssl_dh_generate_key, NULL)
+ PHP_FE(openssl_dh_compute_key, NULL)
+ PHP_FE(openssl_dh_get_params, NULL)
+ PHP_FE(openssl_dh_generate_parameters, NULL)
+ PHP_FE(openssl_dsa_verify, NULL)
+
{NULL, NULL, NULL}
};
/* }}} */
@@ -166,6 +179,14 @@ static int le_key;
static int le_x509;
static int le_csr;
static int ssl_stream_data_index;
+static int le_num;
+static int le_dh;
+
+typedef struct {
+ BIGNUM *bn;
+ long ref;
+ long me;
+} php_bignum;
int php_openssl_get_x509_list_id(void) /* {{{ */
{
@@ -194,6 +215,46 @@ static void php_csr_free(zend_rsrc_list_
X509_REQ * csr = (X509_REQ*)rsrc->ptr;
X509_REQ_free(csr);
}
+
+static void php_bignum_free(php_bignum *bn, int clear)
+{
+ if (bn->me != -1) {
+ zend_list_delete(bn->me);
+ } else {
+ if (bn->ref != -1) {
+ zend_list_delete(bn->ref);
+ } else if (bn->bn != BN_value_one()) {
+ if (clear)
+ BN_clear_free(bn->bn);
+ else
+ BN_free(bn->bn);
+ }
+ efree(bn);
+ }
+}
+
+static long bignum_resource(php_bignum *bn)
+{
+ if (bn->me == -1) {
+ bn->me = zend_list_insert(bn, le_num);
+ }
+ return bn->me;
+}
+
+static void php_bignum_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ php_bignum *bn = (php_bignum*)rsrc->ptr;
+ bn->me = -1;
+ php_bignum_free(bn, 1);
+}
+
+static void php_dh_free(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ DH *dh = (DH*)rsrc->ptr;
+ DH_free(dh);
+}
+
+
/* }}} */
/* {{{ openssl safe_mode & open_basedir checks */
@@ -637,6 +698,8 @@ PHP_MINIT_FUNCTION(openssl)
le_key = zend_register_list_destructors_ex(php_pkey_free, NULL,
"OpenSSL key", module_number);
le_x509 = zend_register_list_destructors_ex(php_x509_free, NULL,
"OpenSSL X.509", module_number);
le_csr = zend_register_list_destructors_ex(php_csr_free, NULL, "OpenSSL
X.509 CSR", module_number);
+ le_num = zend_register_list_destructors_ex(php_bignum_dtor, NULL,
"OpenSSL BIGNUM", module_number);
+ le_dh = zend_register_list_destructors_ex(php_dh_free, NULL, "OpenSSL
DH", module_number);
SSL_library_init();
OpenSSL_add_all_ciphers();
@@ -3361,6 +3424,400 @@ PHP_FUNCTION(openssl_open)
}
/* }}} */
+static php_bignum *openssl_bignum_from_zval(zval **val TSRMLS_DC)
+{
+ BIGNUM *bn = NULL;
+ php_bignum *bnw;
+ char *c;
+
+ if (Z_TYPE_PP(val) == IS_RESOURCE) {
+ /* is it a big number resource ? */
+ void * what;
+ int type;
+
+ what = zend_fetch_resource(val TSRMLS_CC, -1, "OpenSSL BIGNUM",
&type, 1, le_num);
+ if (!what) {
+ return NULL;
+ }
+ if (type == le_num) {
+ zend_list_addref(Z_LVAL_PP(val));
+ return (php_bignum*)what;
+ }
+ return NULL;
+ }
+
+ if (Z_TYPE_PP(val) != IS_STRING) {
+ return NULL;
+ }
+
+ /* force it to be a string */
+ convert_to_string_ex(val);
+
+ if (Z_STRLEN_PP(val) > 2) {
+ if (!strncmp(Z_STRVAL_PP(val), "0x", 2)) {
+ /* hex */
+ BN_hex2bn(&bn, Z_STRVAL_PP(val)+2);
+ }
+ }
+ if (bn == NULL) {
+ /* decimal */
+ BN_dec2bn(&bn, Z_STRVAL_PP(val));
+ }
+
+ if (bn) {
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = -1;
+ bnw->me = -1;
+ bnw->bn = bn;
+ } else {
+ bnw = NULL;
+ }
+ return bnw;
+}
+
+/* {{{ proto resource openssl_bignum_from_bin(string binary)
+ Returns a bignum resource from the raw binary bytes supplied. */
+PHP_FUNCTION(openssl_bignum_from_bin)
+{
+ char *bin;
+ int blen;
+ BIGNUM *bn = NULL;
+ php_bignum *bnw;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &bin, &blen)) {
+ return;
+ }
+
+ bn = BN_bin2bn((unsigned char*)bin, blen, NULL);
+
+ if (!bn) {
+ RETURN_FALSE;
+ }
+
+ bnw = emalloc(sizeof(*bnw));
+ bnw->me = -1;
+ bnw->ref = -1;
+ bnw->bn = bn;
+
+ RETURN_RESOURCE(bignum_resource(bnw));
+}
+/* }}} */
+
+/* {{{ proto resource openssl_bignum_from_hex(string hex)
+ Returns a bignum resource from the hex string supplied */
+PHP_FUNCTION(openssl_bignum_from_hex)
+{
+ char *bin;
+ int blen;
+ BIGNUM *bn = NULL;
+ php_bignum *bnw;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &bin, &blen)) {
+ return;
+ }
+
+ if (!BN_hex2bn(&bn, bin)) {
+ RETURN_FALSE;
+ }
+
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = -1;
+ bnw->me = -1;
+ bnw->bn = bn;
+
+ RETURN_RESOURCE(bignum_resource(bnw));
+}
+/* }}} */
+
+/* {{{ proto string openssl_bignum_to_string(mixed num [, int base])
+ Converts a big number to a string representation. base is the number
base; 0 for binary (default), 10 for decimal, 16 for hex */
+PHP_FUNCTION(openssl_bignum_to_string)
+{
+ zval **num;
+ php_bignum *bn;
+ long format = 0;
+ char *str;
+ int len;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Z|l",
+ &num, &format)) {
+ return;
+ }
+
+ bn = openssl_bignum_from_zval(num TSRMLS_CC);
+ if (!bn) RETURN_FALSE;
+
+ switch (format) {
+ case 0:
+ len = BN_num_bytes(bn->bn);
+ str = emalloc(len + 1);
+ BN_bn2bin(bn->bn, (unsigned char*)str);
+ RETVAL_STRINGL(str, len, 0);
+ break;
+ case 10:
+ str = BN_bn2dec(bn->bn);
+ RETVAL_STRING(str, 1);
+ OPENSSL_free(str);
+ break;
+ case 16:
+ str = BN_bn2hex(bn->bn);
+ RETVAL_STRING(str, 1);
+ OPENSSL_free(str);
+ break;
+ default:
+ RETVAL_FALSE;
+ }
+
+ php_bignum_free(bn, 0);
+}
+/* }}} */
+
+/* {{{ proto resource openssl_bignum_from_dec(string dec)
+ Returns a bignum resource from the decimal string supplied */
+PHP_FUNCTION(openssl_bignum_from_dec)
+{
+ char *bin;
+ int blen;
+ BIGNUM *bn = NULL;
+ php_bignum *bnw;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s",
+ &bin, &blen)) {
+ return;
+ }
+
+ if (!BN_dec2bn(&bn, bin)) {
+ RETURN_FALSE;
+ }
+
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = -1;
+ bnw->bn = bn;
+ bnw->me = -1;
+
+ RETURN_RESOURCE(bignum_resource(bnw));
+}
+/* }}} */
+
+
+/* {{{ proto resource openssl_dh_generate_key(mixed p, mixed g [, mixed priv])
+ Given shared parameters p and g, generates private and public keys */
+PHP_FUNCTION(openssl_dh_generate_key)
+{
+ zval **p, **g, **priv = NULL;
+ DH *dh;
+ php_bignum *P, *G, *PRIV = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ZZ|Z",
+ &p, &g, &priv) == FAILURE) {
+ return;
+ }
+
+ P = openssl_bignum_from_zval(p TSRMLS_CC);
+ G = openssl_bignum_from_zval(g TSRMLS_CC);
+ if (priv) {
+ PRIV = openssl_bignum_from_zval(priv TSRMLS_CC);
+ }
+
+ dh = DH_new();
+ dh->p = P->bn;
+ dh->g = G->bn;
+ if (PRIV) {
+ dh->priv_key = PRIV->bn;
+ }
+
+ if (DH_generate_key(dh)) {
+ /* we're good */
+ ZVAL_RESOURCE(return_value, zend_list_insert(dh, le_dh));
+ return;
+ }
+
+ if (P) php_bignum_free(P, 0);
+ if (G) php_bignum_free(G, 0);
+ if (PRIV) php_bignum_free(PRIV, 1);
+ DH_free(dh);
+}
+/* }}} */
+
+/* {{{ proto string openssl_dh_compute_key(resource dh, mixed peer_pub)
+ Computes the shared secret from the private DH value and the other party's
public value, returning it as a raw binary string */
+PHP_FUNCTION(openssl_dh_compute_key)
+{
+ zval **pub, *dhval;
+ long pres;
+ DH *dh;
+ php_bignum *PUB;
+ char *data;
+ int len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rZ",
+ &dhval, &pub) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(dh, DH *, &dhval, -1, "OpenSSL DH", le_dh);
+
+ PUB = openssl_bignum_from_zval(pub TSRMLS_CC);
+
+ data = emalloc(DH_size(dh) + 1);
+ len = DH_compute_key((unsigned char*)data, PUB->bn, dh);
+
+ if (len >= 0) {
+ RETVAL_STRINGL(data, len, 0);
+ } else {
+ RETVAL_FALSE;
+ }
+
+ if (PUB) php_bignum_free(PUB, 0);
+}
+/* }}} */
+
+/* {{{ proto array openssl_dh_get_params(resource dh)
+ Returns the various pieces of the DH as an array of big numbers */
+PHP_FUNCTION(openssl_dh_get_params)
+{
+ DH *dh;
+ zval *dhval;
+ zval *tmp;
+ php_bignum *bnw;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r",
+ &dhval) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(dh, DH *, &dhval, -1, "OpenSSL DH", le_dh);
+
+ array_init(return_value);
+
+ if (dh->p) {
+ MAKE_STD_ZVAL(tmp);
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = Z_LVAL_P(dhval);
+ zend_list_addref(bnw->ref);
+ bnw->me = -1;
+ bnw->bn = dh->p;
+ ZVAL_RESOURCE(tmp, bignum_resource(bnw));
+ add_assoc_zval(return_value, "p", tmp);
+ }
+ if (dh->g) {
+ MAKE_STD_ZVAL(tmp);
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = Z_LVAL_P(dhval);
+ zend_list_addref(bnw->ref);
+ bnw->me = -1;
+ bnw->bn = dh->g;
+ ZVAL_RESOURCE(tmp, bignum_resource(bnw));
+ add_assoc_zval(return_value, "g", tmp);
+ }
+ if (dh->pub_key) {
+ MAKE_STD_ZVAL(tmp);
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = Z_LVAL_P(dhval);
+ zend_list_addref(bnw->ref);
+ bnw->me = -1;
+ bnw->bn = dh->pub_key;
+ ZVAL_RESOURCE(tmp, bignum_resource(bnw));
+ add_assoc_zval(return_value, "pub_key", tmp);
+ }
+ if (dh->priv_key) {
+ MAKE_STD_ZVAL(tmp);
+ bnw = emalloc(sizeof(*bnw));
+ bnw->ref = Z_LVAL_P(dhval);
+ zend_list_addref(bnw->ref);
+ bnw->me = -1;
+ bnw->bn = dh->priv_key;
+ ZVAL_RESOURCE(tmp, bignum_resource(bnw));
+ add_assoc_zval(return_value, "priv_key", tmp);
+ }
+}
+/* }}} */
+
+/* {{{ proto resource openssl_dh_generate_parameters(int prime_len [, int
generator])
+ Generates parameters g and p for a given length of prime. Generator is
either 2 or 5. */
+PHP_FUNCTION(openssl_dh_generate_parameters)
+{
+ long prime_len, generator = 2;
+ DH *dh;
+ int codes;
+
+ if (FAILURE == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l",
+ &prime_len, &generator)) {
+ return;
+ }
+
+ if (generator != 2 && generator != 5) {
+ RETURN_FALSE;
+ }
+
+ dh = DH_generate_parameters(prime_len, generator, NULL, NULL);
+
+ if (dh) {
+ codes = 0;
+ if (DH_check(dh, &codes) && codes == 0) {
+ RETURN_RESOURCE(zend_list_insert(dh, le_dh));
+ }
+ DH_free(dh);
+ }
+}
+/* }}} */
+
+/* {{{ proto bool openssl_dsa_verify(string dgst, mixed r, mixed s, mixed p,
mixed q, mixed g, mixed pub)
+ Verifies that sig is a valid signature of dgst, given the signing
parameters. */
+PHP_FUNCTION(openssl_dsa_verify)
+{
+ char *dgst;
+ int dlen;
+ zval **p, **q, **g, **r, **s, **pub;
+ DSA *dsa;
+ php_bignum *P, *Q, *G, *R, *S, *PUB;
+ DSA_SIG sig;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sZZZZZZ",
+ &dgst, &dlen,
+ &r, &s, &p, &q, &g, &pub) == FAILURE) {
+ return;
+ }
+
+ P = openssl_bignum_from_zval(p TSRMLS_CC);
+ Q = openssl_bignum_from_zval(q TSRMLS_CC);
+ G = openssl_bignum_from_zval(g TSRMLS_CC);
+ R = openssl_bignum_from_zval(r TSRMLS_CC);
+ S = openssl_bignum_from_zval(s TSRMLS_CC);
+ PUB = openssl_bignum_from_zval(pub TSRMLS_CC);
+
+ dsa = DSA_new();
+ dsa->p = P->bn;
+ dsa->q = Q->bn;
+ dsa->g = G->bn;
+ dsa->pub_key = PUB->bn;
+ sig.r = R->bn;
+ sig.s = S->bn;
+
+ switch (DSA_do_verify((unsigned char*)dgst, dlen, &sig, dsa)) {
+ case -1:
+ case 0:
+ RETVAL_FALSE;
+ break;
+ case 1:
+ RETVAL_TRUE;
+ }
+ dsa->p = NULL;
+ dsa->q = NULL;
+ dsa->g = NULL;
+ dsa->pub_key = NULL;
+ DSA_free(dsa);
+ php_bignum_free(P, 0);
+ php_bignum_free(Q, 0);
+ php_bignum_free(G, 0);
+ php_bignum_free(R, 0);
+ php_bignum_free(S, 0);
+ php_bignum_free(PUB, 0);
+}
+/* }}} */
+
/* SSL verification functions */
#define GET_VER_OPT(name) (stream->context && SUCCESS ==
php_stream_context_get_option(stream->context, "ssl", name, &val))
Index: php_openssl.h
===================================================================
RCS file: /repository/php-src/ext/openssl/php_openssl.h,v
retrieving revision 1.16.2.1.2.3
diff -u -p -r1.16.2.1.2.3 php_openssl.h
--- php_openssl.h 1 Jan 2007 09:36:04 -0000 1.16.2.1.2.3
+++ php_openssl.h 10 Feb 2007 04:02:05 -0000
@@ -69,6 +69,18 @@ PHP_FUNCTION(openssl_csr_export_to_file)
PHP_FUNCTION(openssl_csr_sign);
PHP_FUNCTION(openssl_csr_get_subject);
PHP_FUNCTION(openssl_csr_get_public_key);
+
+PHP_FUNCTION(openssl_bignum_from_bin);
+PHP_FUNCTION(openssl_bignum_from_hex);
+PHP_FUNCTION(openssl_bignum_from_dec);
+PHP_FUNCTION(openssl_bignum_to_string);
+
+PHP_FUNCTION(openssl_dh_generate_key);
+PHP_FUNCTION(openssl_dh_compute_key);
+PHP_FUNCTION(openssl_dh_get_params);
+PHP_FUNCTION(openssl_dh_generate_parameters);
+
+PHP_FUNCTION(openssl_dsa_verify);
#else
#define phpext_openssl_ptr NULL
--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php