]> granicus.if.org Git - php/commitdiff
Initial implementation for CSPRNG API
authorSammyK <sammyk@sammykmedia.com>
Fri, 13 Feb 2015 02:53:55 +0000 (20:53 -0600)
committerNikita Popov <nikic@php.net>
Sat, 9 May 2015 19:57:50 +0000 (21:57 +0200)
ext/standard/basic_functions.c
ext/standard/config.m4
ext/standard/config.w32
ext/standard/php_random.h [new file with mode: 0644]
ext/standard/php_standard.h
ext/standard/random.c [new file with mode: 0644]

index 693ac4c8e39f61346425a48c05a6be5fd46b459f..916f8ef0a01a871a3af1927f81b46aa8a6783380 100644 (file)
@@ -1905,6 +1905,15 @@ ZEND_END_ARG_INFO()
 ZEND_BEGIN_ARG_INFO(arginfo_mt_getrandmax, 0)
 ZEND_END_ARG_INFO()
 /* }}} */
+/* {{{ random.c */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_random_bytes, 0, 0, 0)
+       ZEND_ARG_INFO(0, bytes)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_random_int, 0, 0, 0)
+       ZEND_ARG_INFO(0, max)
+ZEND_END_ARG_INFO()
+/* }}} */
 /* {{{ sha1.c */
 ZEND_BEGIN_ARG_INFO_EX(arginfo_sha1, 0, 0, 1)
        ZEND_ARG_INFO(0, str)
