CryptoRandom::getBytes()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 5
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
/**
4
 * The cryptographically secure pseudo-random generator class.
5
 */
6
7
namespace CryptoManana\Randomness;
8
9
use CryptoManana\Core\Abstractions\Randomness\AbstractGenerator as RandomnessSource;
10
use CryptoManana\Core\Interfaces\Randomness\SeedableGeneratorInterface as SeedAction;
11
use CryptoManana\Factories\ExceptionFactory as ExceptionFactory;
12
13
/**
14
 * Class CryptoRandom - The cryptographically secure pseudo-random generator object.
15
 *
16
 * @package CryptoManana\Randomness
17
 */
18
class CryptoRandom extends RandomnessSource implements SeedAction
19
{
20
    /**
21
     * The initialization seed value property storage for all instances.
22
     *
23
     * @var bool|int The generator's seed value.
24
     */
25
    protected static $seed = false;
26
27
    /**
28
     * Internal static method for single point consumption of the randomness source that outputs integers.
29
     *
30
     * @param int $minimum The lowest value to be returned.
31
     * @param int $maximum The highest value to be returned.
32
     *
33
     * @return int Randomly generated integer number.
34
     * @throws \Exception If no suitable random source is found.
35
     */
36 14
    protected static function getInteger($minimum, $maximum)
37
    {
38 14
        return random_int($minimum, $maximum);
39
    }
40
41
    /**
42
     * Internal static method for single point consumption of the randomness source that outputs bytes.
43
     *
44
     * @param int $count The output string length based on the requested number of bytes.
45
     *
46
     * @return string Randomly generated string containing the requested number of bytes.
47
     * @throws \Exception If no suitable random source is found.
48
     */
49 17
    protected static function getEightBits($count)
50
    {
51 17
        return random_bytes($count);
52
    }
53
54
    /**
55
     * The maximum supported integer.
56
     *
57
     * @return int The upper integer generation border.
58
     */
59 22
    public function getMaxNumber()
60
    {
61 22
        return PHP_INT_MAX;
62
    }
63
64
    /**
65
     * The minimum supported integer.
66
     *
67
     * @return int The lower integer generation border.
68
     */
69 23
    public function getMinNumber()
70
    {
71 23
        return PHP_INT_MIN;
72
    }
73
74
    /**
75
     * Cryptographically secure pseudo-random generator constructor.
76
     *
77
     * Note: This type of generator does not support initialization seeding and is auto-seeded.
78
     *
79
     * @note Consumes the high-entropy source a few times on the first object creation.
80
     */
81 114
    public function __construct()
82
    {
83 114
        parent::__construct();
84
85 114
        if (self::$seed === false) {
86 1
            self::setSeed();
87
        }
88
    }
89
90
    /**
91
     * Get debug information for the class instance.
92
     *
93
     * @return array Debug information.
94
     */
95 1
    public function __debugInfo()
96
    {
97 1
        return [
98 1
            'systemPrecision' => self::$systemPrecision,
99 1
            'seed' => 'NOT SUPPORTED',
100 1
        ];
101
    }
102
103
    /**
104
     * Invoking the auto-seeding of the generator via source consumption.
105
     *
106
     * @param null|int $seed Auto-seeding.
107
     *
108
     * @throws \Exception|\CryptoManana\Exceptions\CryptographyException On seed with other value than `null`.
109
     *
110
     * @note This type of generator does not support controllable seeding.
111
     */
112 6
    public static function setSeed($seed = null)
113
    {
114 6
        if (!is_null($seed)) {
115 1
            $exception = ExceptionFactory::createInstance(
116 1
                ExceptionFactory::CRYPTOGRAPHY_PROBLEM
117 1
            );
118
119 1
            $message = 'Cryptographic pseudo-random data generators do not support seeding!';
120
121 1
            throw $exception->setMessage($message)->setFile(__FILE__)->setLine(__LINE__);
122
        } else {
123
            // Get time information
124 6
            list($microSeconds, $seconds) = explode(' ', microtime());
125
126
            // Get microseconds as integer first
127 6
            $seed = (int)($microSeconds * 1000000) - 1;
128
129
            // A 32bit integer overflow  workaround for the UNIX timestamp format
130 6
            $time = (PHP_MAJOR_VERSION === 5) ? abs($seconds - $seed) : $seconds + $seed;
131
132
            // Auto seeding by via consuming the pool to skip a 8-64 bytes with milliseconds delay
133 6
            for ($i = 1; $i <= 1 + ($time % 8); $i++) {
134 6
                $tmpOne = self::getEightBits(8);
135
            }
136
137
            // Explicit cleanup for delay, because `usleep(1)` can create process hogging
138 6
            unset($tmpOne);
139
140
            // Mark as seeded to seed only once on object creation
141 6
            self::$seed = true;
142
        }
143
    }
144
145
    /**
146
     * Generate a random integer number in a certain range.
147
     *
148
     * Note: Passing `null` will use the default parameter value.
149
     *
150
     * @param null|int $from The lowest value to be returned (default => 0).
151
     * @param null|int $to The highest value to be returned (default => $this->getMaxNumber()).
152
     *
153
     * @return int Randomly generated integer number.
154
     * @throws \Exception Validation errors.
155
     */
156 19
    public function getInt($from = 0, $to = null)
157
    {
158 19
        $from = ($from === null) ? 0 : $from;
159 19
        $to = ($to === null) ? $this->getMaxNumber() : $to;
160
161 19
        $this->validateIntegerRange($from, $to);
162
163 14
        return self::getInteger($from, $to);
164
    }
165
166
    /**
167
     * Generate a random byte string.
168
     *
169
     * Note: PHP represents bytes as characters to make byte strings.
170
     *
171
     * @param int $length The output string length (default => 1).
172
     *
173
     * @return string Randomly generated string containing the requested number of bytes.
174
     * @throws \Exception Validation errors.
175
     */
176 12
    public function getBytes($length = 1)
177
    {
178 12
        $this->validatePositiveInteger($length);
179
180 11
        return self::getEightBits($length);
181
    }
182
}
183