Skip to content

random: Improve the output quality of RANDOM_SEED() #13730

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ PHP 8.4 INTERNALS UPGRADE NOTES
- The CSPRNG API (php_random_(bytes|int)_*) is now provided by the new
and much smaller php_random_csprng.h header. The new header is included
in php_random.h for compatibility with existing users.
- A new php_random_generate_fallback_seed() function has been added as a
replacement for the generically named GENERATE_SEED(). The internal
implementation has been improved to generate better seeds, however any
users should use the opportunity to verify that seeding is first
attempted using the CSPRNG for better output size flexibility.

c. ext/xsl
- The function php_xsl_create_object() was removed as it was not used
Expand Down
6 changes: 3 additions & 3 deletions ext/gmp/gmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -1734,9 +1734,9 @@ static void gmp_init_random(void)
/* Initialize */
gmp_randinit_mt(GMPG(rand_state));
/* Seed */
zend_long seed = 0;
if (php_random_bytes_silent(&seed, sizeof(zend_long)) == FAILURE) {
seed = GENERATE_SEED();
unsigned long int seed = 0;
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = (unsigned long int)php_random_generate_fallback_seed();
}
gmp_randseed_ui(GMPG(rand_state), seed);

Expand Down
2 changes: 1 addition & 1 deletion ext/random/engine_mt19937.c
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ PHPAPI void php_random_mt19937_seed_default(php_random_status_state_mt19937 *sta
uint32_t seed = 0;

if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = GENERATE_SEED();
seed = (uint32_t)php_random_generate_fallback_seed();
}

php_random_mt19937_seed32(state, seed);
Expand Down
14 changes: 5 additions & 9 deletions ext/random/php_random.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,17 +37,11 @@

PHPAPI double php_combined_lcg(void);

PHPAPI uint64_t php_random_generate_fallback_seed(void);

static inline zend_long GENERATE_SEED(void)
{
zend_ulong pid;

# ifdef PHP_WIN32
pid = (zend_ulong) GetCurrentProcessId();
# else
pid = (zend_ulong) getpid();
# endif

return (((zend_long) ((zend_ulong) time(NULL) * pid)) ^ ((zend_long) (1000000.0 * php_combined_lcg())));
return (zend_long)php_random_generate_fallback_seed();
}

# define PHP_MT_RAND_MAX ((zend_long) (0x7FFFFFFF)) /* (1<<31) - 1 */
Expand Down Expand Up @@ -213,6 +207,8 @@ ZEND_BEGIN_MODULE_GLOBALS(random)
int random_fd;
bool combined_lcg_seeded;
bool mt19937_seeded;
bool fallback_seed_initialized;
unsigned char fallback_seed[20];
php_random_status_state_combinedlcg combined_lcg;
php_random_status_state_mt19937 mt19937;
ZEND_END_MODULE_GLOBALS(random)
Expand Down
93 changes: 93 additions & 0 deletions ext/random/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
+----------------------------------------------------------------------+
| Authors: Sammy Kaye Powers <[email protected]> |
| Go Kudo <[email protected]> |
| Tim Düsterhus <[email protected]> |
+----------------------------------------------------------------------+
*/

Expand All @@ -31,6 +32,7 @@

#include "php_random.h"
#include "php_random_csprng.h"
#include "ext/standard/sha1.h"

#if HAVE_UNISTD_H
# include <unistd.h>
Expand Down Expand Up @@ -612,10 +614,101 @@ PHP_FUNCTION(random_int)
}
/* }}} */

static void write_32(PHP_SHA1_CTX *c, uint32_t u)
{
unsigned char buf[4];
unsigned char *p = buf;
*(p++) = (u >> 0) & 0xff;
*(p++) = (u >> 8) & 0xff;
*(p++) = (u >> 16) & 0xff;
*(p++) = (u >> 24) & 0xff;
PHP_SHA1Update(c, buf, sizeof(buf));
}

static void write_64(PHP_SHA1_CTX *c, uint64_t u)
{
write_32(c, u);
write_32(c, u >> 32);
}

static void write_p(PHP_SHA1_CTX *c, uintptr_t p)
{
if (sizeof(p) == 4) {
write_32(c, p);
} else {
write_64(c, p);
}
}

uint64_t php_random_generate_fallback_seed(void)
{
/* Mix various values using SHA-1 as a PRF to obtain as
* much entropy as possible, hopefully generating an
* unpredictable and independent uint64_t. Nevertheless
* the output of this function MUST NOT be treated as
* being cryptographically safe.
*/
PHP_SHA1_CTX c;
struct timeval tv;
char buf[64 + 1];

PHP_SHA1Init(&c);
if (!RANDOM_G(fallback_seed_initialized)) {
/* Current time. */
gettimeofday(&tv, NULL);
write_32(&c, tv.tv_sec);
write_32(&c, tv.tv_usec);
/* Various PIDs. */
write_32(&c, getpid());
#ifndef WIN32
write_32(&c, getppid());
#endif
#ifdef ZTS
write_32(&c, tsrm_thread_id());
#endif
/* Pointer values to benefit from ASLR. */
write_p(&c, (uintptr_t)&RANDOM_G(fallback_seed_initialized));
write_p(&c, (uintptr_t)&c);
/* Updated time. */
gettimeofday(&tv, NULL);
write_32(&c, tv.tv_usec);
/* Hostname. */
memset(buf, 0, sizeof(buf));
if (gethostname(buf, sizeof(buf) - 1) == 0) {
PHP_SHA1Update(&c, (unsigned char*)buf, strlen(buf));
}
/* CSPRNG. */
if (php_random_bytes_silent(buf, 16) == SUCCESS) {
PHP_SHA1Update(&c, (unsigned char*)buf, 16);
}
/* Updated time. */
gettimeofday(&tv, NULL);
write_32(&c, tv.tv_usec);
} else {
/* Current time. */
gettimeofday(&tv, NULL);
write_32(&c, tv.tv_sec);
write_32(&c, tv.tv_usec);
/* Previous state. */
PHP_SHA1Update(&c, RANDOM_G(fallback_seed), 20);
}
PHP_SHA1Final(RANDOM_G(fallback_seed), &c);
RANDOM_G(fallback_seed_initialized) = true;

uint64_t result = 0;

for (int i = 0; i < sizeof(result); i++) {
result = result | (((uint64_t)RANDOM_G(fallback_seed)[i]) << (i * 8));
}

return result;
}

/* {{{ PHP_GINIT_FUNCTION */
static PHP_GINIT_FUNCTION(random)
{
random_globals->random_fd = -1;
random_globals->fallback_seed_initialized = false;
}
/* }}} */

Expand Down
5 changes: 4 additions & 1 deletion ext/session/session.c
Original file line number Diff line number Diff line change
Expand Up @@ -2879,7 +2879,10 @@ static PHP_GINIT_FUNCTION(ps) /* {{{ */
};
php_random_uint128_t seed;
if (php_random_bytes_silent(&seed, sizeof(seed)) == FAILURE) {
seed = php_random_uint128_constant(GENERATE_SEED(), GENERATE_SEED());
seed = php_random_uint128_constant(
php_random_generate_fallback_seed(),
php_random_generate_fallback_seed()
);
}
php_random_pcgoneseq128xslrr64_seed128(ps_globals->random.state, seed);
}
Expand Down