ru-se.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

random_bytes_dev_urandom.php (5440B)


      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 }