FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
php-src/ext/random/csprng.c at master · php/php-src · GitHub
Uh oh!
There was an error while loading.
Please reload this page
.
php
/
php-src
Public
Notifications
You must be signed in to change notification settings
Fork
8.1k
Star
40.3k
Code
Issues
960
Pull requests
1.1k
Actions
Security and quality
52
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
php-src
/
ext
/
random
/
csprng.c
Copy path
More file actions
More file actions
Latest commit
History
History
History
273 lines (237 loc) · 8.16 KB
Breadcrumbs
php-src
/
ext
/
random
/
csprng.c
Copy path
File metadata and controls
273 lines (237 loc) · 8.16 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
/*
+----------------------------------------------------------------------+
| Copyright © The PHP Group and Contributors. |
+----------------------------------------------------------------------+
| This source file is subject to the Modified BSD License that is |
| bundled with this package in the file LICENSE, and is available |
| through the World Wide Web at <https://www.php.net/license/>. |
| |
| SPDX-License-Identifier: BSD-3-Clause |
+----------------------------------------------------------------------+
| Authors: Tim Düsterhus <timwolla@php.net> |
| Go Kudo <zeriyoshi@php.net> |
+----------------------------------------------------------------------+
*/
#ifdef
HAVE_CONFIG_H
# include
"config.h"
#endif
#include
<stdlib.h>
#include
<sys/stat.h>
#include
<fcntl.h>
#include
"php.h"
#include
"Zend/zend_exceptions.h"
#include
"Zend/zend_atomic.h"
#include
"php_random.h"
#include
"php_random_csprng.h"
#ifdef
HAVE_UNISTD_H
# include
<unistd.h>
#endif
#ifdef
PHP_WIN32
# include
"win32/time.h"
# include
"win32/winutil.h"
# include
<process.h>
#endif
#ifdef
__linux__
# include
<sys/syscall.h>
#endif
#ifdef
HAVE_SYS_PARAM_H
# include
<sys/param.h>
# if
(defined(
__FreeBSD__
)
&&
__FreeBSD_version
>
1200000
)
||
(defined(
__DragonFly__
)
&&
__DragonFly_version
>=
500700
)
||
\
(defined(
__sun
)
&&
defined(
HAVE_GETRANDOM
))
||
(defined(
__NetBSD__
)
&&
__NetBSD_Version__
>=
1000000000
)
||
defined(
__midipix__
)
# include
<sys/random.h>
# endif
#endif
#ifdef
HAVE_COMMONCRYPTO_COMMONRANDOM_H
# include
<CommonCrypto/CommonCryptoError.h>
# include
<CommonCrypto/CommonRandom.h>
#endif
#if
__has_feature
(
memory_sanitizer
)
# include
<sanitizer/msan_interface.h>
#endif
#ifndef
PHP_WIN32
static
zend_atomic_int
random_fd
=
ZEND_ATOMIC_INT_INITIALIZER
(
-1
);
#endif
ZEND_ATTRIBUTE_NONNULL
PHPAPI
zend_result
php_random_bytes_ex
(
void
*
bytes
,
size_t
size
,
char
*
errstr
,
size_t
errstr_size
)
{
#ifdef
PHP_WIN32
/* Defer to CryptGenRandom on Windows */
if
(
php_win32_get_random_bytes
(
bytes
,
size
)
==
FAILURE
) {
snprintf
(
errstr
,
errstr_size
,
"Failed to retrieve randomness from the operating system (BCryptGenRandom)"
);
return
FAILURE
;
}
#elif
defined(
HAVE_COMMONCRYPTO_COMMONRANDOM_H
)
/*
* Purposely prioritized upon arc4random_buf for modern macOs releases
* arc4random api on this platform uses `ccrng_generate` which returns
* a status but silented to respect the "no fail" arc4random api interface
* the vast majority of the time, it works fine ; but better make sure we catch failures
*/
if
(
CCRandomGenerateBytes
(
bytes
,
size
)
!=
kCCSuccess
) {
snprintf
(
errstr
,
errstr_size
,
"Failed to retrieve randomness from the operating system (CCRandomGenerateBytes)"
);
return
FAILURE
;
}
#elif
defined(
HAVE_ARC4RANDOM_BUF
)
&&
((defined(
__OpenBSD__
)
&&
OpenBSD
>=
201405
)
||
(defined(
__NetBSD__
)
&&
__NetBSD_Version__
>=
700000001
&&
__NetBSD_Version__
<
1000000000
)
||
\
defined(
__APPLE__
)
||
defined(
__HAIKU__
))
/*
* OpenBSD until there is a valid equivalent
* or NetBSD before the 10.x release
* falls back to arc4random_buf
* giving a decent output, the main benefit
* is being (relatively) failsafe.
* Older macOs releases fall also into this
* category for reasons explained above.
*/
arc4random_buf
(
bytes
,
size
);
#else
size_t
read_bytes
=
0
;
# if
(defined(
__linux__
)
&&
defined(
SYS_getrandom
))
||
(defined(
__FreeBSD__
)
&&
__FreeBSD_version
>=
1200000
)
||
(defined(
__DragonFly__
)
&&
__DragonFly_version
>=
500700
)
||
\
(defined(
__sun
)
&&
defined(
HAVE_GETRANDOM
))
||
(defined(
__NetBSD__
)
&&
__NetBSD_Version__
>=
1000000000
)
||
defined(
__midipix__
)
/* Linux getrandom(2) syscall or FreeBSD/DragonFlyBSD/NetBSD getrandom(2) function
* Being a syscall, implemented in the kernel, getrandom offers higher quality output
* compared to the arc4random api albeit a fallback to /dev/urandom is considered.
*/
while
(
read_bytes
<
size
) {
/* Below, (bytes + read_bytes) is pointer arithmetic.
bytes read_bytes size
| | |
[#######=============] (we're going to write over the = region)
\\\\\\\\\\\\\
amount_to_read
*/
size_t
amount_to_read
=
size
-
read_bytes
;
ssize_t
n
;
errno
=
0
;
# if
defined(
__linux__
)
n
=
syscall
(
SYS_getrandom
,
bytes
+
read_bytes
,
amount_to_read
,
0
);
# else
n
=
getrandom
(
bytes
+
read_bytes
,
amount_to_read
,
0
);
# endif
if
(
n
==
-1
) {
if
(
errno
==
ENOSYS
) {
/* This can happen if PHP was compiled against a newer kernel where getrandom()
* is available, but then runs on an older kernel without getrandom(). If this
* happens we simply fall back to reading from /dev/urandom. */
ZEND_ASSERT
(
read_bytes
==
0
);
break
;
}
else
if
(
errno
==
EINTR
||
errno
==
EAGAIN
) {
/* Try again */
continue
;
}
else
{
/* If the syscall fails, fall back to reading from /dev/urandom */
break
;
}
}
# if
__has_feature
(
memory_sanitizer
)
/* MSan does not instrument manual syscall invocations. */
__msan_unpoison
(
bytes
+
read_bytes
,
n
);
# endif
read_bytes
+=
(
size_t
)
n
;
}
# endif
if
(
read_bytes
<
size
) {
int
fd
=
zend_atomic_int_load_ex
(
&
random_fd
);
struct
stat
st
;
if
(
fd
<
0
) {
errno
=
0
;
fd
=
open
(
"/dev/urandom"
,
O_RDONLY
);
if
(
fd
<
0
) {
if
(
errno
!=
0
) {
snprintf
(
errstr
,
errstr_size
,
"Cannot open /dev/urandom: %s"
,
strerror
(
errno
));
}
else
{
snprintf
(
errstr
,
errstr_size
,
"Cannot open /dev/urandom"
);
}
return
FAILURE
;
}
errno
=
0
;
/* Does the file exist and is it a character device? */
if
(
fstat
(
fd
,
&
st
)
!=
0
||
# ifdef
S_ISNAM
!(
S_ISNAM
(
st
.
st_mode
)
||
S_ISCHR
(
st
.
st_mode
))
# else
!
S_ISCHR
(
st
.
st_mode
)
# endif
) {
close
(
fd
);
if
(
errno
!=
0
) {
snprintf
(
errstr
,
errstr_size
,
"Error reading from /dev/urandom: %s"
,
strerror
(
errno
));
}
else
{
snprintf
(
errstr
,
errstr_size
,
"Error reading from /dev/urandom"
);
}
return
FAILURE
;
}
int
expected
=
-1
;
if
(!
zend_atomic_int_compare_exchange_ex
(
&
random_fd
,
&
expected
,
fd
)) {
close
(
fd
);
/* expected is now the actual value of random_fd */
fd
=
expected
;
}
}
read_bytes
=
0
;
while
(
read_bytes
<
size
) {
errno
=
0
;
ssize_t
n
=
read
(
fd
,
bytes
+
read_bytes
,
size
-
read_bytes
);
if
(
n
<=
0
) {
if
(
errno
!=
0
) {
snprintf
(
errstr
,
errstr_size
,
"Could not gather sufficient random data: %s"
,
strerror
(
errno
));
}
else
{
snprintf
(
errstr
,
errstr_size
,
"Could not gather sufficient random data"
);
}
return
FAILURE
;
}
read_bytes
+=
(
size_t
)
n
;
}
}
#endif
return
SUCCESS
;
}
ZEND_ATTRIBUTE_NONNULL
PHPAPI
zend_result
php_random_bytes
(
void
*
bytes
,
size_t
size
,
bool
should_throw
)
{
char
errstr
[
128
];
zend_result
result
=
php_random_bytes_ex
(
bytes
,
size
,
errstr
,
sizeof
(
errstr
));
if
(
result
==
FAILURE
&&
should_throw
) {
zend_throw_exception
(
random_ce_Random_RandomException
,
errstr
,
0
);
}
return
result
;
}
ZEND_ATTRIBUTE_NONNULL
PHPAPI
zend_result
php_random_int
(
zend_long
min
,
zend_long
max
,
zend_long
*
result
,
bool
should_throw
)
{
zend_ulong
umax
;
zend_ulong
trial
;
if
(
min
==
max
) {
*
result
=
min
;
return
SUCCESS
;
}
umax
=
(
zend_ulong
)
max
-
(
zend_ulong
)
min
;
if
(
php_random_bytes
(
&
trial
,
sizeof
(
trial
),
should_throw
)
==
FAILURE
) {
return
FAILURE
;
}
/* Special case where no modulus is required */
if
(
umax
==
ZEND_ULONG_MAX
) {
*
result
=
(
zend_long
)
trial
;
return
SUCCESS
;
}
/* Increment the max so the range is inclusive of max */
umax
++
;
/* Powers of two are not biased */
if
((
umax
&
(
umax
-
1
))
!=
0
) {
/* Ceiling under which ZEND_LONG_MAX % max == 0 */
zend_ulong
limit
=
ZEND_ULONG_MAX
-
(
ZEND_ULONG_MAX
%
umax
)
-
1
;
/* Discard numbers over the limit to avoid modulo bias */
while
(
trial
>
limit
) {
if
(
php_random_bytes
(
&
trial
,
sizeof
(
trial
),
should_throw
)
==
FAILURE
) {
return
FAILURE
;
}
}
}
*
result
=
(
zend_long
)((
trial
%
umax
)
+
min
);
return
SUCCESS
;
}
PHPAPI
void
php_random_csprng_shutdown
(
void
)
{
#ifndef
PHP_WIN32
int
fd
=
zend_atomic_int_exchange
(
&
random_fd
,
-1
);
if
(
fd
!=
-1
) {
close
(
fd
);
}
#endif
}
Back
|
FazBrowse Home
|
New Git URL