29
29
# include "win32/winutil.h"
30
30
#endif
31
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 )
32
+ static int php_random_bytes (void * bytes , size_t size )
40
33
{
41
34
int n = 0 ;
42
35
43
36
#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 ) {
47
39
php_error_docref (NULL , E_WARNING , "Could not gather sufficient random data" );
48
40
return FAILURE ;
49
41
}
50
- n = (int )size ;
51
42
#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 ;
54
47
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
56
52
fd = open ("/dev/urandom" , O_RDONLY );
53
+ #endif // URANDOM
54
+ #endif // ARANDOM
57
55
if (fd < 0 ) {
58
56
php_error_docref (NULL , E_WARNING , "Cannot open source device" );
59
57
return FAILURE ;
60
58
}
59
+
61
60
while (read_bytes < size ) {
62
61
n = read (fd , bytes + read_bytes , size - read_bytes );
63
62
if (n < 0 ) {
64
63
break ;
65
64
}
66
65
read_bytes += n ;
67
66
}
68
- n = read_bytes ;
69
67
70
68
close (fd );
71
- if (n < size ) {
69
+ if (read_bytes < size ) {
72
70
php_error_docref (NULL , E_WARNING , "Could not gather sufficient random data" );
73
71
return FAILURE ;
74
72
}
75
- #endif
76
-
77
- // @todo - Do we need to do this?
78
- bytes [size ] = '\0' ;
73
+ #endif // !ARC4RANDOM_BUF
74
+ #endif // !WIN32
79
75
80
76
return SUCCESS ;
81
77
}
82
78
83
- /* {{{ proto string random_bytes(int bytes )
79
+ /* {{{ proto string random_bytes(int length )
84
80
Return an arbitrary length of pseudo-random bytes as binary string */
85
81
PHP_FUNCTION (random_bytes )
86
82
{
@@ -91,8 +87,8 @@ PHP_FUNCTION(random_bytes)
91
87
return ;
92
88
}
93
89
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" );
96
92
RETURN_FALSE ;
97
93
}
98
94
@@ -103,48 +99,59 @@ PHP_FUNCTION(random_bytes)
103
99
return ;
104
100
}
105
101
102
+ bytes -> val [size ] = '\0' ;
103
+
106
104
RETURN_STR (bytes );
107
105
}
108
106
/* }}} */
109
107
110
- /* {{{ proto int random_int(int maximum )
108
+ /* {{{ proto int random_int(int max )
111
109
Return an arbitrary pseudo-random integer */
112
110
PHP_FUNCTION (random_int )
113
111
{
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 ;
117
117
118
- if (zend_parse_parameters (ZEND_NUM_ARGS (), "|l " , & maximum ) == FAILURE ) {
118
+ if (zend_parse_parameters (ZEND_NUM_ARGS (), "|ll " , & min , & max ) == FAILURE ) {
119
119
return ;
120
120
}
121
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 );
122
+ if (min >= max ) {
123
+ php_error_docref (NULL , E_WARNING , "Minimum value must be greater than the maximum value" );
128
124
RETURN_FALSE ;
129
125
}
130
126
131
- long range = ( long ) maximum ; // @todo Support min?
127
+ umax = max - min ;
132
128
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
+ }
139
132
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
+ }
143
151
}
144
- result = value .number & mask ;
145
- } while (result > maximum );
152
+ }
146
153
147
- RETURN_LONG (result );
154
+ RETURN_LONG (( zend_long )(( result % umax ) + min ) );
148
155
}
149
156
/* }}} */
150
157
0 commit comments