1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 5 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2013-2016 Jakub Zelenka |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
15 | Author: Jakub Zelenka <bukka@php.net> |
16 +----------------------------------------------------------------------+
17 */
18
19 #include "php.h"
20 #include "php_crypto.h"
21 #include "php_crypto_rand.h"
22 #include "zend_exceptions.h"
23
24 #include <openssl/rand.h>
25 #include <openssl/err.h>
26
27 PHP_CRYPTO_EXCEPTION_DEFINE(Rand)
28 PHP_CRYPTO_ERROR_INFO_BEGIN(Rand)
29 PHP_CRYPTO_ERROR_INFO_ENTRY(
30 GENERATE_PREDICTABLE,
31 "The PRNG state is not yet unpredictable"
32 )
33 PHP_CRYPTO_ERROR_INFO_ENTRY(
34 FILE_WRITE_PREDICTABLE,
35 "The bytes written were generated without appropriate seed"
36 )
37 PHP_CRYPTO_ERROR_INFO_ENTRY(
38 REQUESTED_BYTES_NUMBER_TOO_HIGH,
39 "The requested number of bytes is too high"
40 )
41 PHP_CRYPTO_ERROR_INFO_ENTRY(
42 SEED_LENGTH_TOO_HIGH,
43 "The supplied seed length is too high"
44 )
45 PHP_CRYPTO_ERROR_INFO_END()
46
47 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_rand_generate, 0, 0, 1)
48 ZEND_ARG_INFO(0, num)
49 ZEND_ARG_INFO(0, must_be_strong)
50 ZEND_ARG_INFO(1, returned_strong_result)
51 ZEND_END_ARG_INFO()
52
53 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_rand_seed, 0, 0, 1)
54 ZEND_ARG_INFO(0, buf)
55 ZEND_ARG_INFO(0, entropy)
56 ZEND_END_ARG_INFO()
57
58 ZEND_BEGIN_ARG_INFO_EX(arginfo_crypto_rand_load_file, 0, 0, 1)
59 ZEND_ARG_INFO(0, filename)
60 ZEND_ARG_INFO(0, max_bytes)
61 ZEND_END_ARG_INFO()
62
63 ZEND_BEGIN_ARG_INFO(arginfo_crypto_rand_write_file, 0)
64 ZEND_ARG_INFO(0, filename)
65 ZEND_END_ARG_INFO()
66
67 static const zend_function_entry php_crypto_rand_object_methods[] = {
68 PHP_CRYPTO_ME(
69 Rand, generate,
70 arginfo_crypto_rand_generate,
71 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
72 )
73 PHP_CRYPTO_ME(
74 Rand, seed,
75 arginfo_crypto_rand_seed,
76 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
77 )
78 PHP_CRYPTO_ME(
79 Rand, cleanup,
80 NULL,
81 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
82 )
83 PHP_CRYPTO_ME(
84 Rand, loadFile,
85 arginfo_crypto_rand_load_file,
86 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
87 )
88 PHP_CRYPTO_ME(
89 Rand, writeFile,
90 arginfo_crypto_rand_write_file,
91 ZEND_ACC_STATIC|ZEND_ACC_PUBLIC
92 )
93 PHPC_FE_END
94 };
95
96 /* class entry */
97 PHP_CRYPTO_API zend_class_entry *php_crypto_rand_ce;
98
99 /* {{{ PHP_MINIT_FUNCTION */
PHP_MINIT_FUNCTION(crypto_rand)100 PHP_MINIT_FUNCTION(crypto_rand)
101 {
102 zend_class_entry ce;
103
104 /* Rand class */
105 INIT_CLASS_ENTRY(ce, PHP_CRYPTO_CLASS_NAME(Rand),
106 php_crypto_rand_object_methods);
107 php_crypto_rand_ce = PHPC_CLASS_REGISTER(ce);
108
109 /* RandException class */
110 PHP_CRYPTO_EXCEPTION_REGISTER(ce, Rand);
111 PHP_CRYPTO_ERROR_INFO_REGISTER(Rand);
112
113 return SUCCESS;
114 }
115 /* }}} */
116
117 /* {{{ proto static string Crypto\Rand::generate(
118 int $num, bool $must_be_strong = true,
119 &bool $returned_strong_result = true)
120 Generates pseudo random bytes */
PHP_CRYPTO_METHOD(Rand,generate)121 PHP_CRYPTO_METHOD(Rand, generate)
122 {
123 phpc_long_t num_long;
124 int num;
125 PHPC_STR_DECLARE(buf);
126 zval *zstrong_result = NULL;
127 zend_bool strong_result, must_be_strong = 1;
128
129 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|bz/",
130 &num_long, &must_be_strong, &zstrong_result) == FAILURE) {
131 return;
132 }
133
134 if (php_crypto_long_to_int(num_long, &num) == FAILURE) {
135 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Rand, REQUESTED_BYTES_NUMBER_TOO_HIGH));
136 RETURN_FALSE;
137 }
138
139
140 PHPC_STR_ALLOC(buf, num);
141
142 if (must_be_strong) {
143 if (!RAND_bytes((unsigned char *) PHPC_STR_VAL(buf), num)) {
144 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Rand, GENERATE_PREDICTABLE));
145 PHPC_STR_RELEASE(buf);
146 RETURN_FALSE;
147 }
148 strong_result = 1;
149 } else {
150 strong_result = RAND_pseudo_bytes((unsigned char *) PHPC_STR_VAL(buf), num);
151 }
152 if (zstrong_result) {
153 ZVAL_BOOL(zstrong_result, strong_result);
154 }
155 PHPC_STR_VAL(buf)[num] = '\0';
156 PHPC_STR_RETURN(buf);
157 }
158 /* }}} */
159
160 /* {{{ proto static void Crypto\Rand::seed(
161 string $buf, float $entropy = (float) strlen($buf))
162 Mixes bytes in $buf into PRNG state */
PHP_CRYPTO_METHOD(Rand,seed)163 PHP_CRYPTO_METHOD(Rand, seed)
164 {
165 char *buf;
166 phpc_str_size_t buf_str_size;
167 int buf_len;
168 double entropy;
169
170 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|d",
171 &buf, &buf_str_size, &entropy) == FAILURE) {
172 return;
173 }
174
175 if (php_crypto_str_size_to_int(buf_str_size, &buf_len) == FAILURE) {
176 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Rand, SEED_LENGTH_TOO_HIGH));
177 RETURN_NULL();
178 }
179
180 if (ZEND_NUM_ARGS() == 1) {
181 entropy = (double) buf_len;
182 }
183
184 RAND_add(buf, buf_len, entropy);
185 }
186 /* }}} */
187
188 /* {{{ proto static void Crypto\Rand::cleanup()
189 Cleans up PRNG state */
PHP_CRYPTO_METHOD(Rand,cleanup)190 PHP_CRYPTO_METHOD(Rand, cleanup)
191 {
192 if (zend_parse_parameters_none() == FAILURE) {
193 return;
194 }
195 RAND_cleanup();
196 RETURN_NULL();
197 }
198 /* }}} */
199
200 /* {{{ proto static int Crypto\Rand::loadFile(string $filename, int $max_bytes = -1)
201 Reads a number of bytes from file $filename and adds them
202 to the PRNG. If max_bytes is non-negative, up to to max_bytes
203 are read; if $max_bytes is negative, the complete file is read */
PHP_CRYPTO_METHOD(Rand,loadFile)204 PHP_CRYPTO_METHOD(Rand, loadFile)
205 {
206 char *path;
207 phpc_str_size_t path_len;
208 phpc_long_t max_bytes_len = -1;
209 int max_bytes;
210
211 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, PHPC_PATH_ZPP_FLAG"|l",
212 &path, &path_len, &max_bytes_len) == FAILURE) {
213 return;
214 }
215
216 if (php_crypto_long_to_int(max_bytes_len, &max_bytes) == FAILURE) {
217 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Rand, REQUESTED_BYTES_NUMBER_TOO_HIGH));
218 RETURN_FALSE;
219 }
220
221 if (max_bytes < -1) {
222 max_bytes = -1;
223 }
224
225 RETURN_LONG(RAND_load_file(path, max_bytes));
226 }
227 /* }}} */
228
229
230 /* {{{ proto static int Crypto\Rand::writeFile(string $filename)
231 Writes a number of random bytes (currently 1024) to file $filename
232 which can be used to initializethe PRNG by calling
233 Crypto\Rand::loadFile() in a later session */
PHP_CRYPTO_METHOD(Rand,writeFile)234 PHP_CRYPTO_METHOD(Rand, writeFile)
235 {
236 char *path;
237 phpc_str_size_t path_len;
238 int bytes_written;
239
240 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, PHPC_PATH_ZPP_FLAG,
241 &path, &path_len) == FAILURE) {
242 return;
243 }
244
245 bytes_written = RAND_write_file(path);
246 if (bytes_written < 0) {
247 php_crypto_error(PHP_CRYPTO_ERROR_ARGS(Rand, FILE_WRITE_PREDICTABLE));
248 RETURN_FALSE;
249 } else {
250 RETURN_LONG((phpc_long_t) bytes_written);
251 }
252 }
253 /* }}} */
254