Skip to content

Commit 7ef5754

Browse files
committed
Merge pull request php#1 from lt/rand-bytes
Changes from my fork of rand-bytes
2 parents a1e6229 + 77f99cc commit 7ef5754

File tree

4 files changed

+71
-56
lines changed

4 files changed

+71
-56
lines changed

Zend/Zend.m4

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,13 @@ if test -r "/dev/urandom" && test -c "/dev/urandom"; then
425425
AC_MSG_RESULT(yes)
426426
else
427427
AC_MSG_RESULT(no)
428-
AC_MSG_CHECKING(whether /dev/arandom exists)
429-
if test -r "/dev/arandom" && test -c "/dev/arandom"; then
430-
AC_DEFINE([HAVE_DEV_ARANDOM], 1, [Define if the target system has /dev/arandom device])
431-
AC_MSG_RESULT(yes)
432-
else
433-
AC_MSG_RESULT(no)
434-
fi
428+
fi
429+
430+
AC_MSG_CHECKING(whether /dev/arandom exists)
431+
if test -r "/dev/arandom" && test -c "/dev/arandom"; then
432+
AC_DEFINE([HAVE_DEV_ARANDOM], 1, [Define if the target system has /dev/arandom device])
433+
AC_MSG_RESULT(yes)
434+
else
435+
AC_MSG_RESULT(no)
435436
fi
437+

ext/standard/basic_functions.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1899,10 +1899,11 @@ ZEND_END_ARG_INFO()
18991899
/* }}} */
19001900
/* {{{ random.c */
19011901
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_bytes, 0, 0, 0)
1902-
ZEND_ARG_INFO(0, bytes)
1902+
ZEND_ARG_INFO(0, length)
19031903
ZEND_END_ARG_INFO()
19041904

19051905
ZEND_BEGIN_ARG_INFO_EX(arginfo_random_int, 0, 0, 0)
1906+
ZEND_ARG_INFO(0, min)
19061907
ZEND_ARG_INFO(0, max)
19071908
ZEND_END_ARG_INFO()
19081909
/* }}} */

ext/standard/config.m4

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,11 @@ dnl Check for atomic operation API availability in Solaris
592592
dnl
593593
AC_CHECK_HEADERS([atomic.h])
594594

595+
dnl
596+
dnl Check for arc4random on BSD systems
597+
dnl
598+
AC_CHECK_DECLS([arc4random_buf])
599+
595600
dnl
596601
dnl Setup extension sources
597602
dnl

ext/standard/random.c

Lines changed: 55 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -29,58 +29,54 @@
2929
# include "win32/winutil.h"
3030
#endif
3131

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)
32+
static int php_random_bytes(void *bytes, size_t size)
4033
{
4134
int n = 0;
4235

4336
#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) {
37+
/* Defer to CryptGenRandom on Windows */
38+
if (php_win32_get_random_bytes(bytes, size) == FAILURE) {
4739
php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
4840
return FAILURE;
4941
}
50-
n = (int)size;
5142
#else
52-
// @todo Need to cache the fd for random_int() call within loop
53-
int fd;
43+
#if HAVE_DECL_ARC4RANDOM_BUF
44+
arc4random_buf(bytes, size);
45+
#else
46+
int fd = -1;
5447
size_t read_bytes = 0;
55-
48+
#if HAVE_DEV_ARANDOM
49+
fd = open("/dev/arandom", O_RDONLY);
50+
#else
51+
#if HAVE_DEV_URANDOM
5652
fd = open("/dev/urandom", O_RDONLY);
53+
#endif // URANDOM
54+
#endif // ARANDOM
5755
if (fd < 0) {
5856
php_error_docref(NULL, E_WARNING, "Cannot open source device");
5957
return FAILURE;
6058
}
59+
6160
while (read_bytes < size) {
6261
n = read(fd, bytes + read_bytes, size - read_bytes);
6362
if (n < 0) {
6463
break;
6564
}
6665
read_bytes += n;
6766
}
68-
n = read_bytes;
6967

7068
close(fd);
71-
if (n < size) {
69+
if (read_bytes < size) {
7270
php_error_docref(NULL, E_WARNING, "Could not gather sufficient random data");
7371
return FAILURE;
7472
}
75-
#endif
76-
77-
// @todo - Do we need to do this?
78-
bytes[size] = '\0';
73+
#endif // !ARC4RANDOM_BUF
74+
#endif // !WIN32
7975

8076
return SUCCESS;
8177
}
8278

83-
/* {{{ proto string random_bytes(int bytes)
79+
/* {{{ proto string random_bytes(int length)
8480
Return an arbitrary length of pseudo-random bytes as binary string */
8581
PHP_FUNCTION(random_bytes)
8682
{
@@ -91,8 +87,8 @@ PHP_FUNCTION(random_bytes)
9187
return;
9288
}
9389

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);
90+
if (size < 1) {
91+
php_error_docref(NULL, E_WARNING, "Length must be greater than 0");
9692
RETURN_FALSE;
9793
}
9894

@@ -103,48 +99,59 @@ PHP_FUNCTION(random_bytes)
10399
return;
104100
}
105101

102+
bytes->val[size] = '\0';
103+
106104
RETURN_STR(bytes);
107105
}
108106
/* }}} */
109107

110-
/* {{{ proto int random_int(int maximum)
108+
/* {{{ proto int random_int(int max)
111109
Return an arbitrary pseudo-random integer */
112110
PHP_FUNCTION(random_int)
113111
{
114-
zend_long maximum;
115-
zend_long size;
116-
size_t i;
112+
zend_long min = ZEND_LONG_MIN;
113+
zend_long max = ZEND_LONG_MAX;
114+
zend_ulong limit;
115+
zend_ulong umax;
116+
zend_ulong result;
117117

118-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|l", &maximum) == FAILURE) {
118+
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|ll", &min, &max) == FAILURE) {
119119
return;
120120
}
121121

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);
122+
if (min >= max) {
123+
php_error_docref(NULL, E_WARNING, "Minimum value must be greater than the maximum value");
128124
RETURN_FALSE;
129125
}
130126

131-
long range = (long) maximum; // @todo Support min?
127+
umax = max - min;
132128

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;
129+
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
130+
return;
131+
}
139132

140-
do {
141-
if (php_random_bytes(&value.buffer, 8) == FAILURE) {
142-
return;
133+
// Special case where no modulus is required
134+
if (umax == ZEND_ULONG_MAX) {
135+
RETURN_LONG((zend_long)result);
136+
}
137+
138+
// Increment the max so the range is inclusive of max
139+
umax++;
140+
141+
// Powers of two are not biased
142+
if (umax & ~umax != umax) {
143+
// Ceiling under which ZEND_LONG_MAX % max == 0
144+
limit = ZEND_ULONG_MAX - (ZEND_ULONG_MAX % umax) - 1;
145+
146+
// Discard numbers over the limit to avoid modulo bias
147+
while (result > limit) {
148+
if (php_random_bytes(&result, sizeof(result)) == FAILURE) {
149+
return;
150+
}
143151
}
144-
result = value.number & mask;
145-
} while (result > maximum);
152+
}
146153

147-
RETURN_LONG(result);
154+
RETURN_LONG((zend_long)((result % umax) + min));
148155
}
149156
/* }}} */
150157

0 commit comments

Comments
 (0)