Skip to content

Commit bc54d13

Browse files
SammyKnikic
authored andcommitted
Initial implementation for CSPRNG API
1 parent 8f9f21e commit bc54d13

File tree

6 files changed

+207
-2
lines changed

6 files changed

+207
-2
lines changed

ext/standard/basic_functions.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1905,6 +1905,15 @@ ZEND_END_ARG_INFO()
19051905
ZEND_BEGIN_ARG_INFO(arginfo_mt_getrandmax, 0)
19061906
ZEND_END_ARG_INFO()
19071907
/* }}} */
1908+
/* {{{ random.c */
1909+
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_bytes, 0, 0, 0)
1910+
ZEND_ARG_INFO(0, bytes)
1911+
ZEND_END_ARG_INFO()
1912+
1913+
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_int, 0, 0, 0)
1914+
ZEND_ARG_INFO(0, max)
1915+
ZEND_END_ARG_INFO()
1916+
/* }}} */
19081917
/* {{{ sha1.c */
19091918
ZEND_BEGIN_ARG_INFO_EX(arginfo_sha1, 0, 0, 1)
19101919
ZEND_ARG_INFO(0, str)
@@ -2828,6 +2837,9 @@ const zend_function_entry basic_functions[] = { /* {{{ */
28282837
PHP_FE(mt_srand, arginfo_mt_srand)
28292838
PHP_FE(mt_getrandmax, arginfo_mt_getrandmax)
28302839

2840+
PHP_FE(random_bytes, arginfo_random_bytes)
2841+
PHP_FE(random_int, arginfo_random_int)
2842+
28312843
#if HAVE_GETSERVBYNAME
28322844
PHP_FE(getservbyname, arginfo_getservbyname)
28332845
#endif

ext/standard/config.m4

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -605,7 +605,8 @@ PHP_NEW_EXTENSION(standard, array.c base64.c basic_functions.c browscap.c crc32.
605605
incomplete_class.c url_scanner_ex.c ftp_fopen_wrapper.c \
606606
http_fopen_wrapper.c php_fopen_wrapper.c credits.c css.c \
607607
var_unserializer.c ftok.c sha1.c user_filters.c uuencode.c \
608-
filters.c proc_open.c streamsfuncs.c http.c password.c,,,
608+
filters.c proc_open.c streamsfuncs.c http.c password.c \
609+
random.c,,,
609610
-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
610611

611612
PHP_ADD_MAKEFILE_FRAGMENT

ext/standard/config.w32

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ EXTENSION("standard", "array.c base64.c basic_functions.c browscap.c \
2020
url_scanner_ex.c ftp_fopen_wrapper.c http_fopen_wrapper.c \
2121
php_fopen_wrapper.c credits.c css.c var_unserializer.c ftok.c sha1.c \
2222
user_filters.c uuencode.c filters.c proc_open.c password.c \
23-
streamsfuncs.c http.c flock_compat.c", false /* never shared */,
23+
streamsfuncs.c http.c flock_compat.c random.c", false /* never shared */,
2424
'/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1');
2525
PHP_INSTALL_HEADERS("", "ext/standard");
2626
if (PHP_MBREGEX != "no") {

ext/standard/php_random.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| PHP Version 7 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 1997-2015 The PHP Group |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 3.01 of the PHP license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.php.net/license/3_01.txt |
11+
| If you did not receive a copy of the PHP license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| [email protected] so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Sammy Kaye Powers <[email protected]> |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
/* $Id$ */
20+
21+
#ifndef PHP_RANDOM_H
22+
#define PHP_RANDOM_H
23+
24+
PHP_FUNCTION(random_bytes);
25+
PHP_FUNCTION(random_int);
26+
#endif
27+
28+
/*
29+
* Local variables:
30+
* tab-width: 4
31+
* c-basic-offset: 4
32+
* End:
33+
*/

ext/standard/php_standard.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959
#include "php_ftok.h"
6060
#include "php_type.h"
6161
#include "php_password.h"
62+
#include "php_random.h"
6263

6364
#include "php_version.h"
6465
#define PHP_STANDARD_VERSION PHP_VERSION

ext/standard/random.c

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
/*
2+
+----------------------------------------------------------------------+
3+
| PHP Version 7 |
4+
+----------------------------------------------------------------------+
5+
| Copyright (c) 1997-2015 The PHP Group |
6+
+----------------------------------------------------------------------+
7+
| This source file is subject to version 3.01 of the PHP license, |
8+
| that is bundled with this package in the file LICENSE, and is |
9+
| available through the world-wide-web at the following url: |
10+
| http://www.php.net/license/3_01.txt |
11+
| If you did not receive a copy of the PHP license and are unable to |
12+
| obtain it through the world-wide-web, please send a note to |
13+
| [email protected] so we can mail you a copy immediately. |
14+
+----------------------------------------------------------------------+
15+
| Authors: Sammy Kaye Powers <[email protected]> |
16+
+----------------------------------------------------------------------+
17+
*/
18+
19+
/* $Id$ */
20+
21+
#include <stdlib.h>
22+
#include <sys/stat.h>
23+
#include <fcntl.h>
24+
#include <math.h>
25+
26+
#include "php.h"
27+
28+
#if PHP_WIN32
29+
# include "win32/winutil.h"
30+
#endif
31+
32+
// Big thanks to @ircmaxell for the help on this bit
33+
union rand_long_buffer {
34+
char buffer[8];
35+
long number;
36+
};
37+
38+
// Copy/pasted from mcrypt.c
39+
static int php_random_bytes(char *bytes, zend_long size)
40+
{
41+
int n = 0;
42+
43+
#if PHP_WIN32
44+
/* random/urandom equivalent on Windows */
45+
BYTE *win_bytes = (BYTE *) bytes;
46+
if (php_win32_get_random_bytes(win_bytes, (size_t) size) == FAILURE) {
47+
php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
48+
return FAILURE;
49+
}
50+
n = (int)size;
51+
#else
52+
// @todo Need to cache the fd for random_int() call within loop
53+
int fd;
54+
size_t read_bytes = 0;
55+
56+
fd = open("/dev/urandom", O_RDONLY);
57+
if (fd < 0) {
58+
php_error_docref(NULL, E_WARNING, "Cannot open source device");
59+
return FAILURE;
60+
}
61+
while (read_bytes < size) {
62+
n = read(fd, bytes + read_bytes, size - read_bytes);
63+
if (n < 0) {
64+
break;
65+
}
66+
read_bytes += n;
67+
}
68+
n = read_bytes;
69+
70+
close(fd);
71+
if (n < size) {
72+
php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
73+
return FAILURE;
74+
}
75+
#endif
76+
77+
// @todo - Do we need to do this?
78+
bytes[size] = '\0';
79+
80+
return SUCCESS;
81+
}
82+
83+
/* {{{ proto string random_bytes(int bytes)
84+
Return an arbitrary length of pseudo-random bytes as binary string */
85+
PHP_FUNCTION(random_bytes)
86+
{
87+
zend_long size;
88+
zend_string *bytes;
89+
90+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &size) == FAILURE) {
91+
return;
92+
}
93+
94+
if (size <= 0 || size >= INT_MAX) {
95+
php_error_docref(NULL, E_WARNING, "Cannot genrate a random string with a size of less than 1 or greater than %d", INT_MAX);
96+
RETURN_FALSE;
97+
}
98+
99+
bytes = zend_string_alloc(size, 0);
100+
101+
if (php_random_bytes(bytes->val, size) == FAILURE) {
102+
zend_string_release(bytes);
103+
return;
104+
}
105+
106+
RETURN_STR(bytes);
107+
}
108+
/* }}} */
109+
110+
/* {{{ proto int random_int(int maximum)
111+
Return an arbitrary pseudo-random integer */
112+
PHP_FUNCTION(random_int)
113+
{
114+
zend_long maximum;
115+
zend_long size;
116+
size_t i;
117+
118+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &maximum) == FAILURE) {
119+
return;
120+
}
121+
122+
if (ZEND_NUM_ARGS() == 0) {
123+
maximum = INT_MAX;
124+
}
125+
126+
if (maximum <= 0 || maximum > INT_MAX) {
127+
php_error_docref(NULL, E_WARNING, "Cannot use maximum less than 1 or greater than %d", INT_MAX);
128+
RETURN_FALSE;
129+
}
130+
131+
long range = (long) maximum; // @todo Support min?
132+
133+
// Big thanks to @ircmaxell for the help on this bit
134+
union rand_long_buffer value;
135+
long result;
136+
int bits = (int) (log((double) range) / log(2.0)) + 1;
137+
int bytes = MAX(ceil(bits / 8), 1);
138+
long mask = (long) pow(2.0, (double) bits) - 1;
139+
140+
do {
141+
if (php_random_bytes(&value.buffer, 8) == FAILURE) {
142+
return;
143+
}
144+
result = value.number & mask;
145+
} while (result > maximum);
146+
147+
RETURN_LONG(result);
148+
}
149+
/* }}} */
150+
151+
/*
152+
* Local variables:
153+
* tab-width: 4
154+
* c-basic-offset: 4
155+
* End:
156+
* vim600: sw=4 ts=4 fdm=marker
157+
* vim<600: sw=4 ts=4
158+
*/

0 commit comments

Comments
 (0)