Failed Conditions
Push — PHPSecLib_Rid ( fd1804...32d4bc )
by Florent
06:54 queued 01:57
created

BigInteger::toBytes()   C

Complexity

Conditions 12
Paths 24

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
c 3
b 1
f 0
dl 0
loc 34
rs 5.1612
cc 12
eloc 20
nc 24
nop 1

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2016 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace Jose\Util;
13
14
final class BigInteger
15
{
16
    /**#@+
17
     * Array constants
18
     *
19
     * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
20
     * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
21
     *
22
     */
23
    /**
24
     * $result[self::VALUE] contains the value.
25
     */
26
    const VALUE = 0;
27
    /**
28
     * $result[self::SIGN] contains the sign.
29
     */
30
    const SIGN = 1;
31
    /**#@-*/
32
33
    /**#@+
34
     * Static properties used by the pure-PHP implementation.
35
     *
36
     * @see __construct()
37
     */
38
    private static $base;
0 ignored issues
show
Unused Code introduced by
The property $base is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
39
    private static $baseFull;
0 ignored issues
show
Unused Code introduced by
The property $baseFull is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
40
    private static $maxDigit;
0 ignored issues
show
Unused Code introduced by
The property $maxDigit is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
41
    private static $msb;
0 ignored issues
show
Unused Code introduced by
The property $msb is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
42
43
    /**
44
     * $max10 in greatest $max10Len satisfying
45
     * $max10 = 10**$max10Len <= 2**$base.
46
     */
47
    private static $max10;
0 ignored issues
show
Unused Code introduced by
The property $max10 is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
49
    /**
50
     * $max10Len in greatest $max10Len satisfying
51
     * $max10 = 10**$max10Len <= 2**$base.
52
     */
53
    private static $max10Len;
0 ignored issues
show
Unused Code introduced by
The property $max10Len is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
54
    private static $maxDigit2;
0 ignored issues
show
Unused Code introduced by
The property $maxDigit2 is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
55
    /**#@-*/
56
57
    /**
58
     * Holds the BigInteger's value.
59
     *
60
     * @var resource
61
     */
62
    private $value;
63
64
    /**
65
     * Holds the BigInteger's magnitude.
66
     *
67
     * @var bool
68
     */
69
    private $is_negative = false;
70
71
    /**
72
     * Precision.
73
     */
74
    private $precision = -1;
75
76
    /**
77
     * Precision Bitmask.
78
     */
79
    private $bitmask = false;
80
81
    /**
82
     * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
83
     *
84
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
85
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
86
     *
87
     * Here's an example:
88
     * <code>
89
     * <?php
90
     *    $a = new \Jose\Util\in base-16
91
     *
92
     *    echo $a->toString(); // outputs 50
93
     * ?>
94
     * </code>
95
     *
96
     * @param $x base-10 number or base-$base number if $base set.
97
     * @param int $base
98
     */
99
    public function __construct($x = 0, $base = 10)
100
    {
101
        switch (true) {
102
            case is_resource($x) && get_resource_type($x) == 'GMP integer':
103
                // PHP 5.6 switched GMP from using resources to objects
104
            case $x instanceof \GMP:
0 ignored issues
show
Bug introduced by
The class GMP does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
105
                $this->value = $x;
106
107
                return;
108
        }
109
        $this->value = gmp_init(0);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init(0) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
110
111
        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
112
        // '0' is the only value like this per http://php.net/empty
113
        if (empty($x) && (abs($base) != 256 || $x !== '0')) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $x (integer) and '0' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
114
            return;
115
        }
116
117
        switch ($base) {
118
            case -256:
119
                if (ord($x[0]) & 0x80) {
120
                    $x = ~$x;
121
                    $this->is_negative = true;
122
                }
123
            case 256:
124
                $sign = $this->is_negative ? '-' : '';
125
                $this->value = gmp_init($sign.'0x'.bin2hex($x));
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($sign . '0x' . bin2hex($x)) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
126
127
                if ($this->is_negative) {
128
                    $this->is_negative = false;
129
                    $temp = $this->add(new static('-1'));
130
                    $this->value = $temp->value;
131
                }
132
                break;
133
            case 16:
134
            case -16:
135
                if ($base > 0 && $x[0] == '-') {
136
                    $this->is_negative = true;
137
                    $x = substr($x, 1);
138
                }
139
140
                $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
141
142
                $is_negative = false;
143
                if ($base < 0 && hexdec($x[0]) >= 8) {
144
                    $this->is_negative = $is_negative = true;
145
                    $x = bin2hex(~hex2bin($x));
146
                }
147
148
            $temp = $this->is_negative ? '-0x'.$x : '0x'.$x;
149
            $this->value = gmp_init($temp);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($temp) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
150
            $this->is_negative = false;
151
152
                if ($is_negative) {
153
                    $temp = $this->add(new static('-1'));
154
                    $this->value = $temp->value;
155
                }
156
                break;
157
            case 10:
158
            case -10:
159
                // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
160
                // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
161
                // [^-0-9].*: find any non-numeric characters and then any characters that follow that
162
                $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
163
164
                $this->value = gmp_init($x);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($x) of type object<GMP> is incompatible with the declared type resource of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
165
                break;
166
            case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
167
            case -2:
168
                if ($base > 0 && $x[0] == '-') {
169
                    $this->is_negative = true;
170
                    $x = substr($x, 1);
171
                }
172
173
                $x = preg_replace('#^([01]*).*#', '$1', $x);
174
                $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
175
176
                $str = '0x';
177
                while (strlen($x)) {
178
                    $part = substr($x, 0, 4);
179
                    $str .= dechex(bindec($part));
180
                    $x = substr($x, 4);
181
                }
182
183
                if ($this->is_negative) {
184
                    $str = '-'.$str;
185
                }
186
187
                $temp = new static($str, 8 * $base); // ie. either -16 or +16
188
                $this->value = $temp->value;
189
                $this->is_negative = $temp->is_negative;
190
191
                break;
192
            default:
193
                // base not supported, so we'll let $this == 0
194
        }
195
    }
