Failed Conditions
Push — PHPSecLib_Rid ( 1a2f9f...838957 )
by Florent
05:04
created

src/Util/BigInteger.php (3 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
use Assert\Assertion;
15
16
final class BigInteger
17
{
18
    /**
19
     * Holds the BigInteger's value.
20
     *
21
     * @var resource
22
     */
23
    private $value;
24
25
    /**
26
     * Converts base-10 and binary strings (base-256) to BigIntegers.
27
     *
28
     * If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
29
     * two's compliment.  The sole exception to this is -10, which is treated the same as 10 is.
30
     *
31
     * Here's an example:
32
     * <code>
33
     * <?php
34
     *    $a = new \Jose\Util\in base-16
35
     *
36
     *    echo $a->toString(); // outputs 50
37
     * ?>
38
     * </code>
39
     *
40
     * @param mixed $value    base-10 number or base-$base number if $base set.
41
     * @param int   $base
42
     */
43
    private function __construct($value = 0, $base = 10)
44
    {
45
        if($value instanceof \GMP) {
0 ignored issues
show
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...
46
            $this->value = $value;
47
            
48
            return;
49
        }
50
        
51
        $this->value = gmp_init(0);
52
53
        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
54
        // '0' is the only value like this per http://php.net/empty
55
        if (empty($value) && (abs($base) != 256 || $value !== '0')) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of $value (integer) and '0' (string) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
56
            return;
57
        }
58
59
        if (256 === $base) {
60
            $value = '0x'.bin2hex($value);
61
            $base = 16;
62
        }
63
64
        $this->value = gmp_init($value, $base);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($value, $base) 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...
65
    }
66
67
    /**
68
     * @param \GMP $value
69
     *
70
     * @return \Jose\Util\BigInteger
71
     */
72
    public static function createFromGMPResource($value)
73
    {
74
        Assertion::isInstanceOf($value, \GMP::class);
75
76
        return new self($value);
77
    }
78
79
    /**
80
     * @param string $value
81
     * @param bool   $is_negative
82
     *
83
     * @return \Jose\Util\BigInteger
84
     */
85
    public static function createFromBinaryString($value, $is_negative = false)
86
    {
87
        Assertion::string($value);
88
        $value = '0x'.bin2hex($value);
89
        if (true === $is_negative) {
90
            $value = '-'.$value;
91
        }
92
93
        return new self($value, 16);
94
    }
95
96
    /**
97
     * @param string $value
98
     *
99
     * @return \Jose\Util\BigInteger
100
     */
101
    public static function createFromDecimalString($value)
102
    {
103
        Assertion::string($value);
104
105
        return new self($value, 10);
106
    }
107
108
    /**
109
     * Converts a BigInteger to a byte string (eg. base-256).
110
     *
111
     * Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
112
     * saved as two's compliment.
113
     *
114
     * Here's an example:
115
     * <code>
116
     * <?php
117
     *    $a = new \Jose\Util\ger('65');
118
     *
119
     *    echo $a->toBytes(); // outputs chr(65)
120
     * ?>
121
     * </code>
122
     *
123
     * @return string
124
     *
125
     */
126
    public function toBytes()
127
    {
128
        if (gmp_cmp($this->value, gmp_init(0)) === 0) {
129
            return '';
130
        }
131
132
        $temp = gmp_strval(gmp_abs($this->value), 16);
133
        $temp = (strlen($temp) & 1) ? '0'.$temp : $temp;
134
        $temp = hex2bin($temp);
135
136
        return ltrim($temp, chr(0));
137
    }
138
139
    /**
140
     * Adds two BigIntegers.
141
     *
142
     * Here's an example:
143
     * <code>
144
     * <?php
145
     *    $a = new \Jose\Util\ger('10');
146
     *    $b = new \Jose\Util\ger('20');
147
     *
148
     *    $c = $a->add($b);
149
     *
150
     *    echo $c->toString(); // outputs 30
151
     * ?>
152
     * </code>
153
     *
154
     * @param \Jose\Util\BigInteger $y
155
     *
156
     * @return \Jose\Util\BigInteger
157
     *
158
     */
159
    public function add(BigInteger $y)
160
    {
161
        $value = gmp_add($this->value, $y->value);
162
163
        return self::createFromGMPResource($value);
164
    }
165
166
    /**
167
     * Subtracts two BigIntegers.
168
     *
169
     * Here's an example:
170
     * <code>
171
     * <?php
172
     *    $a = new \Jose\Util\ger('10');
173
     *    $b = new \Jose\Util\ger('20');
174
     *
175
     *    $c = $a->subtract($b);
176
     *
177
     *    echo $c->toString(); // outputs -10
178
     * ?>
179
     * </code>
180
     *
181
     * @param \Jose\Util\BigInteger $y
182
     *
183
     * @return \Jose\Util\BigInteger
184
     *
185
     */
186
    public function subtract(BigInteger $y)
187
    {
188
        $value = gmp_sub($this->value, $y->value);
189
190
        return self::createFromGMPResource($value);
191
    }
192
193
    /**
194
     * Multiplies two BigIntegers.
195
     *
196
     * Here's an example:
197
     * <code>
198
     * <?php
199
     *    $a = new \Jose\Util\ger('10');
200
     *    $b = new \Jose\Util\ger('20');
201
     *
202
     *    $c = $a->multiply($b);
203
     *
204
     *    echo $c->toString(); // outputs 200
205
     * ?>
206
     * </code>
207
     *
208
     * @param \Jose\Util\BigInteger $x
209
     *
210
     * @return \Jose\Util\BigInteger
211
     */
212
    public function multiply(BigInteger $x)
213
    {
214
        $value = gmp_mul($this->value, $x->value);
215
216
        return self::createFromGMPResource($value);
217
    }
218
219
    /**
220
     * Divides two BigIntegers.
221
     *
222
     * Returns an array whose first element contains the quotient and whose second element contains the
223
     * "common residue".  If the remainder would be positive, the "common residue" and the remainder are the
224
     * same.  If the remainder would be negative, the "common residue" is equal to the sum of the remainder
225
     * and the divisor (basically, the "common residue" is the first positive modulo).
226
     *
227
     * Here's an example:
228
     * <code>
229
     * <?php
230
     *    $a = new \Jose\Util\ger('10');
231
     *    $b = new \Jose\Util\ger('20');
232
     *
233
     *    list($quotient, $remainder) = $a->divide($b);
234
     *
235
     *    echo $quotient->toString(); // outputs 0
236
     *    echo "\r\n";
237
     *    echo $remainder->toString(); // outputs 10
238
     * ?>
239
     * </code>
240
     * @param \Jose\Util\BigInteger $y
241
     *
242
     * @return \Jose\Util\BigInteger[]
243
     *
244
     */
245
    public function divide(BigInteger $y)
246
    {
247
        list($quotient_value, $remainder_value) = gmp_div_qr($this->value, $y->value);
248
249
        if (gmp_sign($remainder_value) < 0) {
250
            $remainder_value = gmp_add($remainder_value, gmp_abs($y->value));
251
        }
252
253
        return [new self($quotient_value), new self($remainder_value)];
254
    }
255
256
    /**
257
     * Performs modular exponentiation.
258
     *
259
     * Here's an example:
260
     * <code>
261
     * <?php
262
     *    $a = new \Jose\Util\ger('10');
263
     *    $b = new \Jose\Util\ger('20');
264
     *    $c = new \Jose\Util\ger('30');
265
     *
266
     *    $c = $a->modPow($b, $c);
267
     *
268
     *    echo $c->toString(); // outputs 10
269
     * ?>
270
     * </code>
271
     *
272
     * @param \Jose\Util\BigInteger $e
273
     * @param \Jose\Util\BigInteger $n
274
     *
275
     * @return \Jose\Util\BigInteger|bool
276
     *
277
     *    and although the approach involving repeated squaring does vastly better, it, too, is impractical
278
     *    for our purposes.  The reason being that division - by far the most complicated and time-consuming
279
     *    of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
280
     *
281
     *    Modular reductions resolve this issue.  Although an individual modular reduction takes more time
282
     *    then an individual division, when performed in succession (with the same modulo), they're a lot faster.
283
     *
284
     *    The two most commonly used modular reductions are Barrett and Montgomery reduction.  Montgomery reduction,
285
     *    although faster, only works when the gcd of the modulo and of the base being used is 1.  In RSA, when the
286
     *    base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
287
     *    the product of two odd numbers is odd), but what about when RSA isn't used?
288
     *
289
     *    In contrast, Barrett reduction has no such constraint.  As such, some bigint implementations perform a
290
     *    Barrett reduction after every operation in the modpow function.  Others perform Barrett reductions when the
291
     *    modulo is even and Montgomery reductions when the modulo is odd.  BigInteger.java's modPow method, however,
292
     *    uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
293
     *    the other, a power of two - and recombine them, later.  This is the method that this modPow function uses.
294
     *    {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
295
     */
296
    public function modPow(BigInteger $e, BigInteger $n)
297
    {
298
        $n = $n->abs();
299
300
        if ($e->compare(new self()) < 0) {
301
            $e = $e->abs();
302
303
            $temp = $this->modInverse($n);
304
            if ($temp === false) {
305
                return false;
306
            }
307
308
            return $temp->modPow($e, $n);
309
        }
310
311
        $value = gmp_powm($this->value, $e->value, $n->value);
312
313
        return self::createFromGMPResource($value);
314
    }
315
316
    /**
317
     * Calculates modular inverses.
318
     *
319
     * Say you have (30 mod 17 * x mod 17) mod 17 == 1.  x can be found using modular inverses.
320
     *
321
     * Here's an example:
322
     * <code>
323
     * <?php
324
     *    $a = new \Jose\Util\teger(30);
325
     *    $b = new \Jose\Util\teger(17);
326
     *
327
     *    $c = $a->modInverse($b);
328
     *    echo $c->toString(); // outputs 4
329
     *
330
     *    echo "\r\n";
331
     *
332
     *    $d = $a->multiply($c);
333
     *    list(, $d) = $d->divide($b);
334
     *    echo $d; // outputs 1 (as per the definition of modular inverse)
335
     * ?>
336
     * </code>
337
     *
338
     * @param \Jose\Util\BigInteger $n
339
     *
340
     * @return \Jose\Util\BigInteger|bool
341
     *
342
     */
343
    public function modInverse(BigInteger $n)
344
    {
345
        $value = gmp_invert($this->value, $n->value);
346
347
        return false === $value ? false : new self($value);
348
    }
349
350
    /**
351
     * Absolute value.
352
     *
353
     * @return \Jose\Util\BigInteger
354
     */
355
    public function abs()
356
    {
357
        $value = gmp_abs($this->value);
358
359
        return self::createFromGMPResource($value);
360
    }
361
362
    /**
363
     * Compares two numbers.
364
     *
365
     * Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite.  The reason for this is
366
     * demonstrated thusly:
367
     *
368
     * $x  > $y: $x->compare($y)  > 0
369
     * $x  < $y: $x->compare($y)  < 0
370
     * $x == $y: $x->compare($y) == 0
371
     *
372
     * Note how the same comparison operator is used.  If you want to test for equality, use $x->equals($y).
373
     *
374
     * @param \Jose\Util\BigInteger $y
375
     *
376
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
377
     *
378
     */
379
    public function compare(BigInteger $y)
380
    {
381
        return gmp_cmp($this->value, $y->value);
382
    }
383
384
    /**
385
     * Logical Left Shift.
386
     *
387
     * Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
388
     *
389
     * @param int $shift
390
     *
391
     * @return \Jose\Util\BigInteger
392
     *
393
     */
394
    public function bitwise_leftShift($shift)
395
    {
396
        $two = gmp_init('2');
397
        $value = gmp_mul($this->value, gmp_pow($two, $shift));
398
399
        return self::createFromGMPResource($value);
400
    }
401
402
    /**
403
     * Generates a random BigInteger.
404
     *
405
     * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
406
     *
407
     * @param int $size
408
     *
409
     * @return \Jose\Util\BigInteger
410
     */
411
    private static function _random_number_helper($size)
412
    {
413
        return new self(random_bytes($size), 256);
414
    }
415
416
    /**
417
     * Generate a random number.
418
     *
419
     * Returns a random number between $min and $max where $min and $max
420
     * can be defined using one of the two methods:
421
     *
422
     * BigInteger::random($min, $max)
423
     * BigInteger::random($max, $min)
424
     *
425
     * @param \Jose\Util\BigInteger $min
426
     * @param \Jose\Util\BigInteger $max
427
     *
428
     * @return \Jose\Util\BigInteger
429
     */
430
    public static function random(BigInteger $min, BigInteger $max)
431
    {
432
        $compare = $max->compare($min);
433
434
        if (!$compare) {
435
            return $min;
436
        } elseif ($compare < 0) {
437
            // if $min is bigger then $max, swap $min and $max
438
            $temp = $max;
439
            $max = $min;
440
            $min = $temp;
441
        }
442
443
        $one = new self('1');
444
445
        $max = $max->subtract($min->subtract($one));
446
        $size = strlen(ltrim($max->toBytes(), chr(0)));
447
448
        /*
449
            doing $random % $max doesn't work because some numbers will be more likely to occur than others.
450
            eg. if $max is 140 and $random's max is 255 then that'd mean both $random = 5 and $random = 145
451
            would produce 5 whereas the only value of random that could produce 139 would be 139. ie.
452
            not all numbers would be equally likely. some would be more likely than others.
453
454
            creating a whole new random number until you find one that is within the range doesn't work
455
            because, for sufficiently small ranges, the likelihood that you'd get a number within that range
456
            would be pretty small. eg. with $random's max being 255 and if your $max being 1 the probability
457
            would be pretty high that $random would be greater than $max.
458
459
            phpseclib works around this using the technique described here:
460
461
            http://crypto.stackexchange.com/questions/5708/creating-a-small-number-from-a-cryptographically-secure-random-string
462
        */
463
        $random_max = new self(chr(1).str_repeat("\0", $size), 256);
464
        $random = self::_random_number_helper($size);
465
466
        list($max_multiple) = $random_max->divide($max);
467
        $max_multiple = $max_multiple->multiply($max);
468
469
        while ($random->compare($max_multiple) >= 0) {
470
            $random = $random->subtract($max_multiple);
471
            $random_max = $random_max->subtract($max_multiple);
472
            $random = $random->bitwise_leftShift(8);
473
            $random = $random->add(self::_random_number_helper(1));
474
            $random_max = $random_max->bitwise_leftShift(8);
475
            list($max_multiple) = $random_max->divide($max);
476
            $max_multiple = $max_multiple->multiply($max);
477
        }
478
        list(, $random) = $random->divide($max);
479
480
        return $random->add($min);
481
    }
482
    
483
}
484