Skip to content

Commit 81744d6

Browse files
authored
random: Improve the output quality of RANDOM_SEED() (#13730)
* random: Improve the output quality of RANDOM_SEED() Previously 4 consecutive calls to `RANDOM_SEED()` each for 4 different CLI requests resulted in: $ sapi/cli/php test.php 2c13e9fde9caa 2c13e9fd1d6b0 2c13e9fd4de34 2c13e9fd1610e $ sapi/cli/php test.php 2c1436764fe07 2c14367621770 2c143676c0bf6 2c143676e02f5 $ sapi/cli/php test.php 2c144995a0626 2c14499590fe2 2c144995c65db 2c14499536833 $ sapi/cli/php test.php 2c145cb30860b 2c145cb3ec027 2c145cb33b4ca 2c145cb38ff63 Now they result in: $ sapi/cli/php test.php 6796973ace1b5f3d 1913daf5c158cb4b 255dbf24237bc8c9 7c3ba22e60f35196 $ sapi/cli/php test.php afb7cc9ba9819cd2 3e01a71b91ad020c 6b718364d3ef108 bdcd17beeb4b31d2 $ sapi/cli/php test.php 53d36eb9b83f8788 4381c85e816187aa 2e9b32ee9898e71e 31d15c946842bddb $ sapi/cli/php test.php 2037a3cba88114b4 ba0b0d93a9bb43aa e13d82d2421269e2 191de474f3292240 * tree-wide: Replace GENERATE_SEED() by php_random_generate_fallback_seed() * random: Fix NTS build * random: Fix Windows build
1 parent 4f7a3d9 commit 81744d6

File tree

6 files changed

+111
-14
lines changed

6 files changed

+111
-14
lines changed

UPGRADING.INTERNALS

+5
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ PHP 8.4 INTERNALS UPGRADE NOTES
174174
- The CSPRNG API (php_random_(bytes|int)_*) is now provided by the new
175175
and much smaller php_random_csprng.h header. The new header is included
176176
in php_random.h for compatibility with existing users.
177+
- A new php_random_generate_fallback_seed() function has been added as a
178+
replacement for the generically named GENERATE_SEED(). The internal
179+
implementation has been improved to generate better seeds, however any
180+
users should use the opportunity to verify that seeding is first
181+
attempted using the CSPRNG for better output size flexibility.
177182

178183
c. ext/xsl
179184
- The function php_xsl_create_object() was removed as it was not used

ext/gmp/gmp.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1734,9 +1734,9 @@ static void gmp_init_random(void)
17341734
/* Initialize */
17351735
gmp_randinit_mt(GMPG(rand_state));
17361736
/* Seed */
1737-
zend_long seed = 0;
1738-
if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) {
1739-
seed = GENERATE_SEED();
1737+
unsigned long int seed = 0;
1738+
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
1739+
seed = (unsigned long int)php_random_generate_fallback_seed();
17401740
}
17411741
gmp_randseed_ui(GMPG(rand_state), seed);
17421742

ext/random/engine_mt19937.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *sta
242242
uint32_t seed = 0;
243243

244244
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
245-
seed = GENERATE_SEED();
245+
seed = (uint32_t)php_random_generate_fallback_seed();
246246
}
247247

248248
php_random_mt19937_seed32(state, seed);

ext/random/php_random.h

+5-9
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,11 @@
3737

3838
PHPAPI double php_combined_lcg(void);
3939

40+
PHPAPI uint64_t php_random_generate_fallback_seed(void);
41+
4042
static inline zend_long GENERATE_SEED(void)
4143
{
42-
zend_ulong pid;
43-
44-
# ifdef PHP_WIN32
45-
pid = (zend_ulong) GetCurrentProcessId();
46-
# else
47-
pid = (zend_ulong) getpid();
48-
# endif
49-
50-
return (((zend_long) ((zend_ulong) time(NULL) * pid)) ^ ((zend_long) (1000000.0 * php_combined_lcg())));
44+
return (zend_long)php_random_generate_fallback_seed();
5145
}
5246

5347
# define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
@@ -213,6 +207,8 @@ ZEND_BEGIN_MODULE_GLOBALS(random)
213207
int random_fd;
214208
bool combined_lcg_seeded;
215209
bool mt19937_seeded;
210+
bool fallback_seed_initialized;
211+
unsigned char fallback_seed[20];
216212
php_random_status_state_combinedlcg combined_lcg;
217213
php_random_status_state_mt19937 mt19937;
218214
ZEND_END_MODULE_GLOBALS(random)

ext/random/random.c

+93
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
+----------------------------------------------------------------------+
1313
| Authors: Sammy Kaye Powers <[email protected]> |
1414
| Go Kudo <[email protected]> |
15+
| Tim Düsterhus <[email protected]> |
1516
+----------------------------------------------------------------------+
1617
*/
1718

@@ -31,6 +32,7 @@
3132

3233
#include "php_random.h"
3334
#include "php_random_csprng.h"
35+
#include "ext/standard/sha1.h"
3436

3537
#if HAVE_UNISTD_H
3638
# include <unistd.h>
@@ -612,10 +614,101 @@ PHP_FUNCTION(random_int)
612614
}
613615
/* }}} */
614616

617+
static void write_32(PHP_SHA1_CTX *c, uint32_t u)
618+
{
619+
unsigned char buf[4];
620+
unsigned char *p = buf;
621+
*(p++) = (u >> 0) & 0xff;
622+
*(p++) = (u >> 8) & 0xff;
623+
*(p++) = (u >> 16) & 0xff;
624+
*(p++) = (u >> 24) & 0xff;
625+
PHP_SHA1Update(c, buf, sizeof(buf));
626+
}
627+
628+
static void write_64(PHP_SHA1_CTX *c, uint64_t u)
629+
{
630+
write_32(c, u);
631+
write_32(c, u >> 32);
632+
}
633+
634+
static void write_p(PHP_SHA1_CTX *c, uintptr_t p)
635+
{
636+
if (sizeof(p) == 4) {
637+
write_32(c, p);
638+
} else {
639+
write_64(c, p);
640+
}
641+
}
642+
643+
uint64_t php_random_generate_fallback_seed(void)
644+
{
645+
/* Mix various values using SHA-1 as a PRF to obtain as
646+
* much entropy as possible, hopefully generating an
647+
* unpredictable and independent uint64_t. Nevertheless
648+
* the output of this function MUST NOT be treated as
649+
* being cryptographically safe.
650+
*/
651+
PHP_SHA1_CTX c;
652+
struct timeval tv;
653+
char buf[64 + 1];
654+
655+
PHP_SHA1Init(&c);
656+
if (!RANDOM_G(fallback_seed_initialized)) {
657+
/* Current time. */
658+
gettimeofday(&tv, NULL);
659+
write_32(&c, tv.tv_sec);
660+
write_32(&c, tv.tv_usec);
661+
/* Various PIDs. */
662+
write_32(&c, getpid());
663+
#ifndef WIN32
664+
write_32(&c, getppid());
665+
#endif
666+
#ifdef ZTS
667+
write_32(&c, tsrm_thread_id());
668+
#endif
669+
/* Pointer values to benefit from ASLR. */
670+
write_p(&c, (uintptr_t)&RANDOM_G(fallback_seed_initialized));
671+
write_p(&c, (uintptr_t)&c);
672+
/* Updated time. */
673+
gettimeofday(&tv, NULL);
674+
write_32(&c, tv.tv_usec);
675+
/* Hostname. */
676+
memset(buf, 0, sizeof(buf));
677+
if (gethostname(buf, sizeof(buf) - 1) == 0) {
678+
PHP_SHA1Update(&c, (unsigned char*)buf, strlen(buf));
679+
}
680+
/* CSPRNG. */
681+
if (php_random_bytes_silent(buf, 16) == SUCCESS) {
682+
PHP_SHA1Update(&c, (unsigned char*)buf, 16);
683+
}
684+
/* Updated time. */
685+
gettimeofday(&tv, NULL);
686+
write_32(&c, tv.tv_usec);
687+
} else {
688+
/* Current time. */
689+
gettimeofday(&tv, NULL);
690+
write_32(&c, tv.tv_sec);
691+
write_32(&c, tv.tv_usec);
692+
/* Previous state. */
693+
PHP_SHA1Update(&c, RANDOM_G(fallback_seed), 20);
694+
}
695+
PHP_SHA1Final(RANDOM_G(fallback_seed), &c);
696+
RANDOM_G(fallback_seed_initialized) = true;
697+
698+
uint64_t result = 0;
699+
700+
for (int i = 0; i < sizeof(result); i++) {
701+
result = result | (((uint64_t)RANDOM_G(fallback_seed)[i]) << (i * 8));
702+
}
703+
704+
return result;
705+
}
706+
615707
/* {{{ PHP_GINIT_FUNCTION */
616708
static PHP_GINIT_FUNCTION(random)
617709
{
618710
random_globals->random_fd = -1;
711+
random_globals->fallback_seed_initialized = false;
619712
}
620713
/* }}} */
621714

ext/session/session.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -2879,7 +2879,10 @@ static PHP_GINIT_FUNCTION(ps) /* {{{ */
28792879
};
28802880
php_random_uint128_t seed;
28812881
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
2882-
seed = php_random_uint128_constant(GENERATE_SEED(), GENERATE_SEED());
2882+
seed = php_random_uint128_constant(
2883+
php_random_generate_fallback_seed(),
2884+
php_random_generate_fallback_seed()
2885+
);
28832886
}
28842887
php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed);
28852888
}

0 commit comments

Comments
 (0)