]> granicus.if.org Git - php/commitdiff
Add PBKDF2 support via openssl()
authorScott MacVicar <scottmac@php.net>
Mon, 11 Jun 2012 07:16:30 +0000 (00:16 -0700)
committerScott MacVicar <scottmac@php.net>
Mon, 11 Jun 2012 20:35:25 +0000 (13:35 -0700)
Summary:
No easy way to put these in the hash extension since we don't really support optional
parameters to certain algorithms. Implemented in openssl for now since it has it already
and is pretty stable.

Only SHA1 is confirmed to work as an algorithm but openssl has a parameter so it can be
changed in the future.

Will backport to 5.4 potentially with Stas' approval.

Test Plan:
Ran newly added tests which came from RFC 6070

ext/openssl/openssl.c
ext/openssl/php_openssl.h
ext/openssl/tests/openssl_pkcs5_pbkdf2_hmac.phpt [new file with mode: 0644]

index 7187a9601e6628bf0f389acb76f6762c500da9be..28f76184f4947e72e92a8d99189c7980fa5cbbbc 100644 (file)
@@ -242,6 +242,16 @@ ZEND_BEGIN_ARG_INFO(arginfo_openssl_pkey_get_details, 0)
     ZEND_ARG_INFO(0, key)
 ZEND_END_ARG_INFO()
 
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs5_pbkdf2_hmac, 0, 0, 4)
+    ZEND_ARG_INFO(0, password)
+    ZEND_ARG_INFO(0, salt)
+    ZEND_ARG_INFO(0, key_length)
+    ZEND_ARG_INFO(0, iterations)
+    ZEND_ARG_INFO(0, digest_algorithm)
+ZEND_END_ARG_INFO()
+#endif
+
 ZEND_BEGIN_ARG_INFO_EX(arginfo_openssl_pkcs7_verify, 0, 0, 2)
     ZEND_ARG_INFO(0, filename)
     ZEND_ARG_INFO(0, flags)
@@ -428,6 +438,10 @@ const zend_function_entry openssl_functions[] = {
        PHP_FE(openssl_seal,                            arginfo_openssl_seal)
        PHP_FE(openssl_open,                            arginfo_openssl_open)
 
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+       PHP_FE(openssl_pkcs5_pbkdf2_hmac,       arginfo_openssl_pkcs5_pbkdf2_hmac)
+#endif
+
 /* for S/MIME handling */
        PHP_FE(openssl_pkcs7_verify,            arginfo_openssl_pkcs7_verify)
        PHP_FE(openssl_pkcs7_decrypt,           arginfo_openssl_pkcs7_decrypt)
@@ -3317,6 +3331,57 @@ PHP_FUNCTION(openssl_pkey_get_details)
 
 /* }}} */
 
+#if OPENSSL_VERSION_NUMBER >= 0x10000000L
+
+/* {{{ proto string openssl_pkcs5_pbkdf2_hmac(string password, string salt, long key_length, long iterations [, string digest_method = "sha1"])
+   Generates a PKCS5 v2 PBKDF2 string, defaults to sha1 */
+PHP_FUNCTION(openssl_pkcs5_pbkdf2_hmac)
+{
+       long key_length = 0, iterations = 0;
+       char *password; int password_len;
+       char *salt; int salt_len;
+       char *method; int method_len = 0;
+       unsigned char *out_buffer;
+
+       const EVP_MD *digest;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssll|s",
+                               &password, &password_len,
+                               &salt, &salt_len,
+                               &key_length, &iterations,
+                               &method, &method_len) == FAILURE) {
+               return;
+       }
+
+       if (key_length <= 0) {
+               RETURN_FALSE;
+       }
+
+       if (method_len) {
+               digest = EVP_get_digestbyname(method);
+       } else {
+               digest = EVP_sha1();
+       }
+
+       if (!digest) {
+               php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown signature algorithm");
+               RETURN_FALSE;
+       }
+
+       out_buffer = emalloc(key_length + 1);
+       out_buffer[key_length] = '\0';
+
+       if (PKCS5_PBKDF2_HMAC(password, password_len, (unsigned char *)salt, salt_len, iterations, digest, key_length, out_buffer) == 1) {
+               RETVAL_STRINGL((char *)out_buffer, key_length, 0);
+       } else {
+               efree(out_buffer);
+               RETURN_FALSE;
+       }
+}
+/* }}} */
+
+#endif
+
 /* {{{ PKCS7 S/MIME functions */
 
 /* {{{ proto bool openssl_pkcs7_verify(string filename, long flags [, string signerscerts [, array cainfo [, string extracerts [, string content]]]])
index fc118dba1ebb8d9f4af038852db18531bdf5712b..0dbe7d288730273a37284b1981341eca427be93a 100644 (file)
@@ -52,6 +52,8 @@ PHP_FUNCTION(openssl_private_decrypt);
 PHP_FUNCTION(openssl_public_encrypt);
 PHP_FUNCTION(openssl_public_decrypt);
 
+PHP_FUNCTION(openssl_pkcs5_pbkdf2_hmac);
+
 PHP_FUNCTION(openssl_pkcs7_verify);
 PHP_FUNCTION(openssl_pkcs7_decrypt);
 PHP_FUNCTION(openssl_pkcs7_sign);
diff --git a/ext/openssl/tests/openssl_pkcs5_pbkdf2_hmac.phpt b/ext/openssl/tests/openssl_pkcs5_pbkdf2_hmac.phpt
new file mode 100644 (file)
index 0000000..af1fcb1
--- /dev/null
@@ -0,0 +1,26 @@
+--TEST--
+openssl_pkcs5_pbkdf2_hmac() tests
+--SKIPIF--
+<?php if (!extension_loaded("openssl") || !function_exists("openssl_pkcs5_pbkdf2_hmac")) print "skip"; ?>
+--FILE--
+<?php
+// official test vectors
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac('password', 'salt', 20, 1)));
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac('password', 'salt', 20, 2)));
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac('password', 'salt', 20, 4096)));
+
+/* really slow but should be:
+string(40) "eefe3d61cd4da4e4e9945b3d6ba2158c2634e984"
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac('password', 'salt', 20, 16777216)));
+*/
+
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac('passwordPASSWORDpassword', 'saltSALTsaltSALTsaltSALTsaltSALTsalt', 25, 4096)));
+var_dump(bin2hex(openssl_pkcs5_pbkdf2_hmac("pass\0word", "sa\0lt", 16, 4096)));
+
+?>
+--EXPECTF--
+string(40) "0c60c80f961f0e71f3a9b524af6012062fe037a6"
+string(40) "ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957"
+string(40) "4b007901b765489abead49d926f721d065a429c1"
+string(50) "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"
+string(32) "56fa6aa75548099dcc37d7f03425e0c3"