annotate vendor/paragonie/random_compat/lib/random_bytes_dev_urandom.php @ 5:12f9dff5fda9 tip

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