Passed
Branch master (df288f)
by Tony Karavasilev (Тони
02:30
created

CryptoRandom::getInt()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 4
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 8
ccs 5
cts 5
cp 1
crap 3
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 24
    protected static function getInteger($minimum, $maximum)
37
    {
38 24
        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 20
    protected static function getEightBits($count)
50
    {
51 20
        return random_bytes($count);
52
    }
53
54
    /**
55
     * The maximum supported integer.
56
     *
57
     * @return int The upper integer generation border.
58
     */
59 40
    public function getMaxNumber()
60
    {
61 40
        return PHP_INT_MAX;
62
    }
63
64
    /**
65
     * The minimum supported integer.
66
     *
67
     * @return int The lower integer generation border.
68
     */
69 42
    public function getMinNumber()
70
    {
71 42
        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
     * @internal Consumes the high-entropy source a few times on the first object creation.
80
     */
81 86
    public function __construct()
82
    {
83 86
        parent::__construct();
84
85 86
        if (self::$seed === false) {
86 2
            self::setSeed();
87
        }
88 86
    }
89
90
    /**
91
     * Invoking the auto-seeding of the generator via source consumption.
92
     *
93
     * @param null|int $seed Auto-seeding.
94
     *
95
     * @throws \Exception|\CryptoManana\Exceptions\CryptographyException On seed with other value than `null`.
96
     * @internal This type of generator does not support controllable seeding.
97
     */
98 10
    public static function setSeed($seed = null)
99
    {
100 10
        if (!is_null($seed)) {
101 2
            $exception = ExceptionFactory::createInstance(
102 2
                ExceptionFactory::CRYPTOGRAPHY_PROBLEM
103
            );
104
105 2
            $message = 'Cryptographic pseudo-random data generators do not support seeding!';
106
107 2
            throw $exception->setMessage($message)->setFile(__FILE__)->setLine(__LINE__);
108
        } else {
109
            // Get time information
110 10
            list($microSeconds, $seconds) = explode(' ', microtime());
111
112
            // Get microseconds as integer first
113 10
            $seed = (int)($microSeconds * 1000000) - 1;
114
115
            // A 32bit integer overflow  workaround for the UNIX timestamp format
116 10
            $time = (PHP_MAJOR_VERSION === 5) ? abs($seconds - $seed) : $seconds + $seed;
117
118
            // Auto seeding by via consuming the pool to skip a 8-64 bytes with milliseconds delay
119 10
            for ($i = 1; $i <= 1 + ($time % 8); $i++) {
120 10
                $tmpOne = self::getEightBits(8);
121
            }
122
123
            // Explicit cleanup for delay, because `usleep(1)` can create process hogging
124 10
            unset($tmpOne);
125
126
            // Mark as seeded to seed only once on object creation
127 10
            self::$seed = true;
128
        }
129 10
    }
130
131
    /**
132
     * Generate a random integer number in a certain range.
133
     *
134
     * Note: Passing `null` will use the default parameter value.
135
     *
136
     * @param null|int $from The lowest value to be returned (default => 0).
137
     * @param null|int $to The highest value to be returned (default => $this->getMaxNumber()).
138
     *
139
     * @return int Randomly generated integer number.
140
     * @throws \Exception Validation errors.
141
     */
142 34
    public function getInt($from = 0, $to = null)
143
    {
144 34
        $from = ($from === null) ? 0 : $from;
145 34
        $to = ($to === null) ? $this->getMaxNumber() : $to;
146
147 34
        $this->validateIntegerRange($from, $to);
148
149 24
        return self::getInteger($from, $to);
150
    }
151
152
    /**
153
     * Generate a random byte string.
154
     *
155
     * Note: PHP represents bytes as characters to make byte strings.
156
     *
157
     * @param int $length The output string length (default => 1).
158
     *
159
     * @return string Randomly generated string containing the requested number of bytes.
160
     * @throws \Exception Validation errors.
161
     */
162 12
    public function getBytes($length = 1)
163
    {
164 12
        $this->validatePositiveInteger($length);
165
166 10
        return self::getEightBits($length);
167
    }
168
169
    /**
170
     * Get debug information for the class instance.
171
     *
172
     * @return array Debug information.
173
     */
174 2
    public function __debugInfo()
175
    {
176
        return [
177 2
            'systemPrecision' => self::$systemPrecision,
178 2
            'seed' => 'NOT SUPPORTED',
179
        ];
180
    }
181
}
182