wp/wp-includes/random_compat/random_bytes_dev_urandom.php
changeset 19 3d72ae0968f4
parent 9 177826044cd9
equal deleted inserted replaced
18:be944660c56a 19:3d72ae0968f4
     1 <?php
     1 <?php
     2 /**
     2 /**
     3  * Random_* Compatibility Library 
     3  * Random_* Compatibility Library
     4  * for using the new PHP 7 random_* API in PHP 5 projects
     4  * for using the new PHP 7 random_* API in PHP 5 projects
     5  * 
     5  *
     6  * The MIT License (MIT)
     6  * The MIT License (MIT)
     7  *
     7  *
     8  * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
     8  * Copyright (c) 2015 - 2018 Paragon Initiative Enterprises
     9  * 
     9  *
    10  * Permission is hereby granted, free of charge, to any person obtaining a copy
    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
    11  * of this software and associated documentation files (the "Software"), to deal
    12  * in the Software without restriction, including without limitation the rights
    12  * in the Software without restriction, including without limitation the rights
    13  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    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
    14  * copies of the Software, and to permit persons to whom the Software is
    15  * furnished to do so, subject to the following conditions:
    15  * furnished to do so, subject to the following conditions:
    16  * 
    16  *
    17  * The above copyright notice and this permission notice shall be included in
    17  * The above copyright notice and this permission notice shall be included in
    18  * all copies or substantial portions of the Software.
    18  * all copies or substantial portions of the Software.
    19  * 
    19  *
    20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    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,
    21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    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
    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,
    24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    34     /**
    34     /**
    35      * Unless open_basedir is enabled, use /dev/urandom for
    35      * Unless open_basedir is enabled, use /dev/urandom for
    36      * random numbers in accordance with best practices
    36      * random numbers in accordance with best practices
    37      *
    37      *
    38      * Why we use /dev/urandom and not /dev/random
    38      * Why we use /dev/urandom and not /dev/random
       
    39      * @ref https://www.2uo.de/myths-about-urandom
    39      * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
    40      * @ref http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers
    40      *
    41      *
    41      * @param int $bytes
    42      * @param int $bytes
    42      *
    43      *
    43      * @throws Exception
    44      * @throws Exception
    44      *
    45      *
    45      * @return string
    46      * @return string
    46      */
    47      */
    47     function random_bytes($bytes)
    48     function random_bytes($bytes)
    48     {
    49     {
       
    50         /** @var resource $fp */
    49         static $fp = null;
    51         static $fp = null;
       
    52 
    50         /**
    53         /**
    51          * This block should only be run once
    54          * This block should only be run once
    52          */
    55          */
    53         if (empty($fp)) {
    56         if (empty($fp)) {
    54             /**
    57             /**
    55              * We use /dev/urandom if it is a char device.
    58              * We don't want to ever read C:\dev\random, only /dev/urandom on
    56              * We never fall back to /dev/random
    59              * Unix-like operating systems. While we guard against this
       
    60              * condition in random.php, it doesn't hurt to be defensive in depth
       
    61              * here.
       
    62              *
       
    63              * To that end, we only try to open /dev/urandom if we're on a Unix-
       
    64              * like operating system (which means the directory separator is set
       
    65              * to "/" not "\".
    57              */
    66              */
    58             $fp = fopen('/dev/urandom', 'rb');
    67             if (DIRECTORY_SEPARATOR === '/') {
    59             if (!empty($fp)) {
    68                 if (!is_readable('/dev/urandom')) {
    60                 $st = fstat($fp);
    69                     throw new Exception(
    61                 if (($st['mode'] & 0170000) !== 020000) {
    70                         'Environment misconfiguration: ' .
    62                     fclose($fp);
    71                         '/dev/urandom cannot be read.'
    63                     $fp = false;
    72                     );
       
    73                 }
       
    74                 /**
       
    75                  * We use /dev/urandom if it is a char device.
       
    76                  * We never fall back to /dev/random
       
    77                  */
       
    78                 /** @var resource|bool $fp */
       
    79                 $fp = fopen('/dev/urandom', 'rb');
       
    80                 if (is_resource($fp)) {
       
    81                     /** @var array<string, int> $st */
       
    82                     $st = fstat($fp);
       
    83                     if (($st['mode'] & 0170000) !== 020000) {
       
    84                         fclose($fp);
       
    85                         $fp = false;
       
    86                     }
    64                 }
    87                 }
    65             }
    88             }
    66 
    89 
    67             if (!empty($fp)) {
    90             if (is_resource($fp)) {
    68                 /**
    91                 /**
    69                  * stream_set_read_buffer() does not exist in HHVM
    92                  * stream_set_read_buffer() does not exist in HHVM
    70                  *
    93                  *
    71                  * If we don't set the stream's read buffer to 0, PHP will
    94                  * If we don't set the stream's read buffer to 0, PHP will
    72                  * internally buffer 8192 bytes, which can waste entropy
    95                  * internally buffer 8192 bytes, which can waste entropy
    81                 }
   104                 }
    82             }
   105             }
    83         }
   106         }
    84 
   107 
    85         try {
   108         try {
       
   109             /** @var int $bytes */
    86             $bytes = RandomCompat_intval($bytes);
   110             $bytes = RandomCompat_intval($bytes);
    87         } catch (TypeError $ex) {
   111         } catch (TypeError $ex) {
    88             throw new TypeError(
   112             throw new TypeError(
    89                 'random_bytes(): $bytes must be an integer'
   113                 'random_bytes(): $bytes must be an integer'
    90             );
   114             );
   101          *
   125          *
   102          * It does not belong in an else {} block, because the above
   126          * 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
   127          * if (empty($fp)) line is logic that should only be run once per
   104          * page load.
   128          * page load.
   105          */
   129          */
   106         if (!empty($fp)) {
   130         if (is_resource($fp)) {
   107             /**
   131             /**
   108              * @var int
   132              * @var int
   109              */
   133              */
   110             $remaining = $bytes;
   134             $remaining = $bytes;
   111 
   135 
   121                 /**
   145                 /**
   122                  * @var string|bool
   146                  * @var string|bool
   123                  */
   147                  */
   124                 $read = fread($fp, $remaining);
   148                 $read = fread($fp, $remaining);
   125                 if (!is_string($read)) {
   149                 if (!is_string($read)) {
   126                     if ($read === false) {
   150                     /**
   127                         /**
   151                      * We cannot safely read from the file. Exit the
   128                          * We cannot safely read from the file. Exit the
   152                      * do-while loop and trigger the exception condition
   129                          * do-while loop and trigger the exception condition
   153                      *
   130                          *
   154                      * @var string|bool
   131                          * @var string|bool
   155                      */
   132                          */
   156                     $buf = false;
   133                         $buf = false;
   157                     break;
   134                         break;
       
   135                     }
       
   136                 }
   158                 }
   137                 /**
   159                 /**
   138                  * Decrease the number of bytes returned from remaining
   160                  * Decrease the number of bytes returned from remaining
   139                  */
   161                  */
   140                 $remaining -= RandomCompat_strlen($read);
   162                 $remaining -= RandomCompat_strlen($read);
   141                 /**
   163                 /**
   142                  * @var string|bool
   164                  * @var string $buf
   143                  */
   165                  */
   144                 $buf = $buf . $read;
   166                 $buf .= $read;
   145             } while ($remaining > 0);
   167             } while ($remaining > 0);
   146 
   168 
   147             /**
   169             /**
   148              * Is our result valid?
   170              * Is our result valid?
       
   171              * @var string|bool $buf
   149              */
   172              */
   150             if (is_string($buf)) {
   173             if (is_string($buf)) {
   151                 if (RandomCompat_strlen($buf) === $bytes) {
   174                 if (RandomCompat_strlen($buf) === $bytes) {
   152                     /**
   175                     /**
   153                      * Return our random entropy buffer here:
   176                      * Return our random entropy buffer here: