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: David Blackman |
16 | Sebastiano Vigna <vigna@acm.org> |
17 +----------------------------------------------------------------------+
18 */
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include "php.h"
25 #include "php_random.h"
26
27 #include "Zend/zend_exceptions.h"
28
splitmix64(uint64_t * seed)29 static inline uint64_t splitmix64(uint64_t *seed)
30 {
31 uint64_t r;
32
33 r = (*seed += 0x9e3779b97f4a7c15ULL);
34 r = (r ^ (r >> 30)) * 0xbf58476d1ce4e5b9ULL;
35 r = (r ^ (r >> 27)) * 0x94d049bb133111ebULL;
36 return (r ^ (r >> 31));
37 }
38
rotl(const uint64_t x,int k)39 ZEND_ATTRIBUTE_CONST static inline uint64_t rotl(const uint64_t x, int k)
40 {
41 return (x << k) | (x >> (64 - k));
42 }
43
generate_state(php_random_status_state_xoshiro256starstar * s)44 static inline uint64_t generate_state(php_random_status_state_xoshiro256starstar *s)
45 {
46 const uint64_t r = rotl(s->state[1] * 5, 7) * 9;
47 const uint64_t t = s->state[1] << 17;
48
49 s->state[2] ^= s->state[0];
50 s->state[3] ^= s->state[1];
51 s->state[1] ^= s->state[2];
52 s->state[0] ^= s->state[3];
53
54 s->state[2] ^= t;
55
56 s->state[3] = rotl(s->state[3], 45);
57
58 return r;
59 }
60
jump(php_random_status_state_xoshiro256starstar * state,const uint64_t * jmp)61 static inline void jump(php_random_status_state_xoshiro256starstar *state, const uint64_t *jmp)
62 {
63 uint64_t s0 = 0, s1 = 0, s2 = 0, s3 = 0;
64
65 for (uint32_t i = 0; i < 4; i++) {
66 for (uint32_t j = 0; j < 64; j++) {
67 if (jmp[i] & 1ULL << j) {
68 s0 ^= state->state[0];
69 s1 ^= state->state[1];
70 s2 ^= state->state[2];
71 s3 ^= state->state[3];
72 }
73
74 generate_state(state);
75 }
76 }
77
78 state->state[0] = s0;
79 state->state[1] = s1;
80 state->state[2] = s2;
81 state->state[3] = s3;
82 }
83
seed256(php_random_status * status,uint64_t s0,uint64_t s1,uint64_t s2,uint64_t s3)84 static inline void seed256(php_random_status *status, uint64_t s0, uint64_t s1, uint64_t s2, uint64_t s3)
85 {
86 php_random_status_state_xoshiro256starstar *s = status->state;
87
88 s->state[0] = s0;
89 s->state[1] = s1;
90 s->state[2] = s2;
91 s->state[3] = s3;
92 }
93
seed(php_random_status * status,uint64_t seed)94 static void seed(php_random_status *status, uint64_t seed)
95 {
96 uint64_t s[4];
97
98 s[0] = splitmix64(&seed);
99 s[1] = splitmix64(&seed);
100 s[2] = splitmix64(&seed);
101 s[3] = splitmix64(&seed);
102
103 seed256(status, s[0], s[1], s[2], s[3]);
104 }
105
generate(php_random_status * status)106 static uint64_t generate(php_random_status *status)
107 {
108 return generate_state(status->state);
109 }
110
range(php_random_status * status,zend_long min,zend_long max)111 static zend_long range(php_random_status *status, zend_long min, zend_long max)
112 {
113 return php_random_range(&php_random_algo_xoshiro256starstar, status, min, max);
114 }
115
serialize(php_random_status * status,HashTable * data)116 static bool serialize(php_random_status *status, HashTable *data)
117 {
118 php_random_status_state_xoshiro256starstar *s = status->state;
119 zval t;
120
121 for (uint32_t i = 0; i < 4; i++) {
122 ZVAL_STR(&t, php_random_bin2hex_le(&s->state[i], sizeof(uint64_t)));
123 zend_hash_next_index_insert(data, &t);
124 }
125
126 return true;
127 }
128
unserialize(php_random_status * status,HashTable * data)129 static bool unserialize(php_random_status *status, HashTable *data)
130 {
131 php_random_status_state_xoshiro256starstar *s = status->state;
132 zval *t;
133
134 /* Verify the expected number of elements, this implicitly ensures that no additional elements are present. */
135 if (zend_hash_num_elements(data) != 4) {
136 return false;
137 }
138
139 for (uint32_t i = 0; i < 4; i++) {
140 t = zend_hash_index_find(data, i);
141 if (!t || Z_TYPE_P(t) != IS_STRING || Z_STRLEN_P(t) != (2 * sizeof(uint64_t))) {
142 return false;
143 }
144 if (!php_random_hex2bin_le(Z_STR_P(t), &s->state[i])) {
145 return false;
146 }
147 }
148
149 return true;
150 }
151
152 const php_random_algo php_random_algo_xoshiro256starstar = {
153 sizeof(uint64_t),
154 sizeof(php_random_status_state_xoshiro256starstar),
155 seed,
156 generate,
157 range,
158 serialize,
159 unserialize
160 };
161
php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar * state)162 PHPAPI void php_random_xoshiro256starstar_jump(php_random_status_state_xoshiro256starstar *state)
163 {
164 static const uint64_t jmp[] = {0x180ec6d33cfd0aba, 0xd5a61266f0c9392c, 0xa9582618e03fc9aa, 0x39abdc4529b1661c};
165 jump(state, jmp);
166 }
167
php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar * state)168 PHPAPI void php_random_xoshiro256starstar_jump_long(php_random_status_state_xoshiro256starstar *state)
169 {
170 static const uint64_t jmp[] = {0x76e15d3efefdcbbf, 0xc5004e441c522fb3, 0x77710069854ee241, 0x39109bb02acbe635};
171 jump(state, jmp);
172 }
173
174 /* {{{ Random\Engine\Xoshiro256StarStar::jump() */
PHP_METHOD(Random_Engine_Xoshiro256StarStar,jump)175 PHP_METHOD(Random_Engine_Xoshiro256StarStar, jump)
176 {
177 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
178 php_random_status_state_xoshiro256starstar *state = engine->status->state;
179
180 ZEND_PARSE_PARAMETERS_NONE();
181
182 php_random_xoshiro256starstar_jump(state);
183 }
184 /* }}} */
185
186 /* {{{ Random\Engine\Xoshiro256StarStar::jumpLong() */
PHP_METHOD(Random_Engine_Xoshiro256StarStar,jumpLong)187 PHP_METHOD(Random_Engine_Xoshiro256StarStar, jumpLong)
188 {
189 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
190 php_random_status_state_xoshiro256starstar *state = engine->status->state;
191
192 ZEND_PARSE_PARAMETERS_NONE();
193
194 php_random_xoshiro256starstar_jump_long(state);
195 }
196 /* }}} */
197
198 /* {{{ Random\Engine\Xoshiro256StarStar::__construct */
PHP_METHOD(Random_Engine_Xoshiro256StarStar,__construct)199 PHP_METHOD(Random_Engine_Xoshiro256StarStar, __construct)
200 {
201 php_random_engine *engine = Z_RANDOM_ENGINE_P(ZEND_THIS);
202 php_random_status_state_xoshiro256starstar *state = engine->status->state;
203 zend_string *str_seed = NULL;
204 zend_long int_seed = 0;
205 bool seed_is_null = true;
206
207 ZEND_PARSE_PARAMETERS_START(0, 1)
208 Z_PARAM_OPTIONAL;
209 Z_PARAM_STR_OR_LONG_OR_NULL(str_seed, int_seed, seed_is_null);
210 ZEND_PARSE_PARAMETERS_END();
211
212 if (seed_is_null) {
213 do {
214 if (php_random_bytes_throw(&state->state, 32) == FAILURE) {
215 zend_throw_exception(random_ce_Random_RandomException, "Failed to generate a random seed", 0);
216 RETURN_THROWS();
217 }
218 } while (UNEXPECTED(state->state[0] == 0 && state->state[1] == 0 && state->state[2] == 0 && state->state[3] == 0));
219 } else {
220 if (str_seed) {
221 /* char (byte: 8 bit) * 32 = 256 bits */
222 if (ZSTR_LEN(str_seed) == 32) {
223 uint64_t t[4];
224
225 /* Endianness safe copy */
226 for (uint32_t i = 0; i < 4; i++) {
227 t[i] = 0;
228 for (uint32_t j = 0; j < 8; j++) {
229 t[i] += ((uint64_t) (unsigned char) ZSTR_VAL(str_seed)[(i * 8) + j]) << (j * 8);
230 }
231 }
232
233 if (UNEXPECTED(t[0] == 0 && t[1] == 0 && t[2] == 0 && t[3] == 0)) {
234 zend_argument_value_error(1, "must not consist entirely of NUL bytes");
235 RETURN_THROWS();
236 }
237
238 seed256(engine->status, t[0], t[1], t[2], t[3]);
239 } else {
240 zend_argument_value_error(1, "must be a 32 byte (256 bit) string");
241 RETURN_THROWS();
242 }
243 } else {
244 engine->algo->seed(engine->status, (uint64_t) int_seed);
245 }
246 }
247 }
248 /* }}} */
249