Security update for Core, with self-updated composer
[yaffs-website] / vendor / paragonie / random_compat / lib / random_bytes_dev_urandom.php
1 <?php
2 /**
3  * Random_* Compatibility Library 
4  * for using the new PHP 7 random_* API in PHP 5 projects
5  * 
6  * The MIT License (MIT)
7  *
8  * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
9  * 
10  * Permission is hereby granted, free of charge, to any person obtaining a copy
11  * of this software and associated documentation files (the "Software"), to deal
12  * in the Software without restriction, including without limitation the rights
13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14  * copies of the Software, and to permit persons to whom the Software is
15  * furnished to do so, subject to the following conditions:
16  * 
17  * The above copyright notice and this permission notice shall be included in
18  * all copies or substantial portions of the Software.
19  * 
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26  * SOFTWARE.
27  */
28
29 if (!defined('RANDOM_COMPAT_READ_BUFFER')) {
30     define('RANDOM_COMPAT_READ_BUFFER', 8);
31 }
32
33 if (!is_callable('random_bytes')) {
34     /**
35      * Unless open_basedir is enabled, use /dev/urandom for
36      * random numbers in accordance with best practices
37      *
38      * Why we use /dev/urandom and not /dev/random
39      * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
40      *
41      * @param int $bytes
42      *
43      * @throws Exception
44      *
45      * @return string
46      */
47     function random_bytes($bytes)
48     {
49         static $fp = null;
50         /**
51          * This block should only be run once
52          */
53         if (empty($fp)) {
54             /**
55              * We use /dev/urandom if it is a char device.
56              * We never fall back to /dev/random
57              */
58             $fp = fopen('/dev/urandom', 'rb');
59             if (!empty($fp)) {
60                 $st = fstat($fp);
61                 if (($st['mode'] & 0170000) !== 020000) {
62                     fclose($fp);
63                     $fp = false;
64                 }
65             }
66
67             if (!empty($fp)) {
68                 /**
69                  * stream_set_read_buffer() does not exist in HHVM
70                  *
71                  * If we don't set the stream's read buffer to 0, PHP will
72                  * internally buffer 8192 bytes, which can waste entropy
73                  *
74                  * stream_set_read_buffer returns 0 on success
75                  */
76                 if (is_callable('stream_set_read_buffer')) {
77                     stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
78                 }
79                 if (is_callable('stream_set_chunk_size')) {
80                     stream_set_chunk_size($fp, RANDOM_COMPAT_READ_BUFFER);
81                 }
82             }
83         }
84
85         try {
86             $bytes = RandomCompat_intval($bytes);
87         } catch (TypeError $ex) {
88             throw new TypeError(
89                 'random_bytes(): $bytes must be an integer'
90             );
91         }
92
93         if ($bytes < 1) {
94             throw new Error(
95                 'Length must be greater than 0'
96             );
97         }
98
99         /**
100          * This if() block only runs if we managed to open a file handle
101          *
102          * It does not belong in an else {} block, because the above
103          * if (empty($fp)) line is logic that should only be run once per
104          * page load.
105          */
106         if (!empty($fp)) {
107             /**
108              * @var int
109              */
110             $remaining = $bytes;
111
112             /**
113              * @var string|bool
114              */
115             $buf = '';
116
117             /**
118              * We use fread() in a loop to protect against partial reads
119              */
120             do {
121                 /**
122                  * @var string|bool
123                  */
124                 $read = fread($fp, $remaining);
125                 if (!is_string($read)) {
126                     if ($read === false) {
127                         /**
128                          * We cannot safely read from the file. Exit the
129                          * do-while loop and trigger the exception condition
130                          *
131                          * @var string|bool
132                          */
133                         $buf = false;
134                         break;
135                     }
136                 }
137                 /**
138                  * Decrease the number of bytes returned from remaining
139                  */
140                 $remaining -= RandomCompat_strlen($read);
141                 /**
142                  * @var string|bool
143                  */
144                 $buf = $buf . $read;
145             } while ($remaining > 0);
146
147             /**
148              * Is our result valid?
149              */
150             if (is_string($buf)) {
151                 if (RandomCompat_strlen($buf) === $bytes) {
152                     /**
153                      * Return our random entropy buffer here:
154                      */
155                     return $buf;
156                 }
157             }
158         }
159
160         /**
161          * If we reach here, PHP has failed us.
162          */
163         throw new Exception(
164             'Error reading from source device'
165         );
166     }
167 }