196
197
    /**
198
     * Converts a BigInteger to a byte string (eg. base-256).
199
     *
200
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
201
     * saved as two's compliment.
202
     *
203
     * Here's an example:
204
     * <code>
205
     * <?php
206
     *    $a = new \Jose\Util\ger('65');
207
     *
208
     *    echo $a->toBytes(); // outputs chr(65)
209
     * ?>
210
     * </code>
211
     *
212
     * @param bool $twos_compliment
213
     *
214
     * @return string
215
     *
216
     */
217
    public function toBytes($twos_compliment = false)
218
    {
219
        if ($twos_compliment) {
220
            $comparison = $this->compare(new static());
221
            if ($comparison == 0) {
222
                return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
223
            }
224
225
            $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
226
            $bytes = $temp->toBytes();
227
228
            if (empty($bytes)) { // eg. if the number we're trying to convert is -1
229
                $bytes = chr(0);
230
            }
231
232
            if (ord($bytes[0]) & 0x80) {
233
                $bytes = chr(0).$bytes;
234
            }
235
236
            return $comparison < 0 ? ~$bytes : $bytes;
237
        }
238
239
        if (gmp_cmp($this->value, gmp_init(0)) == 0) {
240
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
241
        }
242
243
        $temp = gmp_strval(gmp_abs($this->value), 16);
244
        $temp = (strlen($temp) & 1) ? '0'.$temp : $temp;
245
        $temp = hex2bin($temp);
246
247
        return $this->precision > 0 ?
248
            substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
249
            ltrim($temp, chr(0));
250
    }
251
252
    /**
253
     * Adds two BigIntegers.
254
     *
255
     * Here's an example:
256
     * <code>
257
     * <?php
258
     *    $a = new \Jose\Util\ger('10');
259
     *    $b = new \Jose\Util\ger('20');
260
     *
261
     *    $c = $a->add($b);
262
     *
263
     *    echo $c->toString(); // outputs 30
264
     * ?>
265
     * </code>
266
     *
267
     * @param \Jose\Util\BigInteger $y
268
     *
269
     * @return \Jose\Util\BigInteger
270
     *
271
     */
272
    public function add(BigInteger $y)
273
    {
274
        $temp = new static();
275
        $temp->value = gmp_add($this->value, $y->value);
276
277
        return $this->_normalize($temp);
278
    }
279
280
    /**
281
     * Subtracts two BigIntegers.
282
     *
283
     * Here's an example:
284
     * <code>
285
     * <?php
286
     *    $a = new \Jose\Util\ger('10');
287
     *    $b = new \Jose\Util\ger('20');
288
     *
289
     *    $c = $a->subtract($b);
290
     *
291
     *    echo $c->toString(); // outputs -10
292
     * ?>
293
     * </code>
294
     *
295
     * @param \Jose\Util\BigInteger $y
296
     *
297
     * @return \Jose\Util\BigInteger
298
     *
299
     */
300
    public function subtract(BigInteger $y)
301
    {
302
        $temp = new static();
303
        $temp->value = gmp_sub($this->value, $y->value);
304
305
        return $this->_normalize($temp);
306
    }
307
308
    /**
309
     * Multiplies two BigIntegers.
310
     *
311
     * Here's an example:
312
     * <code>
313
     * <?php
314
     *    $a = new \Jose\Util\ger('10');
315
     *    $b = new \Jose\Util\ger('20');
316
     *
317
     *    $c = $a->multiply($b);
318
     *
319
     *    echo $c->toString(); // outputs 200
320
     * ?>
321
     * </code>
322
     *
323
     * @param \Jose\Util\BigInteger $x
324
     *
325
     * @return \Jose\Util\BigInteger
326
     */
327
    public function multiply(BigInteger $x)
328
    {
329
        $temp = new static();
330
        $temp->value = gmp_mul($this->value, $x->value);
331
332
        return $this->_normalize($temp);
333
    }
334
335
    /**
336
     * Divides two BigIntegers.
337
     *
338
     * Returns an array whose first element contains the quotient and whose second element contains the
339
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
340
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
341
     * and the divisor (basically, the "common residue" is the first positive modulo).
342
     *
343
     * Here's an example:
344
     * <code>
345
     * <?php
346
     *    $a = new \Jose\Util\ger('10');
347
     *    $b = new \Jose\Util\ger('20');
348
     *
349
     *    list($quotient, $remainder) = $a->divide($b);
350
     *
351
     *    echo $quotient->toString(); // outputs 0
352
     *    echo "\r\n";
353
     *    echo $remainder->toString(); // outputs 10
354
     * ?>
355
     * </code>
356
     *
357
     * @param \Jose\Util\BigInteger $y
358
     *
359
     * @return @return \Jose\Util\BigInteger[]
0 ignored issues
show
Documentation introduced by
The doc-type @return could not be parsed: Unknown type name "@return" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
360
     *
361
     */
362
    public function divide(BigInteger $y)
363
    {
364
        $quotient = new static();
365
        $remainder = new static();
366
367
        list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
368
369
        if (gmp_sign($remainder->value) < 0) {
370
            $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
371
        }
372
373
        return [$this->_normalize($quotient), $this->_normalize($remainder)];
374
    }
