Index: ext/openssl/tests/026.phpt =================================================================== --- ext/openssl/tests/026.phpt (revision 0) +++ ext/openssl/tests/026.phpt (revision 0) @@ -0,0 +1,84 @@ +--TEST-- +openssl_*() with OPENSSL_KEYTYPE_EC +--SKIPIF-- + +--FILE-- + "secp384r1", + "private_key_type" => OPENSSL_KEYTYPE_EC, +); + +$key1 = openssl_pkey_new($args); +var_dump($key1); + +$d1 = openssl_pkey_get_details($key1); +var_dump($d1["bits"]); +var_dump(strlen($d1["key"])); +var_dump($d1["ec"]["group_name"]); +var_dump(strlen($d1["ec"]["priv_key"])); +var_dump(strlen($d1["ec"]["pub_key"])); +var_dump($d1["type"] == OPENSSL_KEYTYPE_EC); + +$key2 = openssl_pkey_new($d1); +var_dump($key2); + +$d2 = openssl_pkey_get_details($key2); +var_dump(array_diff_assoc($d2, $d1)); +var_dump(array_diff_assoc($d2["ec"], $d1["ec"])); + + +$dn = array( + "countryName" => "BR", + "stateOrProvinceName" => "Rio Grande do Sul", + "localityName" => "Porto Alegre", + "commonName" => "Henrique do N. Angelo", + "emailAddress" => "hnangelo@php.net" +); +$csr = openssl_csr_new($dn, $key1, $args); + +$args["digest_alg"] = "sha1"; +$csr = openssl_csr_new($dn, $key1, $args); +var_dump($csr); + +$pubkey1 = openssl_pkey_get_details(openssl_csr_get_public_key($csr)); +var_dump(array_diff_assoc($d1, $pubkey1)); +var_dump(isset($pubkey1["ec"]["priv_key"])); +unset($d1["ec"]["priv_key"]); +var_dump(array_diff($d1["ec"], $pubkey1["ec"])); + +$x509 = openssl_csr_sign($csr, null, $key1, 365, $args); +var_dump($x509); + + +var_dump(openssl_x509_check_private_key($x509, $key1)); + +$key3 = openssl_pkey_new($args); +var_dump(openssl_x509_check_private_key($x509, $key3)); + +?> +--EXPECTF-- +resource(%d) of type (OpenSSL key) +int(384) +int(215) +string(9) "secp384r1" +int(48) +int(97) +bool(true) +resource(%d) of type (OpenSSL key) +array(0) { +} +array(0) { +} + +Warning: openssl_csr_new(): Error signing request in %s on line %d +resource(%d) of type (OpenSSL X.509 CSR) +array(0) { +} +bool(false) +array(0) { +} +resource(%d) of type (OpenSSL X.509) +bool(true) +bool(false) Index: ext/openssl/openssl.c =================================================================== --- ext/openssl/openssl.c (revision 323630) +++ ext/openssl/openssl.c (working copy) @@ -98,6 +98,9 @@ PHP_FUNCTION(openssl_get_md_methods); PHP_FUNCTION(openssl_get_cipher_methods); +#ifdef EVP_PKEY_EC +PHP_FUNCTION(openssl_get_curve_names); +#endif PHP_FUNCTION(openssl_digest); PHP_FUNCTION(openssl_encrypt); @@ -340,6 +343,11 @@ ZEND_ARG_INFO(0, aliases) ZEND_END_ARG_INFO() +#ifdef EVP_PKEY_EC +ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_get_curve_names, 0, 0, 0) +ZEND_END_ARG_INFO() +#endif + ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_digest, 0, 0, 2) ZEND_ARG_INFO(0, data) ZEND_ARG_INFO(0, method) @@ -437,6 +445,9 @@ PHP_FE(openssl_get_md_methods, arginfo_openssl_get_md_methods) PHP_FE(openssl_get_cipher_methods, arginfo_openssl_get_cipher_methods) +#ifdef EVP_PKEY_EC + PHP_FE(openssl_get_curve_names, arginfo_openssl_get_curve_names) +#endif PHP_FE(openssl_dh_compute_key, arginfo_openssl_dh_compute_key) @@ -535,6 +546,10 @@ int priv_key_encrypt; +#ifdef EVP_PKEY_EC + int ec_group_name; +#endif + EVP_PKEY * priv_key; const EVP_CIPHER * priv_key_encrypt_cipher; @@ -858,6 +873,18 @@ } PHP_SSL_CONFIG_SYNTAX_CHECK(request_extensions_section); + +#ifdef EVP_PKEY_EC + /* set the ec group curve name */ + req->ec_group_name = 0; + if (optional_args && zend_hash_find(Z_ARRVAL_P(optional_args), "ec_group_name", sizeof("ec_group_name"), (void**)&item) == SUCCESS) { + req->ec_group_name = OBJ_sn2nid(Z_STRVAL_PP(item)); + if (req->ec_group_name == NID_undef) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown elliptic curve group (short) name %s", Z_STRVAL_PP(item)); + return FAILURE; + } + } +#endif return SUCCESS; } @@ -2843,6 +2870,26 @@ } break; #endif +#ifdef EVP_PKEY_EC + case OPENSSL_KEYTYPE_EC: + { + if (req->ec_group_name == NID_undef) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "Missing configuration value: 'ec_group_name' not set"); + return NULL; + } + EC_KEY *eckey = EC_KEY_new_by_curve_name(req->ec_group_name); + if (eckey) { + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + if (EC_KEY_generate_key(eckey) && + EVP_PKEY_assign_EC_KEY(req->priv_key, eckey)) { + return_val = req->priv_key; + } else { + EC_KEY_free(eckey); + } + } + } + break; +#endif default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unsupported private key type"); } @@ -2898,6 +2945,14 @@ } break; #endif +#ifdef EVP_PKEY_EC + case EVP_PKEY_EC: + assert(pkey->pkey.ec != NULL); + if (EC_KEY_get0_private_key(pkey->pkey.ec) == NULL) { + return 0; + } + break; +#endif default: php_error_docref(NULL TSRMLS_CC, E_WARNING, "key type not supported in this PHP build!"); break; @@ -3015,6 +3070,56 @@ } RETURN_FALSE; } +#ifdef EVP_PKEY_EC + else if (zend_hash_find(Z_ARRVAL_P(args), "ec", sizeof("ec"), (void**)&data) == SUCCESS && + Z_TYPE_PP(data) == IS_ARRAY) { + pkey = EVP_PKEY_new(); + if (pkey) { + EC_KEY *eckey = EC_KEY_new(); + if (eckey) { + EC_GROUP *group = NULL; + zval **bn; + + if (zend_hash_find(Z_ARRVAL_PP(data), "group_name", sizeof("group_name"), (void**)&bn) == SUCCESS && + Z_TYPE_PP(bn) == IS_STRING) { + int name = OBJ_sn2nid(Z_STRVAL_PP(bn)); + if (name != NID_undef) { + group = EC_GROUP_new_by_curve_name(name); + EC_GROUP_set_asn1_flag(group, OPENSSL_EC_NAMED_CURVE); + EC_KEY_set_group(eckey, group); + } + } + + if (zend_hash_find(Z_ARRVAL_PP(data), "priv_key", sizeof("priv_key"), (void**)&bn) == SUCCESS && + Z_TYPE_PP(bn) == IS_STRING) { + EC_KEY_set_private_key(eckey, BN_bin2bn((unsigned char*)Z_STRVAL_PP(bn), Z_STRLEN_PP(bn), NULL)); + } + + if (zend_hash_find(Z_ARRVAL_PP(data), "pub_key", sizeof("pub_key"), (void**)&bn) == SUCCESS && + Z_TYPE_PP(bn) == IS_STRING && group != NULL) { + EC_POINT *pub_key = EC_POINT_new(group); + EC_POINT_oct2point(group, pub_key, (unsigned char*)Z_STRVAL_PP(bn), Z_STRLEN_PP(bn), NULL); + EC_KEY_set_public_key(eckey, pub_key); + } + + /* we don't need that any longer. key holds it's own copy */ + if (group != NULL) { + EC_GROUP_free(group); + } + + if (!EC_KEY_check_key(eckey)) { + EC_KEY_generate_key(eckey); + } + if (EC_KEY_check_key(eckey) && EVP_PKEY_assign_EC_KEY(pkey, eckey)) { + RETURN_RESOURCE(zend_list_insert(pkey, le_key)); + } + EC_KEY_free(eckey); + } + EVP_PKEY_free(pkey); + } + RETURN_FALSE; + } +#endif } PHP_SSL_REQ_INIT(&req); @@ -3299,6 +3404,38 @@ #ifdef EVP_PKEY_EC case EVP_PKEY_EC: ktype = OPENSSL_KEYTYPE_EC; + if (pkey->pkey.ec != NULL) { + zval *ec; + + ALLOC_INIT_ZVAL(ec); + array_init(ec); + + const EC_GROUP *group = EC_KEY_get0_group(pkey->pkey.ec); + int name = (group != NULL) ? EC_GROUP_get_curve_name(group) : 0; + if (name) { + add_assoc_string(ec, "group_name", estrdup(OBJ_nid2sn(name)), 0); + } + + const BIGNUM *key = EC_KEY_get0_private_key(pkey->pkey.ec); + if (key != NULL) { + int len = BN_num_bytes(key); + char *str = emalloc(len + 1); + BN_bn2bin(key, (unsigned char*)str); + str[len] = 0; + add_assoc_stringl(ec, "priv_key", str, len, 0); + } + + const EC_POINT *pub_key = EC_KEY_get0_public_key(pkey->pkey.ec); + if (group != NULL && pub_key != NULL) { + int len = EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); + char *str = emalloc(len + 1); + EC_POINT_point2oct(group, pub_key, POINT_CONVERSION_UNCOMPRESSED, str, len, NULL); + str[len] = 0; + add_assoc_stringl(ec, "pub_key", str, len, 0); + } + + add_assoc_zval(return_value, "ec", ec); + } break; #endif default: @@ -4609,6 +4746,34 @@ } /* }}} */ +/* {{{ proto array openssl_get_curve_names([bool aliases = false]) + Return array of available elliptic curves */ +#ifdef EVP_PKEY_EC +PHP_FUNCTION(openssl_get_curve_names) +{ + EC_builtin_curve *curves = NULL; + const char *sname; + int i; + + array_init(return_value); + size_t len = EC_get_builtin_curves(NULL, 0); + curves = emalloc((int)(sizeof(EC_builtin_curve) * len)); + if (!EC_get_builtin_curves(curves, len)) { + return; + } + + for(i = 0; i < len; i++) + { + sname = OBJ_nid2sn(curves[i].nid); + if (sname == NULL) + continue; + add_next_index_string(return_value, sname, 1); + } + efree(curves); +} +#endif +/* }}} */ + /* {{{ 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)