Add zend_random_bytes(), zend_random_bytes_insecure() functions (#14054)

Co-authored-by: Tim Düsterhus <tim@bastelstu.be>
This commit is contained in:
Arnaud Le Blanc 2024-06-12 13:57:54 +02:00
parent d545b1d643
commit d1048a0869
No known key found for this signature in database
GPG key ID: 0098C05DD15ABC13
10 changed files with 169 additions and 12 deletions

View file

@ -94,6 +94,8 @@ ZEND_API char *(*zend_getenv)(const char *name, size_t name_len);
ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
ZEND_API zend_result (*zend_post_startup_cb)(void) = NULL;
ZEND_API void (*zend_post_shutdown_cb)(void) = NULL;
ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(void *bytes, size_t size, char *errstr, size_t errstr_size) = NULL;
ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(zend_random_bytes_insecure_state *state, void *bytes, size_t size) = NULL;
/* This callback must be signal handler safe! */
void (*zend_on_timeout)(int seconds);
@ -912,6 +914,10 @@ void zend_startup(zend_utility_functions *utility_functions) /* {{{ */
php_win32_cp_set_by_id(65001);
#endif
/* Set up early utility functions. */
zend_random_bytes = utility_functions->random_bytes_function;
zend_random_bytes_insecure = utility_functions->random_bytes_insecure_function;
start_memory_manager();
virtual_cwd_startup(); /* Could use shutdown to free the main cwd but it would just slow it down for CGI */

View file

@ -234,6 +234,11 @@ struct _zend_class_entry {
} info;
};
typedef union {
zend_max_align_t align;
uint64_t opaque[5];
} zend_random_bytes_insecure_state;
typedef struct _zend_utility_functions {
void (*error_function)(int type, zend_string *error_filename, const uint32_t error_lineno, zend_string *message);
size_t (*printf_function)(const char *format, ...) ZEND_ATTRIBUTE_PTR_FORMAT(printf, 1, 2);
@ -248,6 +253,8 @@ typedef struct _zend_utility_functions {
void (*printf_to_smart_str_function)(smart_str *buf, const char *format, va_list ap);
char *(*getenv_function)(const char *name, size_t name_len);
zend_string *(*resolve_path_function)(zend_string *filename);
zend_result (*random_bytes_function)(void *bytes, size_t size, char *errstr, size_t errstr_size);
void (*random_bytes_insecure_function)(zend_random_bytes_insecure_state *state, void *bytes, size_t size);
} zend_utility_functions;
typedef struct _zend_utility_values {
@ -340,6 +347,14 @@ extern void (*zend_printf_to_smart_string)(smart_string *buf, const char *format
extern void (*zend_printf_to_smart_str)(smart_str *buf, const char *format, va_list ap);
extern ZEND_API char *(*zend_getenv)(const char *name, size_t name_len);
extern ZEND_API zend_string *(*zend_resolve_path)(zend_string *filename);
/* Generate 'size' random bytes into 'bytes' with the OS CSPRNG. */
extern ZEND_ATTRIBUTE_NONNULL ZEND_API zend_result (*zend_random_bytes)(
void *bytes, size_t size, char *errstr, size_t errstr_size);
/* Generate 'size' random bytes into 'bytes' with a general purpose PRNG (not
* crypto safe). 'state' must be zeroed before the first call and can be reused.
*/
extern ZEND_ATTRIBUTE_NONNULL ZEND_API void (*zend_random_bytes_insecure)(
zend_random_bytes_insecure_state *state, void *bytes, size_t size);
/* These two callbacks are especially for opcache */
extern ZEND_API zend_result (*zend_post_startup_cb)(void);

View file

@ -791,4 +791,24 @@ extern "C++" {
# define ZEND_STATIC_ASSERT(c, m)
#endif
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L) /* C11 */ \
|| (defined(__cplusplus) && __cplusplus >= 201103L) /* C++11 */
typedef max_align_t zend_max_align_t;
#else
typedef union {
char c;
short s;
int i;
long l;
#if SIZEOF_LONG_LONG
long long ll;
#endif
float f;
double d;
long double ld;
void *p;
void (*fun)();
} zend_max_align_t;
#endif
#endif /* ZEND_PORTABILITY_H */

View file

@ -27,6 +27,7 @@ PHP_NEW_EXTENSION(random,
engine_secure.c \
engine_user.c \
gammasection.c \
randomizer.c,
randomizer.c \
zend_utils.c,
no,, -DZEND_ENABLE_STATIC_TSRMLS_CACHE=1)
PHP_INSTALL_HEADERS([ext/random], [php_random.h php_random_csprng.h php_random_uint128.h])

View file

@ -1,4 +1,4 @@
EXTENSION("random", "random.c", false /* never shared */, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1");
PHP_RANDOM="yes";
ADD_SOURCES(configure_module_dirname, "csprng.c engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c", "random");
ADD_SOURCES(configure_module_dirname, "csprng.c engine_combinedlcg.c engine_mt19937.c engine_pcgoneseq128xslrr64.c engine_xoshiro256starstar.c engine_secure.c engine_user.c gammasection.c randomizer.c zend_utils.c", "random");
PHP_INSTALL_HEADERS("ext/random", "php_random.h php_random_csprng.h php_random_uint128.h");

View file

@ -37,7 +37,10 @@
PHPAPI double php_combined_lcg(void);
typedef struct _php_random_fallback_seed_state php_random_fallback_seed_state;
PHPAPI uint64_t php_random_generate_fallback_seed(void);
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state);
static inline zend_long GENERATE_SEED(void)
{
@ -99,6 +102,11 @@ typedef struct _php_random_algo_with_state {
void *state;
} php_random_algo_with_state;
typedef struct _php_random_fallback_seed_state {
bool initialized;
unsigned char seed[20];
} php_random_fallback_seed_state;
extern PHPAPI const php_random_algo php_random_algo_combinedlcg;
extern PHPAPI const php_random_algo php_random_algo_mt19937;
extern PHPAPI const php_random_algo php_random_algo_pcgoneseq128xslrr64;
@ -197,8 +205,7 @@ PHP_RINIT_FUNCTION(random);
ZEND_BEGIN_MODULE_GLOBALS(random)
bool combined_lcg_seeded;
bool mt19937_seeded;
bool fallback_seed_initialized;
unsigned char fallback_seed[20];
php_random_fallback_seed_state fallback_seed_state;
php_random_status_state_combinedlcg combined_lcg;
php_random_status_state_mt19937 mt19937;
ZEND_END_MODULE_GLOBALS(random)

View file

@ -0,0 +1,35 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Arnaud Le Blanc <arnaud.lb@gmail.com> |
| Tim Düsterhus <timwolla@php.net> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_RANDOM_ZEND_UTILS_H
# define PHP_RANDOM_ZEND_UTILS_H
# include "php.h"
# include "php_random.h"
# include "zend.h"
typedef struct _php_random_bytes_insecure_state_for_zend {
bool initialized;
php_random_status_state_xoshiro256starstar xoshiro256starstar_state;
} php_random_bytes_insecure_state_for_zend;
ZEND_STATIC_ASSERT(sizeof(zend_random_bytes_insecure_state) >= sizeof(php_random_bytes_insecure_state_for_zend), "");
ZEND_ATTRIBUTE_NONNULL PHPAPI void php_random_bytes_insecure_for_zend(
zend_random_bytes_insecure_state *state, void *bytes, size_t size);
#endif /* PHP_RANDOM_ZEND_UTILS_H */

View file

@ -610,7 +610,7 @@ static inline void fallback_seed_add(PHP_SHA1_CTX *c, void *p, size_t l){
PHP_SHA1Update(c, p, l);
}
PHPAPI uint64_t php_random_generate_fallback_seed(void)
PHPAPI uint64_t php_random_generate_fallback_seed_ex(php_random_fallback_seed_state *state)
{
/* Mix various values using SHA-1 as a PRF to obtain as
* much entropy as possible, hopefully generating an
@ -628,7 +628,7 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
char buf[64 + 1];
PHP_SHA1Init(&c);
if (!RANDOM_G(fallback_seed_initialized)) {
if (!state->initialized) {
/* Current time. */
gettimeofday(&tv, NULL);
fallback_seed_add(&c, &tv, sizeof(tv));
@ -644,7 +644,7 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
fallback_seed_add(&c, &tid, sizeof(tid));
#endif
/* Pointer values to benefit from ASLR. */
pointer = &RANDOM_G(fallback_seed_initialized);
pointer = &state;
fallback_seed_add(&c, &pointer, sizeof(pointer));
pointer = &c;
fallback_seed_add(&c, &pointer, sizeof(pointer));
@ -668,24 +668,29 @@ PHPAPI uint64_t php_random_generate_fallback_seed(void)
gettimeofday(&tv, NULL);
fallback_seed_add(&c, &tv, sizeof(tv));
/* Previous state. */
fallback_seed_add(&c, RANDOM_G(fallback_seed), 20);
fallback_seed_add(&c, state->seed, 20);
}
PHP_SHA1Final(RANDOM_G(fallback_seed), &c);
RANDOM_G(fallback_seed_initialized) = true;
PHP_SHA1Final(state->seed, &c);
state->initialized = true;
uint64_t result = 0;
for (size_t i = 0; i < sizeof(result); i++) {
result = result | (((uint64_t)RANDOM_G(fallback_seed)[i]) << (i * 8));
result = result | (((uint64_t)state->seed[i]) << (i * 8));
}
return result;
}
PHPAPI uint64_t php_random_generate_fallback_seed(void)
{
return php_random_generate_fallback_seed_ex(&RANDOM_G(fallback_seed_state));
}
/* {{{ PHP_GINIT_FUNCTION */
static PHP_GINIT_FUNCTION(random)
{
random_globals->fallback_seed_initialized = false;
random_globals->fallback_seed_state.initialized = false;
}
/* }}} */

64
ext/random/zend_utils.c Normal file
View file

@ -0,0 +1,64 @@
/*
+----------------------------------------------------------------------+
| Copyright (c) The PHP Group |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| https://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
| Authors: Arnaud Le Blanc <arnaud.lb@gmail.com> |
| Tim Düsterhus <timwolla@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "php_random_zend_utils.h"
ZEND_ATTRIBUTE_NONNULL PHPAPI void php_random_bytes_insecure_for_zend(
zend_random_bytes_insecure_state *opaque_state, void *bytes, size_t size)
{
php_random_bytes_insecure_state_for_zend *state = (php_random_bytes_insecure_state_for_zend*) opaque_state;
if (UNEXPECTED(!state->initialized)) {
uint64_t t[4];
php_random_fallback_seed_state fallback_state;
fallback_state.initialized = false;
do {
/* Skip the CSPRNG if it has already failed */
if (!fallback_state.initialized) {
char errstr[128];
if (php_random_bytes_ex(&t, sizeof(t), errstr, sizeof(errstr)) == FAILURE) {
#if ZEND_DEBUG
fprintf(stderr, "php_random_bytes_ex: Failed to generate a random seed: %s\n", errstr);
#endif
goto fallback;
}
} else {
fallback:
t[0] = php_random_generate_fallback_seed_ex(&fallback_state);
t[1] = php_random_generate_fallback_seed_ex(&fallback_state);
t[2] = php_random_generate_fallback_seed_ex(&fallback_state);
t[3] = php_random_generate_fallback_seed_ex(&fallback_state);
}
} while (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0));
php_random_xoshiro256starstar_seed256(&state->xoshiro256starstar_state, t[0], t[1], t[2], t[3]);
state->initialized = true;
}
while (size > 0) {
php_random_result result = php_random_algo_xoshiro256starstar.generate(&state->xoshiro256starstar_state);
ZEND_ASSERT(result.size == 8 && sizeof(result.result) == 8);
size_t chunk_size = MIN(size, 8);
bytes = zend_mempcpy(bytes, &result.result, chunk_size);
size -= chunk_size;
}
}

View file

@ -49,6 +49,8 @@
#include "fopen_wrappers.h"
#include "ext/standard/php_standard.h"
#include "ext/date/php_date.h"
#include "ext/random/php_random_csprng.h"
#include "ext/random/php_random_zend_utils.h"
#include "php_variables.h"
#include "ext/standard/credits.h"
#ifdef PHP_WIN32
@ -2119,6 +2121,8 @@ zend_result php_module_startup(sapi_module_struct *sf, zend_module_entry *additi
zuf.printf_to_smart_str_function = php_printf_to_smart_str;
zuf.getenv_function = sapi_getenv;
zuf.resolve_path_function = php_resolve_path_for_zend;
zuf.random_bytes_function = php_random_bytes_ex;
zuf.random_bytes_insecure_function = php_random_bytes_insecure_for_zend;
zend_startup(&zuf);
zend_reset_lc_ctype_locale();
zend_update_current_locale();