375
376
    /**
377
     * Performs modular exponentiation.
378
     *
379
     * Here's an example:
380
     * <code>
381
     * <?php
382
     *    $a = new \Jose\Util\ger('10');
383
     *    $b = new \Jose\Util\ger('20');
384
     *    $c = new \Jose\Util\ger('30');
385
     *
386
     *    $c = $a->modPow($b, $c);
387
     *
388
     *    echo $c->toString(); // outputs 10
389
     * ?>
390
     * </code>
391
     *
392
     * @param \Jose\Util\BigInteger $e
393
     * @param \Jose\Util\BigInteger $n
394
     *
395
     * @return \Jose\Util\BigInteger
396
     *
397
     *    and although the approach involving repeated squaring does vastly better, it, too, is impractical
398
     *    for our purposes.  The reason being that division - by far the most complicated and time-consuming
399
     *    of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
400
     *
401
     *    Modular reductions resolve this issue.  Although an individual modular reduction takes more time
402
     *    then an individual division, when performed in succession (with the same modulo), they're a lot faster.
403
     *
404
     *    The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
405
     *    although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
406
     *    base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
407
     *    the product of two odd numbers is odd), but what about when RSA isn't used?
408
     *
409
     *    In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
410
     *    Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
411
     *    modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
412
     *    uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
413
     *    the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
414
     *    {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
415
     */
416
    public function modPow(BigInteger $e, BigInteger $n)
417
    {
418
        $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
0 ignored issues
show
Bug introduced by
The method compare cannot be called on $this->bitmask (of type boolean).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
419
420
        if ($e->compare(new static()) < 0) {
421
            $e = $e->abs();
422
423
            $temp = $this->modInverse($n);
0 ignored issues
show
Bug introduced by
It seems like $n defined by $this->bitmask !== false...is->bitmask : $n->abs() on line 418 can also be of type boolean; however, Jose\Util\BigInteger::modInverse() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
424
            if ($temp === false) {
425
                return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by Jose\Util\BigInteger::modPow of type Jose\Util\BigInteger.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
426
            }
427
428
            return $this->_normalize($temp->modPow($e, $n));
0 ignored issues
show
Bug introduced by
It seems like $n defined by $this->bitmask !== false...is->bitmask : $n->abs() on line 418 can also be of type boolean; however, Jose\Util\BigInteger::modPow() does only seem to accept object<Jose\Util\BigInteger>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
429
        }
430
431
            $temp = new static();
432
            $temp->value = gmp_powm($this->value, $e->value, $n->value);
433
434
            return $this->_normalize($temp);
435
    }
436
437
    /**
438
     * Calculates modular inverses.
439
     *
440
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
441
     *
442
     * Here's an example:
443
     * <code>
444
     * <?php
445
     *    $a = new \Jose\Util\teger(30);
446
     *    $b = new \Jose\Util\teger(17);
447
     *
448
     *    $c = $a->modInverse($b);
449
     *    echo $c->toString(); // outputs 4
450
     *
451
     *    echo "\r\n";
452
     *
453
     *    $d = $a->multiply($c);
454
     *    list(, $d) = $d->divide($b);
455
     *    echo $d; // outputs 1 (as per the definition of modular inverse)
456
     * ?>
457
     * </code>
458
     *
459
     * @param \Jose\Util\BigInteger $n
460
     *
461
     * @return \Jose\Util\BigInteger|bool
462
     *
463
     */
464
    public function modInverse(BigInteger $n)
465
    {
466
        $temp = new static();
467
        $temp->value = gmp_invert($this->value, $n->value);
468
469
        return ($temp->value === false) ? false : $this->_normalize($temp);
470
    }
471
472
    /**
473
     * Absolute value.
474
     *
475
     * @return \Jose\Util\BigInteger
476
     */
477
    public function abs()
478
    {
479
        $temp = new static();
480
481
        $temp->value = gmp_abs($this->value);
482
483
        return $temp;
484
    }
485
486
    /**
487
     * Compares two numbers.
488
     *
489
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
490
     * demonstrated thusly:
491
     *
492
     * $x  > $y: $x->compare($y)  > 0
493
     * $x  < $y: $x->compare($y)  < 0
494
     * $x == $y: $x->compare($y) == 0
495
     *
496
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
497
     *
498
     * @param \Jose\Util\BigInteger $y
499
     *
500
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
501
     *
502
     */
503
    public function compare(BigInteger $y)
