Failed Conditions
Push — PHPSecLib_Rid ( ab345c...1a2f9f )
by Florent
10:59
created

src/Util/BigInteger.php (2 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
final class BigInteger
15
{
16
    /**
17
     * Holds the BigInteger's value.
18
     *
19
     * @var resource
20
     */
21
    private $value;
22
23
    /**
24
     * Converts base-10 and binary strings (base-256) to BigIntegers.
25
     *
26
     * @param $x base-10 number or base-$base number if $base set.
27
     * @param int $base
28
     */
29
    public function __construct($x = 0, $base = 10)
30
    {
31
        if(is_resource($x) && get_resource_type($x) == 'GMP integer') {
32
            $this->value = $x;
33
            
34
            return;
35
        }
36
        
37
        $this->value = gmp_init(0);
38
39
        // '0' counts as empty() but when the base is 256 '0' is equal to ord('0') or 48
40
        // '0' is the only value like this per http://php.net/empty
41
        if (empty($x) && (abs($base) !== 256 || $x !== '0')) {
42
            return;
43
        }
44
45
        if (256 === $base) {
46
            $x = '0x'.bin2hex($x);
47
            $base = 16;
48
        }
49
        $this->value = gmp_init($x, $base);
0 ignored issues
show
Documentation Bug introduced by
It seems like gmp_init($x, $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...
50
    }
51
52
    /**
53
     * Converts a BigInteger to a byte string (eg. base-256).
54
     *
55
     * @return string
56
     */
57
    public function toBytes()
58
    {
59
        if (gmp_cmp($this->value, gmp_init(0)) === 0) {
60
            return '';
61
        }
62
63
        $temp = gmp_strval(gmp_abs($this->value), 16);
64
        $temp = (strlen($temp) & 1) ? '0'.$temp : $temp;
65
        $temp = hex2bin($temp);
66
67
        return ltrim($temp, chr(0));
68
    }
69
70
    /**
71
     * Adds two BigIntegers.
72
     *
73
     * @param \Jose\Util\BigInteger $y
74
     *
75
     * @return \Jose\Util\BigInteger
76
     */
77
    public function add(BigInteger $y)
78
    {
79
        $value = gmp_add($this->value, $y->value);
80
        
81
        return new static($value);
82
    }
83
84
    /**
85
     * Subtracts two BigIntegers.
86
     *
87
     * @param \Jose\Util\BigInteger $y
88
     *
89
     * @return \Jose\Util\BigInteger
90
     */
91
    public function subtract(BigInteger $y)
92
    {
93
        $value = gmp_sub($this->value, $y->value);
0 ignored issues
show
$value is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
94
        
95
        return new static();
96
    }
97
98
    /**
99
     * Multiplies two BigIntegers.
100
     *
101
     * @param \Jose\Util\BigInteger $x
102
     *
103
     * @return \Jose\Util\BigInteger
104
     */
105
    public function multiply(BigInteger $x)
106
    {
107
        $value = gmp_mul($this->value, $x->value);
108
        
109
        return new static($value);
110
    }
111
112
    /**
113
     * Divides two BigIntegers.
114
     * 
115
     * @param \Jose\Util\BigInteger $y
116
     *
117
     * @return \Jose\Util\BigInteger[]
118
     */
119
    public function divide(BigInteger $y)
120
    {
121
        list($quotient_value, $remainder_value) = gmp_div_qr($this->value, $y->value);
122
        
123
        $quotient = new static($quotient_value);
124
        $remainder = new static($remainder_value);
125
126
        if (gmp_sign($remainder->value) < 0) {
127
            $remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
128
        }
129
130
        return [$quotient, $remainder];
131
    }
132
133
    /**
134
     * Performs modular exponentiation.
135
     *
136
     * @param \Jose\Util\BigInteger $e
137
     * @param \Jose\Util\BigInteger $n
138
     *
139
     * @return \Jose\Util\BigInteger
140
     */
141
    public function modPow(BigInteger $e, BigInteger $n)
142
    {
143
        $n = $n->abs();
144
145
        if ($e->compare(new static()) < 0) {
146
            $e = $e->abs();
147
148
            $temp = $this->modInverse($n);
149
            if ($temp === false) {
150
                return false;
151
            }
152
153
            return $temp->modPow($e, $n);
154
        }
155
156
        $value = gmp_powm($this->value, $e->value, $n->value);
157
        
158
        return new static($value);
159
    }
160
161
    /**
162
     * Calculates modular inverses.
163
     *
164
     * @param \Jose\Util\BigInteger $n
165
     *
166
     * @return \Jose\Util\BigInteger|bool
167
     */
168
    public function modInverse(BigInteger $n)
169
    {
170
        $value = gmp_invert($this->value, $n->value);
171
        
172
        return false === $value ? false : new static($value);
173
    }
174
175
    /**
176
     * Absolute value.
177
     *
178
     * @return \Jose\Util\BigInteger
179
     */
180
    public function abs()
181
    {
182
        $value = gmp_abs($this->value);
183
        
184
        return new static($value);
185
    }
186
187
    /**
188
     * Compares two numbers.
189
     *
190
     * @param \Jose\Util\BigInteger $y
191
     *
192
     * @return int < 0 if $this is less than $y; > 0 if $this is greater than $y, and 0 if they are equal.
193
     */
194
    public function compare(BigInteger $y)
195
    {
196
        return gmp_cmp($this->value, $y->value);
197
    }
198
199
    /**
200
     * Logical Left Shift.
201
     *
202
     * @param int $shift
203
     *
204
     * @return \Jose\Util\BigInteger
205
     *
206
     */
207
    public function bitwise_leftShift($shift)
208
    {
209
        $two = gmp_init('2');
210
        $value = gmp_mul($this->value, gmp_pow($two, $shift));
211
        
212
        return new static($value);
213
    }
214
215
    /**
216
     * Generates a random BigInteger.
217
     *
218
     * Byte length is equal to $length. Uses \phpseclib\Crypt\Random if it's loaded and mt_rand if it's not.
219
     *
220
     * @param int $size
221
     *
222
     * @return \Jose\Util\BigInteger
223
     */
224
    private static function _random_number_helper($size)
225
    {
226
        return new static(random_bytes($size), 256);
227
    }
228
229
    /**
230
     * Generate a random number.
231
     *
232
     * @param \Jose\Util\BigInteger $min
233
     * @param \Jose\Util\BigInteger $max
234
     *
235
     * @return \Jose\Util\BigInteger
236
     */
237
    public static function random(BigInteger $min, BigInteger $max)
238
    {
239
        $compare = $max->compare($min);
240
241
        if (!$compare) {
242
            return $min;
243
        } elseif ($compare < 0) {
244
            $temp = $max;
245
            $max = $min;
246
            $min = $temp;
247
        }
248
249
        $one = new static('1');
250
251
        $max = $max->subtract($min->subtract($one));
252
        $size = strlen(ltrim($max->toBytes(), chr(0)));
253
        
254
        $random_max = new static(chr(1).str_repeat("\0", $size), 256);
255
        $random = self::_random_number_helper($size);
256
257
        list($max_multiple) = $random_max->divide($max);
258
        $max_multiple = $max_multiple->multiply($max);
259
260
        while ($random->compare($max_multiple) >= 0) {
261
            $random = $random->subtract($max_multiple);
262
            $random_max = $random_max->subtract($max_multiple);
263
            $random = $random->bitwise_leftShift(8);
264
            $random = $random->add(self::_random_number_helper(1));
265
            $random_max = $random_max->bitwise_leftShift(8);
266
            list($max_multiple) = $random_max->divide($max);
267
            $max_multiple = $max_multiple->multiply($max);
268
        }
269
        list(, $random) = $random->divide($max);
270
271
        return $random->add($min);
272
    }
273
    
274
}
275