Failed Conditions
Push — PHPSecLib_Rid ( 2881a4...c6c82d )
by Florent
04:30
created

src/Util/BigInteger.php (4 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
class BigInteger
15
{
16
    const MONTGOMERY = 0;
17
18
    const BARRETT = 1;
19
20
    const POWEROF2 = 2;
21
22
    const CLASSIC = 3;
23
24
    const NONE = 4;
25
26
    /**#@+
27
     * Array constants
28
     *
29
     * Rather than create a thousands and thousands of new BigInteger objects in repeated function calls to add() and
30
     * multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
31
     *
32
     */
33
    /**
34
     * $result[self::VALUE] contains the value.
35
     */
36
    const VALUE = 0;
37
    /**
38
     * $result[self::SIGN] contains the sign.
39
     */
40
    const SIGN = 1;
41
    /**#@-*/
42
43
    /**#@+
44
     */
45
    /**
46
     * Cache constants.
47
     *
48
     * $cache[self::VARIABLE] tells us whether or not the cached data is still valid.
49
     */
50
    const VARIABLE = 0;
51
    /**
52
     * $cache[self::DATA] contains the cached data.
53
     */
54
    const DATA = 1;
55
    /**#@-*/
56
57
    /**
58
     * Karatsuba Cutoff.
59
     *
60
     * At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
61
     */
62
    const KARATSUBA_CUTOFF = 25;
63
64
    /**#@+
65
     * Static properties used by the pure-PHP implementation.
66
     *
67
     * @see __construct()
68
     */
69
    protected static $base;
70
    protected static $baseFull;
71
    protected static $maxDigit;
72
    protected static $msb;
73
74
    /**
75
     * $max10 in greatest $max10Len satisfying
76
     * $max10 = 10**$max10Len <= 2**$base.
77
     */
78
    protected static $max10;
79
80
    /**
81
     * $max10Len in greatest $max10Len satisfying
82
     * $max10 = 10**$max10Len <= 2**$base.
83
     */
84
    protected static $max10Len;
85
    protected static $maxDigit2;
86
    /**#@-*/
87
88
    /**
89
     * Holds the BigInteger's value.
90
     *
91
     * @var resource
92
     */
93
    private $value;
94
95
    /**
96
     * Holds the BigInteger's magnitude.
97
     *
98
     * @var bool
99
     */
100
    private $is_negative = false;
101
102
    /**
103
     * Precision.
104
     */
105
    private $precision = -1;
106
107
    /**
108
     * Precision Bitmask.
109
     */
110
    private $bitmask = false;
111
112
    /**
113
     * Converts base-2, base-10, base-16, and binary strings (base-256) to BigIntegers.
114
     *
115
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
116
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
117
     *
118
     * Here's an example:
119
     * <code>
120
     * <?php
121
     *    $a = new \Jose\Util\in base-16
122
     *
123
     *    echo $a->toString(); // outputs 50
124
     * ?>
125
     * </code>
126
     *
127
     * @param $x base-10 number or base-$base number if $base set.
128
     * @param int $base
129
     */
130
    public function __construct($x = 0, $base = 10)
131
    {
132
        switch (true) {
133
            case is_resource($x) && get_resource_type($x) == 'GMP integer':
134
                // PHP 5.6 switched GMP from using resources to objects
135
            case $x instanceof \GMP:
136
                $this->value = $x;
137
138
                return;
139
        }
140
        $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...
141
142
        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
143
        // '0' is the only value like this per http://php.net/empty
144
        if (empty($x) && (abs($base) != 256 || $x !== '0')) {
145
            return;
146
        }
147
148
        switch ($base) {
149
            case -256:
150
                if (ord($x[0]) & 0x80) {
151
                    $x = ~$x;
152
                    $this->is_negative = true;
153
                }
154
            case 256:
155
                $sign = $this->is_negative ? '-' : '';
156
                $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...
157
158
                if ($this->is_negative) {
159
                    $this->is_negative = false;
160
                    $temp = $this->add(new static('-1'));
161
                    $this->value = $temp->value;
162
                }
163
                break;
164
            case 16:
165
            case -16:
166
                if ($base > 0 && $x[0] == '-') {
167
                    $this->is_negative = true;
168
                    $x = substr($x, 1);
169
                }
170
171
                $x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
172
173
                $is_negative = false;
174
                if ($base < 0 && hexdec($x[0]) >= 8) {
175
                    $this->is_negative = $is_negative = true;
176
                    $x = bin2hex(~hex2bin($x));
177
                }
178
179
            $temp = $this->is_negative ? '-0x'.$x : '0x'.$x;
180
            $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...
181
            $this->is_negative = false;
182
183
                if ($is_negative) {
184
                    $temp = $this->add(new static('-1'));
185
                    $this->value = $temp->value;
186
                }
187
                break;
188
            case 10:
189
            case -10:
190
                // (?<!^)(?:-).*: find any -'s that aren't at the beginning and then any characters that follow that
191
                // (?<=^|-)0*: find any 0's that are preceded by the start of the string or by a - (ie. octals)
192
                // [^-0-9].*: find any non-numeric characters and then any characters that follow that
193
                $x = preg_replace('#(?<!^)(?:-).*|(?<=^|-)0*|[^-0-9].*#', '', $x);
194
195
                $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...
196
                break;
197
            case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
198
            case -2:
199
                if ($base > 0 && $x[0] == '-') {
200
                    $this->is_negative = true;
201
                    $x = substr($x, 1);
202
                }
203
204
                $x = preg_replace('#^([01]*).*#', '$1', $x);
205
                $x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
206
207
                $str = '0x';
208
                while (strlen($x)) {
209
                    $part = substr($x, 0, 4);
210
                    $str .= dechex(bindec($part));
211
                    $x = substr($x, 4);
212
                }
213
214
                if ($this->is_negative) {
215
                    $str = '-'.$str;
216
                }
217
218
                $temp = new static($str, 8 * $base); // ie. either -16 or +16
219
                $this->value = $temp->value;
220
                $this->is_negative = $temp->is_negative;
221
222
                break;
223
            default:
224
                // base not supported, so we'll let $this == 0
225
        }
226
    }
227
228
    /**
229
     * Converts a BigInteger to a byte string (eg. base-256).
230
     *
231
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
232
     * saved as two's compliment.
233
     *
234
     * Here's an example:
235
     * <code>
236
     * <?php
237
     *    $a = new \Jose\Util\ger('65');
238
     *
239
     *    echo $a->toBytes(); // outputs chr(65)
240
     * ?>
241
     * </code>
242
     *
243
     * @param bool $twos_compliment
244
     *
245
     * @return string
246
     *
247
     */
248
    public function toBytes($twos_compliment = false)
249
    {
250
        if ($twos_compliment) {
251
            $comparison = $this->compare(new static());
252
            if ($comparison == 0) {
253
                return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
254
            }
255
256
            $temp = $comparison < 0 ? $this->add(new static(1)) : $this;
257
            $bytes = $temp->toBytes();
258
259
            if (empty($bytes)) { // eg. if the number we're trying to convert is -1
260
                $bytes = chr(0);
261
            }
262
263
            if (ord($bytes[0]) & 0x80) {
264
                $bytes = chr(0).$bytes;
265
            }
266
267
            return $comparison < 0 ? ~$bytes : $bytes;
268
        }
269
270
        if (gmp_cmp($this->value, gmp_init(0)) == 0) {
271
            return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
272
        }
273
274
        $temp = gmp_strval(gmp_abs($this->value), 16);
275
        $temp = (strlen($temp) & 1) ? '0'.$temp : $temp;
276
        $temp = hex2bin($temp);
277
278
        return $this->precision > 0 ?
279
            substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
280
            ltrim($temp, chr(0));
281
    }
282
283
    /**
284
     * Adds two BigIntegers.
285
     *
286
     * Here's an example:
287
     * <code>
288
     * <?php
289
     *    $a = new \Jose\Util\ger('10');
290
     *    $b = new \Jose\Util\ger('20');
291
     *
292
     *    $c = $a->add($b);
293
     *
294
     *    echo $c->toString(); // outputs 30
295
     * ?>
296
     * </code>
297
     *
298
     * @param \Jose\Util\Integer $y
299
     *
300
     * @return \Jose\Util\BigInteger
301
     *
302
     */
303
    public function add(BigInteger $y)
304
    {
305
        $temp = new static();
306
        $temp->value = gmp_add($this->value, $y->value);
307
308
        return $this->_normalize($temp);
309
    }
310
311
    /**
312
     * Subtracts two BigIntegers.
313
     *
314
     * Here's an example:
315
     * <code>
316
     * <?php
317
     *    $a = new \Jose\Util\ger('10');
318
     *    $b = new \Jose\Util\ger('20');
319
     *
320
     *    $c = $a->subtract($b);
321
     *
322
     *    echo $c->toString(); // outputs -10
323
     * ?>
324
     * </code>
325
     *
326
     * @param \Jose\Util\Integer $y
327
     *
328
     * @return \Jose\Util\BigInteger
329
     *
330
     */
331
    public function subtract(BigInteger $y)
332
    {
333
        $temp = new static();
334
        $temp->value = gmp_sub($this->value, $y->value);
335
336
        return $this->_normalize($temp);
337
    }
338
339
    /**
340
     * Multiplies two BigIntegers.
341
     *
342
     * Here's an example:
343
     * <code>
344
     * <?php
345
     *    $a = new \Jose\Util\ger('10');
346
     *    $b = new \Jose\Util\ger('20');
347
     *
348
     *    $c = $a->multiply($b);
349
     *
350
     *    echo $c->toString(); // outputs 200
351
     * ?>
352
     * </code>
353
     *
354
     * @param \Jose\Util\Integer $x
355
     *
356
     * @return \Jose\Util\BigInteger
357
     */
358
    public function multiply(BigInteger $x)
359
    {
360
        $temp = new static();
361
        $temp->value = gmp_mul($this->value, $x->value);
362
363
        return $this->_normalize($temp);
364
    }
365
366
    /**
367
     * Divides two BigIntegers.
368
     *
369
     * Returns an array whose first element contains the quotient and whose second element contains the
370
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
371
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
372
     * and the divisor (basically, the "common residue" is the first positive modulo).
373
     *
374
     * Here's an example:
375
     * <code>
376
     * <?php
377
     *    $a = new \Jose\Util\ger('10');
378
     *    $b = new \Jose\Util\ger('20');
379
     *
380
     *    list($quotient, $remainder) = $a->divide($b);
381
     *
382
     *    echo $quotient->toString(); // outputs 0
383
     *    echo "\r\n";
384
     *    echo $remainder->toString(); // outputs 10
385
     * ?>
386
     * </code>
387
     *
388
     * @param \Jose\Util\Integer $y
389
     *
390
     * @return array
391
     *
392
     */
393
    public function divide(BigInteger $y)
394
    {
395
        $quotient = new static();
396
        $remainder = new static();
397
398
        list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
399
400
        if (gmp_sign($remainder->value) < 0) {
401
            $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
402
        }
403
404
        return [$this->_normalize($quotient), $this->_normalize($remainder)];
405
    }
406
407
    /**
408
     * Performs modular exponentiation.
409
     *
410
     * Here's an example:
411
     * <code>
412
     * <?php
413
     *    $a = new \Jose\Util\ger('10');
414
     *    $b = new \Jose\Util\ger('20');
415
     *    $c = new \Jose\Util\ger('30');
416
     *
417
     *    $c = $a->modPow($b, $c);
418
     *
419
     *    echo $c->toString(); // outputs 10
420
     * ?>
421
     * </code>
422
     *
423
     * @param \Jose\Util\Integer $e
424
     * @param \Jose\Util\Integer $n
425
     *
426
     * @return \Jose\Util\BigInteger
427
     *
428
     *    and although the approach involving repeated squaring does vastly better, it, too, is impractical
429
     *    for our purposes.  The reason being that division - by far the most complicated and time-consuming
430
     *    of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
431
     *
432
     *    Modular reductions resolve this issue.  Although an individual modular reduction takes more time
433
     *    then an individual division, when performed in succession (with the same modulo), they're a lot faster.
434
     *
435
     *    The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
436
     *    although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
437
     *    base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
438
     *    the product of two odd numbers is odd), but what about when RSA isn't used?
439
     *
440
     *    In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
441
     *    Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
442
     *    modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
443
     *    uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
444
     *    the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
445
     *    {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
446
     */
447
    public function modPow(BigInteger $e, BigInteger $n)
448
    {
449
        $n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
450
451
        if ($e->compare(new static()) < 0) {
452
            $e = $e->abs();
453
454
            $temp = $this->modInverse($n);
455
            if ($temp === false) {
456
                return false;
457
            }
458
459
            return $this->_normalize($temp->modPow($e, $n));
460
        }
461
462
            $temp = new static();
463
            $temp->value = gmp_powm($this->value, $e->value, $n->value);
464
465
            return $this->_normalize($temp);
466
    }
467
468
    /**
469
     * Calculates modular inverses.
470
     *
471
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
472
     *
473
     * Here's an example:
474
     * <code>
475
     * <?php
476
     *    $a = new \Jose\Util\teger(30);
477
     *    $b = new \Jose\Util\teger(17);
478
     *
479
     *    $c = $a->modInverse($b);
480
     *    echo $c->toString(); // outputs 4
481
     *
482
     *    echo "\r\n";
483
     *
484
     *    $d = $a->multiply($c);
485
     *    list(, $d) = $d->divide($b);
486
     *    echo $d; // outputs 1 (as per the definition of modular inverse)
487
     * ?>
488
     * </code>
489
     *
490
     * @param \Jose\Util\Integer $n
491
     *
492
     * @return \Jose\Util\eger|false
493
     *
494
     */
495
    public function modInverse(BigInteger $n)
496
    {
497
        $temp = new static();
498
        $temp->value = gmp_invert($this->value, $n->value);
499
500
        return ($temp->value === false) ? false : $this->_normalize($temp);
501
    }
502
503
    /**
504
     * Absolute value.
505
     *
506
     * @return \Jose\Util\BigInteger
507
     */
508
    public function abs()
509
    {
510
        $temp = new static();
511
512
        $temp->value = gmp_abs($this->value);
513
514
        return $temp;
515
    }
516
517
    /**
518
     * Compares two numbers.
519
     *
520
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
521
     * demonstrated thusly:
522
     *
523
     * $x  > $y: $x->compare($y)  > 0
524
     * $x  < $y: $x->compare($y)  < 0
525
     * $x == $y: $x->compare($y) == 0
526
     *
527
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
528
     *
529
     * @param \Jose\Util\Integer $y
530
     *
531
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
532
     *
533
     */
534
    public function compare(BigInteger $y)
535
    {
536
        return gmp_cmp($this->value, $y->value);
537
    }
538
539
    /**
540
     * Logical Left Shift.
541
     *
542
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
543
     *
544
     * @param int $shift
545
     *
546
     * @return \Jose\Util\BigInteger
547
     *
548
     */
549
    public function bitwise_leftShift($shift)
550
    {
551
        $temp = new static();
552
553
        static $two;
554
555
        if (!isset($two)) {
556
            $two = gmp_init('2');
557
        }
558
559
        $temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
560
561
        return $this->_normalize($temp);
562
    }
563
564
    /**
565
     * Generates a random BigInteger.
566
     *
567
     * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
568
     *
569
     * @param int $size
570
     *
571
     * @return \Jose\Util\BigInteger
572
     */
573
    private static function _random_number_helper($size)
574
    {
575
        return new static(random_bytes($size), 256);
576
    }
577
578
    /**
579
     * Generate a random number.
580
     *
581
     * Returns a random number between $min and $max where $min and $max
582
     * can be defined using one of the two methods:
583
     *
584
     * BigInteger::random($min, $max)
585
     * BigInteger::random($max, $min)
586
     *
587
     * @param \Jose\Util\BigInteger $min
588
     * @param \Jose\Util\BigInteger $max
589
     *
590
     * @return \Jose\Util\BigInteger
591
     */
592
    public static function random(BigInteger $min, BigInteger $max)
593
    {
594
        $compare = $max->compare($min);
595
596
        if (!$compare) {
597
            return $this->_normalize($min);
598
        } elseif ($compare < 0) {
599
            // if $min is bigger then $max, swap $min and $max
600
            $temp = $max;
601
            $max = $min;
602
            $min = $temp;
603
        }
604
605
        static $one;
606
        if (!isset($one)) {
607
            $one = new static(1);
608
        }
609
610
        $max = $max->subtract($min->subtract($one));
611
        $size = strlen(ltrim($max->toBytes(), chr(0)));
612
613
        /*
614
            doing $random % $max doesn't work because some numbers will be more likely to occur than others.
615
            eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
616
            would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
617
            not all numbers would be equally likely. some would be more likely than others.
618
619
            creating a whole new random number until you find one that is within the range doesn't work
620
            because, for sufficiently small ranges, the likelihood that you'd get a number within that range
621
            would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
622
            would be pretty high that $random would be greater than $max.
623
624
            phpseclib works around this using the technique described here:
625
626
            http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
627
        */
628
        $random_max = new static(chr(1).str_repeat("\0", $size), 256);
629
        $random = self::_random_number_helper($size);
630
631
        list($max_multiple) = $random_max->divide($max);
632
        $max_multiple = $max_multiple->multiply($max);
633
634
        while ($random->compare($max_multiple) >= 0) {
635
            $random = $random->subtract($max_multiple);
636
            $random_max = $random_max->subtract($max_multiple);
637
            $random = $random->bitwise_leftShift(8);
638
            $random = $random->add(self::_random_number_helper(1));
639
            $random_max = $random_max->bitwise_leftShift(8);
640
            list($max_multiple) = $random_max->divide($max);
641
            $max_multiple = $max_multiple->multiply($max);
642
        }
643
        list(, $random) = $random->divide($max);
644
645
        return $random->add($min);
646
    }
647
648
    /**
649
     * Normalize.
650
     *
651
     * Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
652
     *
653
     * @param \Jose\Util\BigInteger
654
     *
655
     * @return \Jose\Util\BigInteger
656
     */
657
    private function _normalize($result)
658
    {
659
        $result->precision = $this->precision;
660
        $result->bitmask = $this->bitmask;
661
662
        if ($this->bitmask !== false) {
663
            $result->value = gmp_and($result->value, $result->bitmask->value);
664
        }
665
666
        return $result;
667
    }
668
}
669