5b2143a16297a5ee79ba48f997ef2f39e5d97696
[yaffs-website] / vendor / paragonie / random_compat / lib / random_int.php
1 <?php
2
3 if (!is_callable('random_int')) {
4     /**
5      * Random_* Compatibility Library
6      * for using the new PHP 7 random_* API in PHP 5 projects
7      *
8      * The MIT License (MIT)
9      *
10      * Copyright (c) 2015 - 2017 Paragon Initiative Enterprises
11      *
12      * Permission is hereby granted, free of charge, to any person obtaining a copy
13      * of this software and associated documentation files (the "Software"), to deal
14      * in the Software without restriction, including without limitation the rights
15      * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16      * copies of the Software, and to permit persons to whom the Software is
17      * furnished to do so, subject to the following conditions:
18      *
19      * The above copyright notice and this permission notice shall be included in
20      * all copies or substantial portions of the Software.
21      *
22      * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23      * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24      * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25      * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26      * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27      * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28      * SOFTWARE.
29      */
30
31     /**
32      * Fetch a random integer between $min and $max inclusive
33      *
34      * @param int $min
35      * @param int $max
36      *
37      * @throws Exception
38      *
39      * @return int
40      */
41     function random_int($min, $max)
42     {
43         /**
44          * Type and input logic checks
45          *
46          * If you pass it a float in the range (~PHP_INT_MAX, PHP_INT_MAX)
47          * (non-inclusive), it will sanely cast it to an int. If you it's equal to
48          * ~PHP_INT_MAX or PHP_INT_MAX, we let it fail as not an integer. Floats
49          * lose precision, so the <= and => operators might accidentally let a float
50          * through.
51          */
52
53         try {
54             $min = RandomCompat_intval($min);
55         } catch (TypeError $ex) {
56             throw new TypeError(
57                 'random_int(): $min must be an integer'
58             );
59         }
60
61         try {
62             $max = RandomCompat_intval($max);
63         } catch (TypeError $ex) {
64             throw new TypeError(
65                 'random_int(): $max must be an integer'
66             );
67         }
68
69         /**
70          * Now that we've verified our weak typing system has given us an integer,
71          * let's validate the logic then we can move forward with generating random
72          * integers along a given range.
73          */
74         if ($min > $max) {
75             throw new Error(
76                 'Minimum value must be less than or equal to the maximum value'
77             );
78         }
79
80         if ($max === $min) {
81             return (int) $min;
82         }
83
84         /**
85          * Initialize variables to 0
86          *
87          * We want to store:
88          * $bytes => the number of random bytes we need
89          * $mask => an integer bitmask (for use with the &) operator
90          *          so we can minimize the number of discards
91          */
92         $attempts = $bits = $bytes = $mask = $valueShift = 0;
93
94         /**
95          * At this point, $range is a positive number greater than 0. It might
96          * overflow, however, if $max - $min > PHP_INT_MAX. PHP will cast it to
97          * a float and we will lose some precision.
98          */
99         $range = $max - $min;
100
101         /**
102          * Test for integer overflow:
103          */
104         if (!is_int($range)) {
105
106             /**
107              * Still safely calculate wider ranges.
108              * Provided by @CodesInChaos, @oittaa
109              *
110              * @ref https://gist.github.com/CodesInChaos/03f9ea0b58e8b2b8d435
111              *
112              * We use ~0 as a mask in this case because it generates all 1s
113              *
114              * @ref https://eval.in/400356 (32-bit)
115              * @ref http://3v4l.org/XX9r5  (64-bit)
116              */
117             $bytes = PHP_INT_SIZE;
118             $mask = ~0;
119
120         } else {
121
122             /**
123              * $bits is effectively ceil(log($range, 2)) without dealing with
124              * type juggling
125              */
126             while ($range > 0) {
127                 if ($bits % 8 === 0) {
128                     ++$bytes;
129                 }
130                 ++$bits;
131                 $range >>= 1;
132                 $mask = $mask << 1 | 1;
133             }
134             $valueShift = $min;
135         }
136
137         $val = 0;
138         /**
139          * Now that we have our parameters set up, let's begin generating
140          * random integers until one falls between $min and $max
141          */
142         do {
143             /**
144              * The rejection probability is at most 0.5, so this corresponds
145              * to a failure probability of 2^-128 for a working RNG
146              */
147             if ($attempts > 128) {
148                 throw new Exception(
149                     'random_int: RNG is broken - too many rejections'
150                 );
151             }
152
153             /**
154              * Let's grab the necessary number of random bytes
155              */
156             $randomByteString = random_bytes($bytes);
157
158             /**
159              * Let's turn $randomByteString into an integer
160              *
161              * This uses bitwise operators (<< and |) to build an integer
162              * out of the values extracted from ord()
163              *
164              * Example: [9F] | [6D] | [32] | [0C] =>
165              *   159 + 27904 + 3276800 + 201326592 =>
166              *   204631455
167              */
168             $val &= 0;
169             for ($i = 0; $i < $bytes; ++$i) {
170                 $val |= ord($randomByteString[$i]) << ($i * 8);
171             }
172
173             /**
174              * Apply mask
175              */
176             $val &= $mask;
177             $val += $valueShift;
178
179             ++$attempts;
180             /**
181              * If $val overflows to a floating point number,
182              * ... or is larger than $max,
183              * ... or smaller than $min,
184              * then try again.
185              */
186         } while (!is_int($val) || $val > $max || $val < $min);
187
188         return (int) $val;
189     }
190 }