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 )
8480Return an arbitrary length of pseudo-random bytes as binary string */
8581PHP_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 )
111109Return an arbitrary pseudo-random integer */
112110PHP_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