@@ -2828,6 +2837,9 @@ const zend_function_entry basic_functions[] = { /* {{{ */
        PHP_FE(mt_srand,                                                                                                                arginfo_mt_srand)
        PHP_FE(mt_getrandmax,                                                                                                   arginfo_mt_getrandmax)
 
+       PHP_FE(random_bytes,                                                                                                    arginfo_random_bytes)
+       PHP_FE(random_int,                                                                                                      arginfo_random_int)
+
 #if HAVE_GETSERVBYNAME
        PHP_FE(getservbyname,                                                                                                   arginfo_getservbyname)
 #endif
index 3263389d00d52b9eeef3298043ac7a9a831f496b..a2c7a279f51a6e9a78435c31600e7657bf9ecf81 100644 (file)
@@ -605,7 +605,8 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
                             incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
                             http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
                             var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \
-                            filters.c proc_open.c streamsfuncs.c http.c password.c,,,
+                            filters.c proc_open.c streamsfuncs.c http.c password.c \
+                            random.c,,,
                            -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
 
 PHP_ADD_MAKEFILE_FRAGMENT
index e8b50efc7e9e905b03269aaa9eb7dbc05ef6e0bf..adff3d8c878af497ab531bb2ec94c2d4ca25010c 100644 (file)
@@ -20,7 +20,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
        url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
        php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \
        user_filters.c uuencode.c filters.c proc_open.c password.c \
-       streamsfuncs.c http.c flock_compat.c", false /* never shared */,
+       streamsfuncs.c http.c flock_compat.c random.c", false /* never shared */,
        '/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
        PHP_INSTALL_HEADERS("", "ext/standard");
 if (PHP_MBREGEX != "no") {
diff --git a/ext/standard/php_random.h b/ext/standard/php_random.h
new file mode 100644 (file)
index 0000000..1be5894
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2015 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:           |
+   | http://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: Sammy Kaye Powers <me@sammyk.me>                            |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#ifndef PHP_RANDOM_H
+#define PHP_RANDOM_H
+
+PHP_FUNCTION(random_bytes);
+PHP_FUNCTION(random_int);
+#endif
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
index 545406bef7223e39357769a09583dd9b544c23e1..418350738a6f9a7033ca20e5ae779123b745f996 100644 (file)
@@ -59,6 +59,7 @@
 #include "php_ftok.h"
 #include "php_type.h"
 #include "php_password.h"
+#include "php_random.h"
 
 #include "php_version.h"
 #define PHP_STANDARD_VERSION PHP_VERSION
diff --git a/ext/standard/random.c b/ext/standard/random.c
new file mode 100644 (file)
index 0000000..72377fe
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+   +----------------------------------------------------------------------+
+   | PHP Version 7                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2015 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:           |
+   | http://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: Sammy Kaye Powers <me@sammyk.me>                            |
+   +----------------------------------------------------------------------+
+*/
+
+/* $Id$ */
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <math.h>
+
+#include "php.h"
+
+#if PHP_WIN32
+# include "win32/winutil.h"
+#endif
+
+// Big thanks to @ircmaxell for the help on this bit
+union rand_long_buffer {
+       char buffer[8];
+       long number;
+};
+
+// Copy/pasted from mcrypt.c
+static int php_random_bytes(char *bytes, zend_long size)
+{
+       int n = 0;
+
+#if PHP_WIN32
+       /* random/urandom equivalent on Windows */
+       BYTE *win_bytes = (BYTE *) bytes;
+       if (php_win32_get_random_bytes(win_bytes, (size_t) size) == FAILURE) {
+               php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
+               return FAILURE;
+       }
+       n = (int)size;
+#else
+       // @todo Need to cache the fd for random_int() call within loop
+       int    fd;
+       size_t read_bytes = 0;
+
+       fd = open("/dev/urandom", O_RDONLY);
+       if (fd < 0) {
+               php_error_docref(NULL, E_WARNING, "Cannot open source device");
+               return FAILURE;
+       }
+       while (read_bytes < size) {
+               n = read(fd, bytes + read_bytes, size - read_bytes);
+               if (n < 0) {
+                       break;
+               }
+               read_bytes += n;
+       }
+       n = read_bytes;
+
+       close(fd);
+       if (n < size) {
+               php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
+               return FAILURE;
+       }
+#endif
+
+       // @todo - Do we need to do this?
+       bytes[size] = '\0';
+
+       return SUCCESS;
+}
+
+/* {{{ proto string random_bytes(int bytes)
+Return an arbitrary length of pseudo-random bytes as binary string */
+PHP_FUNCTION(random_bytes)
+{
+       zend_long size;
+       zend_string *bytes;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
+               return;
+       }
+
+       if (size <= 0 || size >= INT_MAX) {
+               php_error_docref(NULL, E_WARNING, "Cannot genrate a random string with a size of less than 1 or greater than %d", INT_MAX);
+               RETURN_FALSE;
+       }
+
+       bytes = zend_string_alloc(size, 0);
+
+       if (php_random_bytes(bytes->val, size) == FAILURE) {
+               zend_string_release(bytes);
+               return;
+       }
+
+       RETURN_STR(bytes);
+}
+/* }}} */
+
+/* {{{ proto int random_int(int maximum)
+Return an arbitrary pseudo-random integer */
+PHP_FUNCTION(random_int)
+{
+       zend_long maximum;
+       zend_long size;
+       size_t i;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &maximum) == FAILURE) {
+               return;
+       }
+
+       if (ZEND_NUM_ARGS() == 0) {
+               maximum = INT_MAX;
+       }
+
+       if (maximum <= 0 || maximum > INT_MAX) {
+               php_error_docref(NULL, E_WARNING, "Cannot use maximum less than 1 or greater than %d", INT_MAX);
+               RETURN_FALSE;
+       }
+
+       long range = (long) maximum; // @todo Support min?
+
+       // Big thanks to @ircmaxell for the help on this bit
+       union rand_long_buffer value;
+       long result;
+       int bits = (int) (log((double) range) / log(2.0)) + 1;
+       int bytes = MAX(ceil(bits / 8), 1);
+       long mask = (long) pow(2.0, (double) bits) - 1;
+
+       do {
+               if (php_random_bytes(&value.buffer, 8) == FAILURE) {
+                       return;
+               }
+               result = value.number & mask;
+       } while (result > maximum);
+
+       RETURN_LONG(result);
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */