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: Sammy Kaye Powers <me@sammyk.me> |
14 | Go Kudo <zeriyoshi@php.net> |
15 | Tim Düsterhus <timwolla@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include <stdlib.h>
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <math.h>
27
28 #include "php.h"
29
30 #include "Zend/zend_enum.h"
31 #include "Zend/zend_exceptions.h"
32
33 #include "php_random.h"
34 #include "php_random_csprng.h"
35 #include "ext/standard/sha1.h"
36
37 #ifdef HAVE_UNISTD_H
38 # include <unistd.h>
39 #endif
40
41 #ifdef PHP_WIN32
42 # include "win32/time.h"
43 # include "win32/winutil.h"
44 # include <process.h>
45 #else
46 # include <sys/time.h>
47 #endif
48
49 #ifdef HAVE_SYS_PARAM_H
50 # include <sys/param.h>
51 #endif
52
53 #include "random_arginfo.h"
54
55 PHPAPI ZEND_DECLARE_MODULE_GLOBALS(random)
56
57 PHPAPI zend_class_entry *random_ce_Random_Engine;
58 PHPAPI zend_class_entry *random_ce_Random_CryptoSafeEngine;
59
60 PHPAPI zend_class_entry *random_ce_Random_Engine_Mt19937;
61 PHPAPI zend_class_entry *random_ce_Random_Engine_PcgOneseq128XslRr64;
62 PHPAPI zend_class_entry *random_ce_Random_Engine_Xoshiro256StarStar;
63 PHPAPI zend_class_entry *random_ce_Random_Engine_Secure;
64
65 PHPAPI zend_class_entry *random_ce_Random_Randomizer;
66
67 PHPAPI zend_class_entry *random_ce_Random_IntervalBoundary;
68
69 PHPAPI zend_class_entry *random_ce_Random_RandomError;
70 PHPAPI zend_class_entry *random_ce_Random_BrokenRandomEngineError;
71 PHPAPI zend_class_entry *random_ce_Random_RandomException;
72
73 static zend_object_handlers random_engine_mt19937_object_handlers;
74 static zend_object_handlers random_engine_pcgoneseq128xslrr64_object_handlers;
75 static zend_object_handlers random_engine_xoshiro256starstar_object_handlers;
76 static zend_object_handlers random_engine_secure_object_handlers;
77 static zend_object_handlers random_randomizer_object_handlers;
78
php_random_range32(php_random_algo_with_state engine,uint32_t umax)79 PHPAPI uint32_t php_random_range32(php_random_algo_with_state engine, uint32_t umax)
80 {
81 const php_random_algo *algo = engine.algo;
82 void *state = engine.state;
83
84 uint32_t result;
85 size_t total_size;
86
87 result = 0;
88 total_size = 0;
89 do {
90 php_random_result r = algo->generate(state);
91 result = result | (((uint32_t) r.result) << (total_size * 8));
92 total_size += r.size;
93 if (EG(exception)) {
94 return 0;
95 }
96 } while (total_size < sizeof(uint32_t));
97
98 /* Special case where no modulus is required */
99 if (UNEXPECTED(umax == UINT32_MAX)) {
100 return result;
101 }
102
103 /* Increment the max so range is inclusive of max */
104 umax++;
105
106 /* Powers of two are not biased */
107 if ((umax & (umax - 1)) == 0) {
108 return result & (umax - 1);
109 }
110
111 /* Ceiling under which UINT32_MAX % max == 0 */
112 uint32_t limit = UINT32_MAX - (UINT32_MAX % umax) - 1;
113
114 /* Discard numbers over the limit to avoid modulo bias */
115 uint32_t count = 0;
116 while (UNEXPECTED(result > limit)) {
117 /* If the requirements cannot be met in a cycles, return fail */
118 if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
119 zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
120 return 0;
121 }
122
123 result = 0;
124 total_size = 0;
125 do {
126 php_random_result r = algo->generate(state);
127 result = result | (((uint32_t) r.result) << (total_size * 8));
128 total_size += r.size;
129 if (EG(exception)) {
130 return 0;
131 }
132 } while (total_size < sizeof(uint32_t));
133 }
134
135 return result % umax;
136 }
137
php_random_range64(php_random_algo_with_state engine,uint64_t umax)138 PHPAPI uint64_t php_random_range64(php_random_algo_with_state engine, uint64_t umax)
139 {
140 const php_random_algo *algo = engine.algo;
141 void *state = engine.state;
142
143 uint64_t result;
144 size_t total_size;
145
146 result = 0;
147 total_size = 0;
148 do {
149 php_random_result r = algo->generate(state);
150 result = result | (r.result << (total_size * 8));
151 total_size += r.size;
152 if (EG(exception)) {
153 return 0;
154 }
155 } while (total_size < sizeof(uint64_t));
156
157 /* Special case where no modulus is required */
158 if (UNEXPECTED(umax == UINT64_MAX)) {
159 return result;
160 }
161
162 /* Increment the max so range is inclusive of max */
163 umax++;
164
165 /* Powers of two are not biased */
166 if ((umax & (umax - 1)) == 0) {
167 return result & (umax - 1);
168 }
169
170 /* Ceiling under which UINT64_MAX % max == 0 */
171 uint64_t limit = UINT64_MAX - (UINT64_MAX % umax) - 1;
172
173 /* Discard numbers over the limit to avoid modulo bias */
174 uint32_t count = 0;
175 while (UNEXPECTED(result > limit)) {
176 /* If the requirements cannot be met in a cycles, return fail */
177 if (++count > PHP_RANDOM_RANGE_ATTEMPTS) {
178 zend_throw_error(random_ce_Random_BrokenRandomEngineError, "Failed to generate an acceptable random number in %d attempts", PHP_RANDOM_RANGE_ATTEMPTS);
179 return 0;
180 }
181
182 result = 0;
183 total_size = 0;
184 do {
185 php_random_result r = algo->generate(state);
186 result = result | (r.result << (total_size * 8));
187 total_size += r.size;
188 if (EG(exception)) {
189 return 0;
190 }
191 } while (total_size < sizeof(uint64_t));
192 }
193
194 return result % umax;
195 }
196
php_random_engine_mt19937_new(zend_class_entry * ce)197 static zend_object *php_random_engine_mt19937_new(zend_class_entry *ce)
198 {
199 return &php_random_engine_common_init(ce, &random_engine_mt19937_object_handlers, &php_random_algo_mt19937)->std;
200 }
201
php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry * ce)202 static zend_object *php_random_engine_pcgoneseq128xslrr64_new(zend_class_entry *ce)
203 {
204 return &php_random_engine_common_init(ce, &random_engine_pcgoneseq128xslrr64_object_handlers, &php_random_algo_pcgoneseq128xslrr64)->std;
205 }
206
php_random_engine_xoshiro256starstar_new(zend_class_entry * ce)207 static zend_object *php_random_engine_xoshiro256starstar_new(zend_class_entry *ce)
208 {
209 return &php_random_engine_common_init(ce, &random_engine_xoshiro256starstar_object_handlers, &php_random_algo_xoshiro256starstar)->std;
210 }
211
php_random_engine_secure_new(zend_class_entry * ce)212 static zend_object *php_random_engine_secure_new(zend_class_entry *ce)
213 {
214 return &php_random_engine_common_init(ce, &random_engine_secure_object_handlers, &php_random_algo_secure)->std;
215 }
216
php_random_randomizer_new(zend_class_entry * ce)217 static zend_object *php_random_randomizer_new(zend_class_entry *ce)
218 {
219 php_random_randomizer *randomizer = zend_object_alloc(sizeof(php_random_randomizer), ce);
220
221 zend_object_std_init(&randomizer->std, ce);
222 object_properties_init(&randomizer->std, ce);
223
224 return &randomizer->std;
225 }
226
randomizer_free_obj(zend_object * object)227 static void randomizer_free_obj(zend_object *object) {
228 php_random_randomizer *randomizer = php_random_randomizer_from_obj(object);
229
230 if (randomizer->is_userland_algo) {
231 php_random_status_free(randomizer->engine.state, false);
232 }
233
234 zend_object_std_dtor(&randomizer->std);
235 }
236
php_random_status_alloc(const php_random_algo * algo,const bool persistent)237 PHPAPI void *php_random_status_alloc(const php_random_algo *algo, const bool persistent)
238 {
239 return algo->state_size > 0 ? pecalloc(1, algo->state_size, persistent) : NULL;
240 }
241
php_random_status_copy(const php_random_algo * algo,void * old_status,void * new_status)242 PHPAPI void *php_random_status_copy(const php_random_algo *algo, void *old_status, void *new_status)
243 {
244 return memcpy(new_status, old_status, algo->state_size);
245 }
246
php_random_status_free(void * status,const bool persistent)247 PHPAPI void php_random_status_free(void *status, const bool persistent)
248 {
249 pefree(status, persistent);
250 }
251
php_random_engine_common_init(zend_class_entry * ce,zend_object_handlers * handlers,const php_random_algo * algo)252 PHPAPI php_random_engine *php_random_engine_common_init(zend_class_entry *ce, zend_object_handlers *handlers, const php_random_algo *algo)
253 {
254 php_random_engine *engine = zend_object_alloc(sizeof(php_random_engine), ce);
255
256 zend_object_std_init(&engine->std, ce);
257 object_properties_init(&engine->std, ce);
258
259 engine->engine = (php_random_algo_with_state){
260 .algo = algo,
261 .state = php_random_status_alloc(algo, false)
262 };
263 engine->std.handlers = handlers;
264
265 return engine;
266 }
267
php_random_engine_common_free_object(zend_object * object)268 PHPAPI void php_random_engine_common_free_object(zend_object *object)
269 {
270 php_random_engine *engine = php_random_engine_from_obj(object);
271
272 php_random_status_free(engine->engine.state, false);
273 zend_object_std_dtor(object);
274 }
275
php_random_engine_common_clone_object(zend_object * object)276 PHPAPI zend_object *php_random_engine_common_clone_object(zend_object *object)
277 {
278 php_random_engine *old_engine = php_random_engine_from_obj(object);
279 php_random_engine *new_engine = php_random_engine_from_obj(old_engine->std.ce->create_object(old_engine->std.ce));
280
281 new_engine->engine.algo = old_engine->engine.algo;
282 if (old_engine->engine.state) {
283 new_engine->engine.state = php_random_status_copy(old_engine->engine.algo, old_engine->engine.state, new_engine->engine.state);
284 }
285
286 zend_objects_clone_members(&new_engine->std, &old_engine->std);
287
288 return &new_engine->std;
289 }
290
291 /* {{{ php_random_range */
php_random_range(php_random_algo_with_state engine,zend_long min,zend_long max)292 PHPAPI zend_long php_random_range(php_random_algo_with_state engine, zend_long min, zend_long max)
293 {
294 zend_ulong umax = (zend_ulong) max - (zend_ulong) min;
295
296 if (umax > UINT32_MAX) {
297 return (zend_long) (php_random_range64(engine, umax) + min);
298 }
299
300 return (zend_long) (php_random_range32(engine, umax) + min);
301 }
302 /* }}} */
303
304 /* {{{ php_random_default_algo */
php_random_default_algo(void)305 PHPAPI const php_random_algo *php_random_default_algo(void)
306 {
307 return &php_random_algo_mt19937;
308 }
309 /* }}} */
310
311 /* {{{ php_random_default_status */
php_random_default_status(void)312 PHPAPI void *php_random_default_status(void)
313 {
314 php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
315
316 if (!RANDOM_G(mt19937_seeded)) {
317 state->mode = MT_RAND_MT19937;
318 php_random_mt19937_seed_default(state);
319 RANDOM_G(mt19937_seeded) = true;
320 }
321
322 return state;
323 }
324 /* }}} */
325
326 /* this is read-only, so it's ok */
327 ZEND_SET_ALIGNED(16, static const char hexconvtab[]) = "0123456789abcdef";
328
329 /* {{{ php_random_bin2hex_le */
330 /* stolen from standard/string.c */
php_random_bin2hex_le(const void * ptr,const size_t len)331 PHPAPI zend_string *php_random_bin2hex_le(const void *ptr, const size_t len)
332 {
333 zend_string *str;
334 size_t i;
335
336 str = zend_string_safe_alloc(len, 2 * sizeof(char), 0, 0);
337
338 i = 0;
339 #ifdef WORDS_BIGENDIAN
340 /* force little endian */
341 for (size_t h = len; 0 < h; h--) {
342 size_t j = h-1;
343 #else
344 for (size_t j = 0; j < len; j++) {
345 #endif
346 ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] >> 4];
347 ZSTR_VAL(str)[i++] = hexconvtab[((unsigned char *) ptr)[j] & 15];
348 }
349 ZSTR_VAL(str)[i] = '\0';
350
351 return str;
352 }
353 /* }}} */
354
355 /* {{{ php_random_hex2bin_le */
356 /* stolen from standard/string.c */
357 PHPAPI bool php_random_hex2bin_le(zend_string *hexstr, void *dest)
358 {
359 size_t len = hexstr->len >> 1;
360 unsigned char *str = (unsigned char *) hexstr->val, c, l, d;
361 unsigned char *ptr = (unsigned char *) dest;
362 int is_letter, i = 0;
363
364 #ifdef WORDS_BIGENDIAN
365 /* force little endian */
366 for (size_t h = len; 0 < h; h--) {
367 size_t j = h-1;
368 #else
369 for (size_t j = 0; j < len; j++) {
370 #endif
371 c = str[i++];
372 l = c & ~0x20;
373 is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
374
375 /* basically (c >= '0' && c <= '9') || (l >= 'A' && l <= 'F') */
376 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
377 d = (l - 0x10 - 0x27 * is_letter) << 4;
378 } else {
379 return false;
380 }
381 c = str[i++];
382 l = c & ~0x20;
383 is_letter = ((uint32_t) ((l - 'A') ^ (l - 'F' - 1))) >> (8 * sizeof(uint32_t) - 1);
384 if (EXPECTED((((c ^ '0') - 10) >> (8 * sizeof(uint32_t) - 1)) | is_letter)) {
385 d |= l - 0x10 - 0x27 * is_letter;
386 } else {
387 return false;
388 }
389 ptr[j] = d;
390 }
391 return true;
392 }
393 /* }}} */
394
395 /* {{{ php_combined_lcg */
396 PHPAPI double php_combined_lcg(void)
397 {
398 php_random_status_state_combinedlcg *state = &RANDOM_G(combined_lcg);
399
400 if (!RANDOM_G(combined_lcg_seeded)) {
401 php_random_combinedlcg_seed_default(state);
402 RANDOM_G(combined_lcg_seeded) = true;
403 }
404
405 return php_random_algo_combinedlcg.generate(state).result * 4.656613e-10;
406 }
407 /* }}} */
408
409 /* {{{ php_mt_srand */
410 PHPAPI void php_mt_srand(uint32_t seed)
411 {
412 php_random_mt19937_seed32(php_random_default_status(), seed);
413 }
414 /* }}} */
415
416 /* {{{ php_mt_rand */
417 PHPAPI uint32_t php_mt_rand(void)
418 {
419 return (uint32_t) php_random_algo_mt19937.generate(php_random_default_status()).result;
420 }
421 /* }}} */
422
423 /* {{{ php_mt_rand_range */
424 PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max)
425 {
426 return php_random_algo_mt19937.range(php_random_default_status(), min, max);
427 }
428 /* }}} */
429
430 /* {{{ php_mt_rand_common
431 * rand() allows min > max, mt_rand does not */
432 PHPAPI zend_long php_mt_rand_common(zend_long min, zend_long max)
433 {
434 php_random_status_state_mt19937 *s = php_random_default_status();
435
436 if (s->mode == MT_RAND_MT19937) {
437 return php_mt_rand_range(min, max);
438 }
439
440 uint64_t r = php_random_algo_mt19937.generate(php_random_default_status()).result >> 1;
441
442 /* This is an inlined version of the RAND_RANGE_BADSCALING macro that does not invoke UB when encountering
443 * (max - min) > ZEND_LONG_MAX.
444 */
445 zend_ulong offset = (double) ( (double) max - min + 1.0) * (r / (PHP_MT_RAND_MAX + 1.0));
446
447 return (zend_long) (offset + min);
448 }
449 /* }}} */
450
451 /* {{{ Returns a value from the combined linear congruential generator */
452 PHP_FUNCTION(lcg_value)
453 {
454 ZEND_PARSE_PARAMETERS_NONE();
455
456 RETURN_DOUBLE(php_combined_lcg());
457 }
458 /* }}} */
459
460 /* {{{ Seeds Mersenne Twister random number generator */
461 PHP_FUNCTION(mt_srand)
462 {
463 zend_long seed = 0;
464 bool seed_is_null = true;
465 zend_long mode = MT_RAND_MT19937;
466 php_random_status_state_mt19937 *state = &RANDOM_G(mt19937);
467
468 ZEND_PARSE_PARAMETERS_START(0, 2)
469 Z_PARAM_OPTIONAL
470 Z_PARAM_LONG_OR_NULL(seed, seed_is_null)
471 Z_PARAM_LONG(mode)
472 ZEND_PARSE_PARAMETERS_END();
473
474 switch (mode) {
475 case MT_RAND_PHP:
476 state->mode = MT_RAND_PHP;
477 zend_error(E_DEPRECATED, "The MT_RAND_PHP variant of Mt19937 is deprecated");
478 break;
479 default:
480 state->mode = MT_RAND_MT19937;
481 }
482
483 if (seed_is_null) {
484 php_random_mt19937_seed_default(state);
485 } else {
486 php_random_mt19937_seed32(state, (uint64_t) seed);
487 }
488 RANDOM_G(mt19937_seeded) = true;
489 }
490 /* }}} */
491
492 /* {{{ Returns a random number from Mersenne Twister */
493 PHP_FUNCTION(mt_rand)
494 {
495 zend_long min, max;
496 int argc = ZEND_NUM_ARGS();
497
498 if (argc == 0) {
499 /* genrand_int31 in mt19937ar.c performs a right shift */
500 RETURN_LONG(php_mt_rand() >> 1);
501 }
502
503 ZEND_PARSE_PARAMETERS_START(2, 2)
504 Z_PARAM_LONG(min)
505 Z_PARAM_LONG(max)
506 ZEND_PARSE_PARAMETERS_END();
507
508 if (UNEXPECTED(max < min)) {
509 zend_argument_value_error(2, "must be greater than or equal to argument #1 ($min)");
510 RETURN_THROWS();
511 }
512
513 RETURN_LONG(php_mt_rand_common(min, max));
514 }
515 /* }}} */
516
517 /* {{{ Returns the maximum value a random number from Mersenne Twister can have */
518 PHP_FUNCTION(mt_getrandmax)
519 {
520 ZEND_PARSE_PARAMETERS_NONE();
521
522 /*
523 * Melo: it could be 2^^32, but we only use 2^^31 to maintain
524 * compatibility with the previous php_rand
525 */
526 RETURN_LONG(PHP_MT_RAND_MAX); /* 2^^31 */
527 }
528 /* }}} */
529
530 /* {{{ Returns a random number from Mersenne Twister */
531 PHP_FUNCTION(rand)
532 {
533 zend_long min, max;
534 int argc = ZEND_NUM_ARGS();
535
536 if (argc == 0) {
537 /* genrand_int31 in mt19937ar.c performs a right shift */
538 RETURN_LONG(php_mt_rand() >> 1);
539 }
540
541 ZEND_PARSE_PARAMETERS_START(2, 2)
542 Z_PARAM_LONG(min)
543 Z_PARAM_LONG(max)
544 ZEND_PARSE_PARAMETERS_END();
545
546 if (max < min) {
547 RETURN_LONG(php_mt_rand_common(max, min));
548 }
549
550 RETURN_LONG(php_mt_rand_common(min, max));
551 }
552 /* }}} */
553
554 /* {{{ Return an arbitrary length of pseudo-random bytes as binary string */
555 PHP_FUNCTION(random_bytes)
556 {
557 zend_long size;
558 zend_string *bytes;
559
560 ZEND_PARSE_PARAMETERS_START(1, 1)
561 Z_PARAM_LONG(size)
562 ZEND_PARSE_PARAMETERS_END();
563
564 if (size < 1) {
565 zend_argument_value_error(1, "must be greater than 0");
566 RETURN_THROWS();
567 }
568
569 bytes = zend_string_alloc(size, 0);
570
571 if (php_random_bytes_throw(ZSTR_VAL(bytes), size) == FAILURE) {
572 zend_string_release_ex(bytes, 0);
573 RETURN_THROWS();
574 }
575
576 ZSTR_VAL(bytes)[size] = '\0';
577
578 RETURN_STR(bytes);
579 }
580 /* }}} */
581
582 /* {{{ Return an arbitrary pseudo-random integer */
583 PHP_FUNCTION(random_int)
584 {
585 zend_long min, max, result;
586
587 ZEND_PARSE_PARAMETERS_START(2, 2)
588 Z_PARAM_LONG(min)
589 Z_PARAM_LONG(max)
590 ZEND_PARSE_PARAMETERS_END();
591
592 if (min > max) {
593 zend_argument_value_error(1, "must be less than or equal to argument #2 ($max)");
594 RETURN_THROWS();
595 }
596
597 if (php_random_int_throw(min, max, &result) == FAILURE) {
598 RETURN_THROWS();
599 }
600
601 RETURN_LONG(result);
602 }
603 /* }}} */
604
605 static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
606 /* Wrapper around PHP_SHA1Update allowing to pass
607 * arbitrary pointers without (unsigned char*) casts
608 * everywhere.
609 */
610 PHP_SHA1Update(c, p, l);
611 }
612
613 PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
614 {
615 /* Mix various values using SHA-1 as a PRF to obtain as
616 * much entropy as possible, hopefully generating an
617 * unpredictable and independent uint64_t. Nevertheless,
618 * the output of this function MUST NOT be treated as
619 * being cryptographically safe.
620 */
621 PHP_SHA1_CTX c;
622 struct timeval tv;
623 void *pointer;
624 pid_t pid;
625 #ifdef ZTS
626 THREAD_T tid;
627 #endif
628 char buf[64 + 1];
629
630 PHP_SHA1Init(&c);
631 if (!state->initialized) {
632 /* Current time. */
633 gettimeofday(&tv, NULL);
634 fallback_seed_add(&c, &tv, sizeof(tv));
635 /* Various PIDs. */
636 pid = getpid();
637 fallback_seed_add(&c, &pid, sizeof(pid));
638 #ifndef PHP_WIN32
639 pid = getppid();
640 fallback_seed_add(&c, &pid, sizeof(pid));
641 #endif
642 #ifdef ZTS
643 tid = tsrm_thread_id();
644 fallback_seed_add(&c, &tid, sizeof(tid));
645 #endif
646 /* Pointer values to benefit from ASLR. */
647 pointer = &state;
648 fallback_seed_add(&c, &pointer, sizeof(pointer));
649 pointer = &c;
650 fallback_seed_add(&c, &pointer, sizeof(pointer));
651 /* Updated time. */
652 gettimeofday(&tv, NULL);
653 fallback_seed_add(&c, &tv, sizeof(tv));
654 /* Hostname. */
655 memset(buf, 0, sizeof(buf));
656 if (gethostname(buf, sizeof(buf) - 1) == 0) {
657 fallback_seed_add(&c, buf, strlen(buf));
658 }
659 /* CSPRNG. */
660 if (php_random_bytes_silent(buf, 16) == SUCCESS) {
661 fallback_seed_add(&c, buf, 16);
662 }
663 /* Updated time. */
664 gettimeofday(&tv, NULL);
665 fallback_seed_add(&c, &tv, sizeof(tv));
666 } else {
667 /* Current time. */
668 gettimeofday(&tv, NULL);
669 fallback_seed_add(&c, &tv, sizeof(tv));
670 /* Previous state. */
671 fallback_seed_add(&c, state->seed, 20);
672 }
673 PHP_SHA1Final(state->seed, &c);
674 state->initialized = true;
675
676 uint64_t result = 0;
677
678 for (size_t i = 0; i < sizeof(result); i++) {
679 result = result | (((uint64_t)state->seed[i]) << (i * 8));
680 }
681
682 return result;
683 }
684
685 PHPAPI uint64_t php_random_generate_fallback_seed(void)
686 {
687 return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
688 }
689
690 /* {{{ PHP_GINIT_FUNCTION */
691 static PHP_GINIT_FUNCTION(random)
692 {
693 random_globals->fallback_seed_state.initialized = false;
694 }
695 /* }}} */
696
697 /* {{{ PHP_MINIT_FUNCTION */
698 PHP_MINIT_FUNCTION(random)
699 {
700 /* Random\Engine */
701 random_ce_Random_Engine = register_class_Random_Engine();
702
703 /* Random\CryptoSafeEngine */
704 random_ce_Random_CryptoSafeEngine = register_class_Random_CryptoSafeEngine(random_ce_Random_Engine);
705
706 /* Random\RandomError */
707 random_ce_Random_RandomError = register_class_Random_RandomError(zend_ce_error);
708
709 /* Random\BrokenRandomEngineError */
710 random_ce_Random_BrokenRandomEngineError = register_class_Random_BrokenRandomEngineError(random_ce_Random_RandomError);
711
712 /* Random\RandomException */
713 random_ce_Random_RandomException = register_class_Random_RandomException(zend_ce_exception);
714
715 /* Random\Engine\Mt19937 */
716 random_ce_Random_Engine_Mt19937 = register_class_Random_Engine_Mt19937(random_ce_Random_Engine);
717 random_ce_Random_Engine_Mt19937->create_object = php_random_engine_mt19937_new;
718 memcpy(&random_engine_mt19937_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
719 random_engine_mt19937_object_handlers.offset = XtOffsetOf(php_random_engine, std);
720 random_engine_mt19937_object_handlers.free_obj = php_random_engine_common_free_object;
721 random_engine_mt19937_object_handlers.clone_obj = php_random_engine_common_clone_object;
722
723 /* Random\Engine\PcgOnseq128XslRr64 */
724 random_ce_Random_Engine_PcgOneseq128XslRr64 = register_class_Random_Engine_PcgOneseq128XslRr64(random_ce_Random_Engine);
725 random_ce_Random_Engine_PcgOneseq128XslRr64->create_object = php_random_engine_pcgoneseq128xslrr64_new;
726 memcpy(&random_engine_pcgoneseq128xslrr64_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
727 random_engine_pcgoneseq128xslrr64_object_handlers.offset = XtOffsetOf(php_random_engine, std);
728 random_engine_pcgoneseq128xslrr64_object_handlers.free_obj = php_random_engine_common_free_object;
729 random_engine_pcgoneseq128xslrr64_object_handlers.clone_obj = php_random_engine_common_clone_object;
730
731 /* Random\Engine\Xoshiro256StarStar */
732 random_ce_Random_Engine_Xoshiro256StarStar = register_class_Random_Engine_Xoshiro256StarStar(random_ce_Random_Engine);
733 random_ce_Random_Engine_Xoshiro256StarStar->create_object = php_random_engine_xoshiro256starstar_new;
734 memcpy(&random_engine_xoshiro256starstar_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
735 random_engine_xoshiro256starstar_object_handlers.offset = XtOffsetOf(php_random_engine, std);
736 random_engine_xoshiro256starstar_object_handlers.free_obj = php_random_engine_common_free_object;
737 random_engine_xoshiro256starstar_object_handlers.clone_obj = php_random_engine_common_clone_object;
738
739 /* Random\Engine\Secure */
740 random_ce_Random_Engine_Secure = register_class_Random_Engine_Secure(random_ce_Random_CryptoSafeEngine);
741 random_ce_Random_Engine_Secure->create_object = php_random_engine_secure_new;
742 memcpy(&random_engine_secure_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
743 random_engine_secure_object_handlers.offset = XtOffsetOf(php_random_engine, std);
744 random_engine_secure_object_handlers.free_obj = php_random_engine_common_free_object;
745 random_engine_secure_object_handlers.clone_obj = NULL;
746
747 /* Random\Randomizer */
748 random_ce_Random_Randomizer = register_class_Random_Randomizer();
749 random_ce_Random_Randomizer->create_object = php_random_randomizer_new;
750 random_ce_Random_Randomizer->default_object_handlers = &random_randomizer_object_handlers;
751 memcpy(&random_randomizer_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
752 random_randomizer_object_handlers.offset = XtOffsetOf(php_random_randomizer, std);
753 random_randomizer_object_handlers.free_obj = randomizer_free_obj;
754 random_randomizer_object_handlers.clone_obj = NULL;
755
756 /* Random\IntervalBoundary */
757 random_ce_Random_IntervalBoundary = register_class_Random_IntervalBoundary();
758
759 register_random_symbols(module_number);
760
761 return SUCCESS;
762 }
763 /* }}} */
764
765 /* {{{ PHP_MSHUTDOWN_FUNCTION */
766 PHP_MSHUTDOWN_FUNCTION(random)
767 {
768 php_random_csprng_shutdown();
769
770 return SUCCESS;
771 }
772 /* }}} */
773
774 /* {{{ PHP_RINIT_FUNCTION */
775 PHP_RINIT_FUNCTION(random)
776 {
777 RANDOM_G(combined_lcg_seeded) = false;
778 RANDOM_G(mt19937_seeded) = false;
779
780 return SUCCESS;
781 }
782 /* }}} */
783
784 /* {{{ random_module_entry */
785 zend_module_entry random_module_entry = {
786 STANDARD_MODULE_HEADER,
787 "random", /* Extension name */
788 ext_functions, /* zend_function_entry */
789 PHP_MINIT(random), /* PHP_MINIT - Module initialization */
790 PHP_MSHUTDOWN(random), /* PHP_MSHUTDOWN - Module shutdown */
791 PHP_RINIT(random), /* PHP_RINIT - Request initialization */
792 NULL, /* PHP_RSHUTDOWN - Request shutdown */
793 NULL, /* PHP_MINFO - Module info */
794 PHP_VERSION, /* Version */
795 PHP_MODULE_GLOBALS(random), /* ZTS Module globals */
796 PHP_GINIT(random), /* PHP_GINIT - Global initialization */
797 NULL, /* PHP_GSHUTDOWN - Global shutdown */
798 NULL, /* Post deactivate */
799 STANDARD_MODULE_PROPERTIES_EX
800 };
801 /* }}} */
802