wp/wp-includes/random_compat/random_bytes_dev_urandom.php
changeset 7 cf61fcea0001
child 9 177826044cd9
equal deleted inserted replaced
6:490d5cc509ed 7:cf61fcea0001
       
     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 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 (function_exists('stream_set_read_buffer')) {
       
    77                 stream_set_read_buffer($fp, RANDOM_COMPAT_READ_BUFFER);
       
    78             }
       
    79             if (function_exists('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         $remaining = $bytes;
       
   108         $buf = '';
       
   109 
       
   110         /**
       
   111          * We use fread() in a loop to protect against partial reads
       
   112          */
       
   113         do {
       
   114             $read = fread($fp, $remaining); 
       
   115             if ($read === false) {
       
   116                 /**
       
   117                  * We cannot safely read from the file. Exit the
       
   118                  * do-while loop and trigger the exception condition
       
   119                  */
       
   120                 $buf = false;
       
   121                 break;
       
   122             }
       
   123             /**
       
   124              * Decrease the number of bytes returned from remaining
       
   125              */
       
   126             $remaining -= RandomCompat_strlen($read);
       
   127             $buf .= $read;
       
   128         } while ($remaining > 0);
       
   129         
       
   130         /**
       
   131          * Is our result valid?
       
   132          */
       
   133         if ($buf !== false) {
       
   134             if (RandomCompat_strlen($buf) === $bytes) {
       
   135                 /**
       
   136                  * Return our random entropy buffer here:
       
   137                  */
       
   138                 return $buf;
       
   139             }
       
   140         }
       
   141     }
       
   142 
       
   143     /**
       
   144      * If we reach here, PHP has failed us.
       
   145      */
       
   146     throw new Exception(
       
   147         'Error reading from source device'
       
   148     );
       
   149 }
       
   150 endif;