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 | Author: Go Kudo <zeriyoshi@php.net> |
14 | |
15 | Based on code from: Melissa O'Neill <oneill@pcg-random.org> |
16 +----------------------------------------------------------------------+
17 */
18
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
22
23 #include "php.h"
24 #include "php_random.h"
25 #include "php_random_csprng.h"
26 #include "php_random_uint128.h"
27
28 #include "Zend/zend_exceptions.h"
29
step(php_random_status_state_pcgoneseq128xslrr64 * s)30 static inline void step(php_random_status_state_pcgoneseq128xslrr64 *s)
31 {
32 s->state = php_random_uint128_add(
33 php_random_uint128_multiply(s->state, php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL)),
34 php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL)
35 );
36 }
37
seed128(php_random_status_state_pcgoneseq128xslrr64 * s,php_random_uint128_t seed)38 static inline void seed128(php_random_status_state_pcgoneseq128xslrr64 *s, php_random_uint128_t seed)
39 {
40 s->state = php_random_uint128_constant(0ULL, 0ULL);
41 step(s);
42 s->state = php_random_uint128_add(s->state, seed);
43 step(s);
44 }
45
seed(php_random_status * status,uint64_t seed)46 static void seed(php_random_status *status, uint64_t seed)
47 {
48 seed128(status->state, php_random_uint128_constant(0ULL, seed));
49 }
50
generate(php_random_status * status)51 static php_random_result generate(php_random_status *status)
52 {
53 php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
54
55 step(s);
56
57 return (php_random_result){
58 .size = sizeof(uint64_t),
59 .result = php_random_pcgoneseq128xslrr64_rotr64(s->state),
60 };
61 }
62
range(php_random_status * status,zend_long min,zend_long max)63 static zend_long range(php_random_status *status, zend_long min, zend_long max)
64 {
65 return php_random_range(&php_random_algo_pcgoneseq128xslrr64, status, min, max);
66 }
67
serialize(php_random_status * status,HashTable * data)68 static bool serialize(php_random_status *status, HashTable *data)
69 {
70 php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
71 uint64_t u;
72 zval z;
73
74 u = php_random_uint128_hi(s->state);
75 ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t)));
76 zend_hash_next_index_insert(data, &z);
77
78 u = php_random_uint128_lo(s->state);
79 ZVAL_STR(&z, php_random_bin2hex_le(&u, sizeof(uint64_t)));
80 zend_hash_next_index_insert(data, &z);
81
82 return true;
83 }
84
unserialize(php_random_status * status,HashTable * data)85 static bool unserialize(php_random_status *status, HashTable *data)
86 {
87 php_random_status_state_pcgoneseq128xslrr64 *s = status->state;
88 uint64_t u[2];
89 zval *t;
90
91 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
92 if (zend_hash_num_elements(data) != 2) {
93 return false;
94 }
95
96 for (uint32_t i = 0; i < 2; i++) {
97 t = zend_hash_index_find(data, i);
98 if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) {
99 return false;
100 }
101 if (!php_random_hex2bin_le(Z_STR_P(t), &u[i])) {
102 return false;
103 }
104 }
105 s->state = php_random_uint128_constant(u[0], u[1]);
106
107 return true;
108 }
109
110 const php_random_algo php_random_algo_pcgoneseq128xslrr64 = {
111 sizeof(php_random_status_state_pcgoneseq128xslrr64),
112 seed,
113 generate,
114 range,
115 serialize,
116 unserialize
117 };
118
119 /* {{{ php_random_pcgoneseq128xslrr64_advance */
php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgoneseq128xslrr64 * state,uint64_t advance)120 PHPAPI void php_random_pcgoneseq128xslrr64_advance(php_random_status_state_pcgoneseq128xslrr64 *state, uint64_t advance)
121 {
122 php_random_uint128_t
123 cur_mult = php_random_uint128_constant(2549297995355413924ULL,4865540595714422341ULL),
124 cur_plus = php_random_uint128_constant(6364136223846793005ULL,1442695040888963407ULL),
125 acc_mult = php_random_uint128_constant(0ULL, 1ULL),
126 acc_plus = php_random_uint128_constant(0ULL, 0ULL);
127
128 while (advance > 0) {
129 if (advance & 1) {
130 acc_mult = php_random_uint128_multiply(acc_mult, cur_mult);
131 acc_plus = php_random_uint128_add(php_random_uint128_multiply(acc_plus, cur_mult), cur_plus);
132 }
133 cur_plus = php_random_uint128_multiply(php_random_uint128_add(cur_mult, php_random_uint128_constant(0ULL, 1ULL)), cur_plus);
134 cur_mult = php_random_uint128_multiply(cur_mult, cur_mult);
135 advance /= 2;
136 }
137
138 state->state = php_random_uint128_add(php_random_uint128_multiply(acc_mult, state->state), acc_plus);
139 }
140 /* }}} */
141
142 /* {{{ Random\Engine\PcgOneseq128XslRr64::__construct */
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64,__construct)143 PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, __construct)
144 {
145 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
146 php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state;
147 zend_string *str_seed = NULL;
148 zend_long int_seed = 0;
149 bool seed_is_null = true;
150
151 ZEND_PARSE_PARAMETERS_START(0, 1)
152 Z_PARAM_OPTIONAL;
153 Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null);
154 ZEND_PARSE_PARAMETERS_END();
155
156 if (seed_is_null) {
157 php_random_uint128_t s;
158
159 if (php_random_bytes_throw(&s, sizeof(s)) == FAILURE) {
160 zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
161 RETURN_THROWS();
162 }
163
164 seed128(state, s);
165 } else {
166 if (str_seed) {
167 /* char (byte: 8 bit) * 16 = 128 bits */
168 if (ZSTR_LEN(str_seed) == 16) {
169 uint64_t t[2];
170
171 /* Endianness safe copy */
172 for (uint32_t i = 0; i < 2; i++) {
173 t[i] = 0;
174 for (uint32_t j = 0; j < 8; j++) {
175 t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8);
176 }
177 }
178
179 seed128(state, php_random_uint128_constant(t[0], t[1]));
180 } else {
181 zend_argument_value_error(1, "must be a 16 byte (128 bit) string");
182 RETURN_THROWS();
183 }
184 } else {
185 seed128(state, php_random_uint128_constant(0ULL, (uint64_t) int_seed));
186 }
187 }
188 }
189 /* }}} */
190
191 /* {{{ Random\Engine\PcgOneseq128XslRr64::jump() */
PHP_METHOD(Random_Engine_PcgOneseq128XslRr64,jump)192 PHP_METHOD(Random_Engine_PcgOneseq128XslRr64, jump)
193 {
194 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
195 php_random_status_state_pcgoneseq128xslrr64 *state = engine->status->state;
196 zend_long advance = 0;
197
198 ZEND_PARSE_PARAMETERS_START(1, 1)
199 Z_PARAM_LONG(advance);
200 ZEND_PARSE_PARAMETERS_END();
201
202 if (UNEXPECTED(advance < 0)) {
203 zend_argument_value_error(1, "must be greater than or equal to 0");
204 RETURN_THROWS();
205 }
206
207 php_random_pcgoneseq128xslrr64_advance(state, advance);
208 }
209 /* }}} */
210