Crypt::randomInts32()   A
last analyzed

Complexity

Conditions 4
Paths 1

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
nc 1
nop 4
1
<?php
2
namespace PHPDaemon\Utils;
3
4
use PHPDaemon\Core\CallbackWrapper;
5
use PHPDaemon\Core\Daemon;
6
use PHPDaemon\Core\Debug;
7
use PHPDaemon\FS\FileSystem;
8
9
/**
10
 * Crypt
11
 * @package PHPDaemon\Utils
12
 * @author  Vasily Zorin <[email protected]>
13
 */
14
class Crypt
15
{
16
    use \PHPDaemon\Traits\ClassWatchdog;
17
18
    /**
19
     * Generate keccak hash for string with salt
20
     * @param  string $str Data
21
     * @param  string $salt Salt
22
     * @param  boolean $plain Is plain text?
23
     * @return string
24
     * @deprecated
25
     */
26
    public static function hash($str, $salt = '', $plain = false)
27
    {
28
        $size = 512;
29
        $rounds = 1;
30
        if (strncmp($salt, '$', 1) === 0) {
31
            $e = explode('$', $salt, 3);
32
            $ee = explode('=', $e[1]);
33
            if (ctype_digit($ee[0])) {
34
                $size = (int)$e[1];
35
            }
36
            if (isset($ee[1]) && ctype_digit($e[1])) {
37
                $size = (int)$e[1];
38
            }
39
        }
40
        $hash = $str . $salt;
41
        if ($rounds < 1) {
42
            $rounds = 1;
43
        } elseif ($rounds > 128) {
44
            $rounds = 128;
45
        }
46
        for ($i = 0; $i < $rounds; ++$i) {
47
            $hash = \keccak_hash($hash, $size);
48
        }
49
        if ($plain) {
50
            return $hash;
51
        }
52
        return base64_encode($hash);
53
    }
54
55
    /**
56
     * Returns string of pseudo random characters
57
     * @param  integer $len Length of desired string
0 ignored issues
show
Documentation introduced by
Should the type for parameter $len not be integer|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
58
     * @param  string $chars String of allowed characters
0 ignored issues
show
Documentation introduced by
Should the type for parameter $chars not be string|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
59
     * @param  callable $cb Callback
0 ignored issues
show
Documentation introduced by
Should the type for parameter $cb not be callable|null?

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
60
     * @param  integer $pri Priority of EIO operation
61
     * @param  boolean $hang If true, we shall use /dev/random instead of /dev/urandom and it may cause a delay
62
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
63
     */
64
    public static function randomString($len = null, $chars = null, $cb = null, $pri = 0, $hang = false)
65
    {
66
        if ($len === null) {
67
            $len = 64;
68
        }
69
        if ($chars === null) {
70
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-.';
71
        }
72
        if ($cb === null) {
73
            Daemon::log('[CODE WARN] \\PHPDaemon\\Utils\\Crypt::randomString: non-callback way is not secure.'
74
                . ' Please rewrite your code with callback function in third argument' . PHP_EOL . Debug::backtrace());
75
76
            $r = '';
77
            $m = mb_orig_strlen($chars) - 1;
78
            for ($i = 0; $i < $len; ++$i) {
79
                $r .= $chars[mt_rand(0, $m)];
80
            }
81
            return $r;
82
        }
83
        $charsLen = mb_orig_strlen($chars);
84
        $mask = static::getMinimalBitMask($charsLen - 1);
85
        $iterLimit = max($len, $len * 64);
86
        static::randomInts(2 * $len, function ($ints) use ($cb, $chars, $charsLen, $len, $mask, &$iterLimit) {
87
            if ($ints === false) {
88
                $cb(false);
89
                return;
90
            }
91
            $r = '';
92
            for ($i = 0, $s = sizeof($ints); $i < $s; ++$i) {
93
                // This is wasteful, but RNGs are fast and doing otherwise adds complexity and bias
94
                $c = $ints[$i] & $mask;
95
                // Only use the random number if it is in range, otherwise try another (next iteration)
96
                if ($c < $charsLen) {
97
                    $r .= static::stringIdx($chars, $c);
98
                }
99
                // Guarantee termination
100
                if (--$iterLimit <= 0) {
101
                    return false;
102
                }
103
            }
104
            $d = $len - mb_orig_strlen($r);
105
            if ($d > 0) {
106
                static::randomString($d, $chars, function ($r2) use ($r, $cb) {
107
                    $cb($r . $r2);
108
                });
109
                return;
110
            }
111
            $cb($r);
112
        }, $pri, $hang);
113
    }
114
115
    /**
116
     * Returns the character at index $idx in $str in constant time
117
     * @param  string $str String
118
     * @param  integer $idx Index
119
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be false|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
120
     */
121
    public static function stringIdx($str, $idx)
122
    {
123
        // FIXME: Make the const-time hack below work for all integer sizes, or
124
        // check it properly
125
        $l = mb_orig_strlen($str);
126
        if ($l > 65535 || $idx > $l) {
127
            return false;
128
        }
129
        $r = 0;
130
        for ($i = 0; $i < $l; ++$i) {
131
            $x = $i ^ $idx;
132
            $mask = (((($x | ($x >> 16)) & 0xFFFF) + 0xFFFF) >> 16) - 1;
133
            $r |= ord($str[$i]) & $mask;
134
        }
135
        return chr($r);
136
    }
137
138
    /**
139
     * Returns string of pseudo random bytes
140
     * @param  integer $len Length of desired string
141
     * @param  callable $cb Callback
142
     * @param  integer $pri Priority of EIO operation
143
     * @param  boolean $hang If true, we shall use /dev/random instead of /dev/urandom and it may cause a delay
144
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
145
     */
146
    public static function randomBytes($len, $cb, $pri = 0, $hang = false)
147
    {
148
        $cb = CallbackWrapper::wrap($cb);
149
        FileSystem::open('/dev/' . ($hang ? '' : 'u') . 'random', 'r', function ($file) use ($len, $cb, $pri) {
150
            if (!$file) {
151
                $cb(false);
152
                return;
153
            }
154
            $file->read($len, 0, function ($file, $data) use ($cb) {
155
                $cb($data);
156
            }, $pri);
157
        }, null, $pri);
158
    }
159
160
    /**
161
     * Returns array of pseudo random integers of machine-dependent size
162
     * @param  integer $numInts Number of integers
163
     * @param  callable $cb Callback
164
     * @param  integer $pri Priority of EIO operation
165
     * @param  boolean $hang If true, we shall use /dev/random instead of /dev/urandom and it may cause a delay
166
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
167
     */
168
    public static function randomInts($numInts, $cb, $pri = 0, $hang = false)
169
    {
170
        static::randomBytes(PHP_INT_SIZE * $numInts, function ($bytes) use ($cb, $numInts) {
171
            if ($bytes === false) {
172
                $cb(false);
173
                return;
174
            }
175
            $ints = [];
176
            for ($i = 0; $i < $numInts; ++$i) {
177
                $thisInt = 0;
178
                for ($j = 0; $j < PHP_INT_SIZE; ++$j) {
179
                    $thisInt = ($thisInt << 8) | (ord($bytes[$i * PHP_INT_SIZE + $j]) & 0xFF);
180
                }
181
                // Absolute value in two's compliment (with min int going to zero)
182
                $thisInt = $thisInt & PHP_INT_MAX;
183
                $ints[] = $thisInt;
184
            }
185
            $cb($ints);
186
        }, $pri, $hang);
187
    }
188
189
    /**
190
     * Returns array of pseudo random 32-bit integers
191
     * @param  integer $numInts Number of integers
192
     * @param  callable $cb Callback
193
     * @param  integer $pri Priority of EIO operation
194
     * @param  boolean $hang If true, we shall use /dev/random instead of /dev/urandom and it may cause a delay
195
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be integer|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
196
     */
197
    public static function randomInts32($numInts, $cb, $pri = 0, $hang = false)
198
    {
199
        static::randomBytes(4 * $numInts, function ($bytes) use ($cb, $numInts) {
200
            if ($bytes === false) {
201
                $cb(false);
202
                return;
203
            }
204
            $ints = [];
205
            for ($i = 0; $i < $numInts; ++$i) {
206
                $thisInt = 0;
207
                for ($j = 0; $j < 4; ++$j) {
208
                    $thisInt = ($thisInt << 8) | (ord($bytes[$i * 4 + $j]) & 0xFF);
209
                }
210
                // Absolute value in two's compliment (with min int going to zero)
211
                $thisInt = $thisInt & 0xFFFFFFFF;
212
                $ints[] = $thisInt;
213
            }
214
            $cb($ints);
215
        }, $pri, $hang);
216
    }
217
218
    /**
219
     * Returns the smallest bit mask of all 1s such that ($toRepresent & mask) = $toRepresent
220
     * @param  integer $toRepresent must be an integer greater than or equal to 1
221
     * @return integer
0 ignored issues
show
Documentation introduced by
Should the return type not be false|integer?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
222
     */
223
    protected static function getMinimalBitMask($toRepresent)
224
    {
225
        if ($toRepresent < 1) {
226
            return false;
227
        }
228
        $mask = 0x1;
229
        while ($mask < $toRepresent) {
230
            $mask = ($mask << 1) | 1;
231
        }
232
        return $mask;
233
    }
234
235
    /**
236
     * Compare strings
237
     * @param  string $a String 1
238
     * @param  string $b String 2
239
     * @return boolean    Equal?
240
     * @deprecated
241
     */
242
    public static function compareStrings($a, $b)
243
    {
244
        return hash_equals($a, $b);
245
    }
246
}
247