504
    {
505
        return gmp_cmp($this->value, $y->value);
506
    }
507
508
    /**
509
     * Logical Left Shift.
510
     *
511
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
512
     *
513
     * @param int $shift
514
     *
515
     * @return \Jose\Util\BigInteger
516
     *
517
     */
518
    public function bitwise_leftShift($shift)
519
    {
520
        $temp = new static();
521
522
        static $two;
523
524
        if (!isset($two)) {
525
            $two = gmp_init('2');
526
        }
527
528
        $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
529
530
        return $this->_normalize($temp);
531
    }
532
533
    /**
534
     * Generates a random BigInteger.
535
     *
536
     * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
537
     *
538
     * @param int $size
539
     *
540
     * @return \Jose\Util\BigInteger
541
     */
542
    private static function _random_number_helper($size)
543
    {
544
        return new static(random_bytes($size), 256);
545
    }
546
547
    /**
548
     * Generate a random number.
549
     *
550
     * Returns a random number between $min and $max where $min and $max
551
     * can be defined using one of the two methods:
552
     *
553
     * BigInteger::random($min, $max)
554
     * BigInteger::random($max, $min)
555
     *
556
     * @param \Jose\Util\BigInteger $min
557
     * @param \Jose\Util\BigInteger $max
558
     *
559
     * @return \Jose\Util\BigInteger
560
     */
561
    public static function random(BigInteger $min, BigInteger $max)
562
    {
563
        $compare = $max->compare($min);
564
565
        if (!$compare) {
566
            return $this->_normalize($min);
0 ignored issues
show
Bug introduced by
The variable $this does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
Usage of "$this" in static methods will cause runtime errors
Loading history...
567
        } elseif ($compare < 0) {
568
            // if $min is bigger then $max, swap $min and $max
569
            $temp = $max;
570
            $max = $min;
571
            $min = $temp;
572
        }
573
574
        static $one;
575
        if (!isset($one)) {
576
            $one = new static(1);
577
        }
578
579
        $max = $max->subtract($min->subtract($one));
580
        $size = strlen(ltrim($max->toBytes(), chr(0)));
581
582
        /*
583
            doing $random % $max doesn't work because some numbers will be more likely to occur than others.
584
            eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
585
            would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
586
            not all numbers would be equally likely. some would be more likely than others.
587
588
            creating a whole new random number until you find one that is within the range doesn't work
589
            because, for sufficiently small ranges, the likelihood that you'd get a number within that range
590
            would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
591
            would be pretty high that $random would be greater than $max.
592
593
            phpseclib works around this using the technique described here:
594
595
            http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
596
        */
597
        $random_max = new static(chr(1).str_repeat("\0", $size), 256);
598
        $random = self::_random_number_helper($size);
599
600
        list($max_multiple) = $random_max->divide($max);
601
        $max_multiple = $max_multiple->multiply($max);
602
603
        while ($random->compare($max_multiple) >= 0) {
604
            $random = $random->subtract($max_multiple);
605
            $random_max = $random_max->subtract($max_multiple);
606
            $random = $random->bitwise_leftShift(8);
607
            $random = $random->add(self::_random_number_helper(1));
608
            $random_max = $random_max->bitwise_leftShift(8);
609
            list($max_multiple) = $random_max->divide($max);
610
            $max_multiple = $max_multiple->multiply($max);
611
        }
612
        list(, $random) = $random->divide($max);
613
614
        return $random->add($min);
615
    }
616
617
    /**
618
     * Normalize.
619
     *
620
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
621
     *
622
     * @param \Jose\Util\BigInteger $result
623
     *
624
     * @return \Jose\Util\BigInteger
625
     */
626
    private function _normalize($result)
627
    {
628
        $result->precision = $this->precision;
629
        $result->bitmask = $this->bitmask;
630
631
        if ($this->bitmask !== false) {
632
            $result->value = gmp_and($result->value, $result->bitmask->value);
633
        }
634
635
        return $result;
636
    }
637
}
638