1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Authors: Rasmus Lerdorf <rasmus@php.net> |
14 | Zeev Suraski <zeev@php.net> |
15 | Pedro Melo <melo@ip.pt> |
16 | Sterling Hughes <sterling@php.net> |
17 | Go Kudo <zeriyoshi@php.net> |
18 | |
19 | Based on code from: Richard J. Wagner <rjwagner@writeme.com> |
20 | Makoto Matsumoto <matumoto@math.keio.ac.jp> |
21 | Takuji Nishimura |
22 | Shawn Cokus <Cokus@math.washington.edu> |
23 +----------------------------------------------------------------------+
24 */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include "php.h"
31 #include "php_random.h"
32
33 #include "Zend/zend_exceptions.h"
34
35 /*
36 The following mt19937 algorithms are based on a C++ class MTRand by
37 Richard J. Wagner. For more information see the web page at
38 http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/C-LANG/MersenneTwister.h
39
40 Mersenne Twister random number generator -- a C++ class MTRand
41 Based on code by Makoto Matsumoto, Takuji Nishimura, and Shawn Cokus
42 Richard J. Wagner v1.0 15 May 2003 rjwagner@writeme.com
43
44 The Mersenne Twister is an algorithm for generating random numbers. It
45 was designed with consideration of the flaws in various other generators.
46 The period, 2^19937-1, and the order of equidistribution, 623 dimensions,
47 are far greater. The generator is also fast; it avoids multiplication and
48 division, and it benefits from caches and pipelines. For more information
49 see the inventors' web page at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
50
51 Reference
52 M. Matsumoto and T. Nishimura, "Mersenne Twister: A 623-Dimensionally
53 Equidistributed Uniform Pseudo-Random Number Generator", ACM Transactions on
54 Modeling and Computer Simulation, Vol. 8, No. 1, January 1998, pp 3-30.
55
56 Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
57 Copyright (C) 2000 - 2003, Richard J. Wagner
58 All rights reserved.
59
60 Redistribution and use in source and binary forms, with or without
61 modification, are permitted provided that the following conditions
62 are met:
63
64 1. Redistributions of source code must retain the above copyright
65 notice, this list of conditions and the following disclaimer.
66
67 2. Redistributions in binary form must reproduce the above copyright
68 notice, this list of conditions and the following disclaimer in the
69 documentation and/or other materials provided with the distribution.
70
71 3. The names of its contributors may not be used to endorse or promote
72 products derived from this software without specific prior written
73 permission.
74
75 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
76 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
77 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
78 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
79 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
80 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
81 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
82 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
83 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
84 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
85 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
86 */
87
88 #define N MT_N /* length of state vector */
89 #define M (397) /* a period parameter */
90 #define hiBit(u) ((u) & 0x80000000U) /* mask all but highest bit of u */
91 #define loBit(u) ((u) & 0x00000001U) /* mask all but lowest bit of u */
92 #define loBits(u) ((u) & 0x7FFFFFFFU) /* mask the highest bit of u */
93 #define mixBits(u, v) (hiBit(u) | loBits(v)) /* move hi bit of u to hi bit of v */
94
95 #define twist(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(v))) & 0x9908b0dfU))
96 #define twist_php(m,u,v) (m ^ (mixBits(u,v) >> 1) ^ ((uint32_t)(-(int32_t)(loBit(u))) & 0x9908b0dfU))
97
mt19937_reload(php_random_status_state_mt19937 * state)98 static inline void mt19937_reload(php_random_status_state_mt19937 *state)
99 {
100 uint32_t *p = state->state;
101
102 if (state->mode == MT_RAND_MT19937) {
103 for (uint32_t i = N - M; i--; ++p) {
104 *p = twist(p[M], p[0], p[1]);
105 }
106 for (uint32_t i = M; --i; ++p) {
107 *p = twist(p[M-N], p[0], p[1]);
108 }
109 *p = twist(p[M-N], p[0], state->state[0]);
110 } else {
111 for (uint32_t i = N - M; i--; ++p) {
112 *p = twist_php(p[M], p[0], p[1]);
113 }
114 for (uint32_t i = M; --i; ++p) {
115 *p = twist_php(p[M-N], p[0], p[1]);
116 }
117 *p = twist_php(p[M-N], p[0], state->state[0]);
118 }
119
120 state->count = 0;
121 }
122
mt19937_seed_state(php_random_status_state_mt19937 * state,uint64_t seed)123 static inline void mt19937_seed_state(php_random_status_state_mt19937 *state, uint64_t seed)
124 {
125 uint32_t i, prev_state;
126
127 /* Initialize generator state with seed
128 See Knuth TAOCP Vol 2, 3rd Ed, p.106 for multiplier.
129 In previous versions, most significant bits (MSBs) of the seed affect
130 only MSBs of the state array. Modified 9 Jan 2002 by Makoto Matsumoto. */
131 state->state[0] = seed & 0xffffffffU;
132 for (i = 1; i < MT_N; i++) {
133 prev_state = state->state[i - 1];
134 state->state[i] = (1812433253U * (prev_state ^ (prev_state >> 30)) + i) & 0xffffffffU;
135 }
136 state->count = i;
137
138 mt19937_reload(state);
139 }
140
seed(php_random_status * status,uint64_t seed)141 static void seed(php_random_status *status, uint64_t seed)
142 {
143 mt19937_seed_state(status->state, seed);
144 }
145
generate(php_random_status * status)146 static uint64_t generate(php_random_status *status)
147 {
148 php_random_status_state_mt19937 *s = status->state;
149 uint32_t s1;
150
151 if (s->count >= MT_N) {
152 mt19937_reload(s);
153 }
154
155 s1 = s->state[s->count++];
156 s1 ^= (s1 >> 11);
157 s1 ^= (s1 << 7) & 0x9d2c5680U;
158 s1 ^= (s1 << 15) & 0xefc60000U;
159
160 return (uint64_t) (s1 ^ (s1 >> 18));
161 }
162
range(php_random_status * status,zend_long min,zend_long max)163 static zend_long range(php_random_status *status, zend_long min, zend_long max)
164 {
165 return php_random_range(&php_random_algo_mt19937, status, min, max);
166 }
167
serialize(php_random_status * status,HashTable * data)168 static bool serialize(php_random_status *status, HashTable *data)
169 {
170 php_random_status_state_mt19937 *s = status->state;
171 zval t;
172
173 for (uint32_t i = 0; i < MT_N; i++) {
174 ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint32_t)));
175 zend_hash_next_index_insert(data, &t);
176 }
177 ZVAL_LONG(&t, s->count);
178 zend_hash_next_index_insert(data, &t);
179 ZVAL_LONG(&t, s->mode);
180 zend_hash_next_index_insert(data, &t);
181
182 return true;
183 }
184
unserialize(php_random_status * status,HashTable * data)185 static bool unserialize(php_random_status *status, HashTable *data)
186 {
187 php_random_status_state_mt19937 *s = status->state;
188 zval *t;
189
190 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
191 if (zend_hash_num_elements(data) != (MT_N + 2)) {
192 return false;
193 }
194
195 for (uint32_t i = 0; i < MT_N; i++) {
196 t = zend_hash_index_find(data, i);
197 if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint32_t))) {
198 return false;
199 }
200 if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) {
201 return false;
202 }
203 }
204 t = zend_hash_index_find(data, MT_N);
205 if (!t || Z_TYPE_P(t) != IS_LONG) {
206 return false;
207 }
208 s->count = Z_LVAL_P(t);
209 if (s->count > MT_N) {
210 return false;
211 }
212
213 t = zend_hash_index_find(data, MT_N + 1);
214 if (!t || Z_TYPE_P(t) != IS_LONG) {
215 return false;
216 }
217 s->mode = Z_LVAL_P(t);
218 if (s->mode != MT_RAND_MT19937 && s->mode != MT_RAND_PHP) {
219 return false;
220 }
221
222 return true;
223 }
224
225 const php_random_algo php_random_algo_mt19937 = {
226 sizeof(uint32_t),
227 sizeof(php_random_status_state_mt19937),
228 seed,
229 generate,
230 range,
231 serialize,
232 unserialize
233 };
234
235 /* {{{ php_random_mt19937_seed_default */
php_random_mt19937_seed_default(php_random_status_state_mt19937 * state)236 PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *state)
237 {
238 zend_long seed = 0;
239
240 if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) {
241 seed = GENERATE_SEED();
242 }
243
244 mt19937_seed_state(state, (uint64_t) seed);
245 }
246 /* }}} */
247
248 /* {{{ Random\Engine\Mt19937::__construct() */
PHP_METHOD(Random_Engine_Mt19937,__construct)249 PHP_METHOD(Random_Engine_Mt19937, __construct)
250 {
251 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
252 php_random_status_state_mt19937 *state = engine->status->state;
253 zend_long seed, mode = MT_RAND_MT19937;
254 bool seed_is_null = true;
255
256 ZEND_PARSE_PARAMETERS_START(0, 2)
257 Z_PARAM_OPTIONAL;
258 Z_PARAM_LONG_OR_NULL(seed, seed_is_null);
259 Z_PARAM_LONG(mode);
260 ZEND_PARSE_PARAMETERS_END();
261
262 switch (mode) {
263 case MT_RAND_MT19937:
264 state->mode = MT_RAND_MT19937;
265 break;
266 case MT_RAND_PHP:
267 zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
268 state->mode = MT_RAND_PHP;
269 break;
270 default:
271 zend_argument_value_error(2, "must be either MT_RAND_MT19937 or MT_RAND_PHP");
272 RETURN_THROWS();
273 }
274
275 if (seed_is_null) {
276 /* MT19937 has a very large state, uses CSPRNG for seeding only */
277 if (php_random_bytes_throw(&seed, sizeof(zend_long)) == FAILURE) {
278 zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
279 RETURN_THROWS();
280 }
281 }
282
283 engine->algo->seed(engine->status, seed);
284 }
285 /* }}} */
286
287 /* {{{ Random\Engine\Mt19937::generate() */
PHP_METHOD(Random_Engine_Mt19937,generate)288 PHP_METHOD(Random_Engine_Mt19937, generate)
289 {
290 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
291 uint64_t generated;
292 size_t size;
293 zend_string *bytes;
294
295 ZEND_PARSE_PARAMETERS_NONE();
296
297 generated = engine->algo->generate(engine->status);
298 size = engine->status->last_generated_size;
299 if (EG(exception)) {
300 RETURN_THROWS();
301 }
302
303 bytes = zend_string_alloc(size, false);
304
305 /* Endianness safe copy */
306 for (size_t i = 0; i < size; i++) {
307 ZSTR_VAL(bytes)[i] = (generated >> (i * 8)) & 0xff;
308 }
309 ZSTR_VAL(bytes)[size] = '\0';
310
311 RETURN_STR(bytes);
312 }
313 /* }}} */
314
315 /* {{{ Random\Engine\Mt19937::__serialize() */
PHP_METHOD(Random_Engine_Mt19937,__serialize)316 PHP_METHOD(Random_Engine_Mt19937, __serialize)
317 {
318 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
319 zval t;
320
321 ZEND_PARSE_PARAMETERS_NONE();
322
323 array_init(return_value);
324
325 /* members */
326 ZVAL_ARR(&t, zend_std_get_properties(&engine->std));
327 Z_TRY_ADDREF(t);
328 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t);
329
330 /* state */
331 array_init(&t);
332 if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) {
333 zend_throw_exception(NULL, "Engine serialize failed", 0);
334 RETURN_THROWS();
335 }
336 zend_hash_next_index_insert(Z_ARRVAL_P(return_value), &t);
337 }
338 /* }}} */
339
340 /* {{{ Random\Engine\Mt19937::__unserialize() */
PHP_METHOD(Random_Engine_Mt19937,__unserialize)341 PHP_METHOD(Random_Engine_Mt19937, __unserialize)
342 {
343 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
344 HashTable *d;
345 zval *t;
346
347 ZEND_PARSE_PARAMETERS_START(1, 1)
348 Z_PARAM_ARRAY_HT(d);
349 ZEND_PARSE_PARAMETERS_END();
350
351 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
352 if (zend_hash_num_elements(d) != 2) {
353 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
354 RETURN_THROWS();
355 }
356
357 /* members */
358 t = zend_hash_index_find(d, 0);
359 if (!t || Z_TYPE_P(t) != IS_ARRAY) {
360 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
361 RETURN_THROWS();
362 }
363 object_properties_load(&engine->std, Z_ARRVAL_P(t));
364 if (EG(exception)) {
365 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
366 RETURN_THROWS();
367 }
368
369 /* state */
370 t = zend_hash_index_find(d, 1);
371 if (!t || Z_TYPE_P(t) != IS_ARRAY) {
372 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
373 RETURN_THROWS();
374 }
375 if (!engine->algo->unserialize(engine->status, Z_ARRVAL_P(t))) {
376 zend_throw_exception_ex(NULL, 0, "Invalid serialization data for %s object", ZSTR_VAL(engine->std.ce->name));
377 RETURN_THROWS();
378 }
379 }
380 /* }}} */
381
382 /* {{{ Random\Engine\Mt19937::__debugInfo() */
PHP_METHOD(Random_Engine_Mt19937,__debugInfo)383 PHP_METHOD(Random_Engine_Mt19937, __debugInfo)
384 {
385 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
386 zval t;
387
388 ZEND_PARSE_PARAMETERS_NONE();
389
390 if (!engine->std.properties) {
391 rebuild_object_properties(&engine->std);
392 }
393 ZVAL_ARR(return_value, zend_array_dup(engine->std.properties));
394
395 if (engine->algo->serialize) {
396 array_init(&t);
397 if (!engine->algo->serialize(engine->status, Z_ARRVAL(t))) {
398 zend_throw_exception(NULL, "Engine serialize failed", 0);
399 RETURN_THROWS();
400 }
401 zend_hash_str_add(Z_ARR_P(return_value), "__states", strlen("__states"), &t);
402 }
403 }
404 /* }}} */
405