Failed Conditions
Push — v7 ( a687dc...e264c8 )
by Florent
02:28
created

NumberTheory   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 233
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
wmc 29
lcom 1
cbo 2
dl 0
loc 233
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B polynomialReduceMod() 0 31 6
B polynomialMultiplyMod() 0 29 4
B polynomialPowMod() 0 36 6
C squareRootModP() 0 93 12
1
<?php
2
3
namespace Jose\Component\Core\Util\Ecc\Math;
4
5
/***********************************************************************
6
     * Copyright (C) 2012 Matyas Danter
7
     *
8
     * Permission is hereby granted, free of charge, to any person obtaining
9
     * a copy of this software and associated documentation files (the "Software"),
10
     * to deal in the Software without restriction, including without limitation
11
     * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12
     * and/or sell copies of the Software, and to permit persons to whom the
13
     * Software is furnished to do so, subject to the following conditions:
14
     *
15
     * The above copyright notice and this permission notice shall be included
16
     * in all copies or substantial portions of the Software.
17
     *
18
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19
     * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21
     * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
22
     * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23
     * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24
     * OTHER DEALINGS IN THE SOFTWARE.
25
     ************************************************************************/
26
27
/**
28
 * Implementation of some number theoretic algorithms
29
 *
30
 * @author Matyas Danter
31
 */
32
33
/**
34
 * Rewritten to take a MathAdaptor to handle different environments. Has
35
 * some desireable functions for public key compression/recovery.
36
 */
37
final class NumberTheory
38
{
39
    /**
40
     * @var GmpMath
41
     */
42
    protected $adapter;
43
44
    /**
45
     * @param GmpMath $adapter
46
     */
47
    public function __construct(GmpMath $adapter)
48
    {
49
        $this->adapter = $adapter;
50
    }
51
52
    /**
53
     * @param \GMP[] $poly
54
     * @param $polymod
55
     * @param $p
56
     * @return array
57
     */
58
    public function polynomialReduceMod($poly, $polymod, $p): array
59
    {
60
        $adapter = $this->adapter;
61
        $count_polymod = count($polymod);
62
        if ($adapter->equals(end($polymod), gmp_init(1)) && $count_polymod > 1) {
63
            $zero = gmp_init(0);
64
            while (count($poly) >= $count_polymod) {
65
                if (!$adapter->equals(end($poly), $zero)) {
66
                    for ($i = 2; $i < $count_polymod + 1; $i++) {
67
                        $poly[count($poly) - $i] =
68
                            $adapter->mod(
69
                                $adapter->sub(
70
                                    $poly[count($poly) - $i],
71
                                    $adapter->mul(
72
                                        end($poly),
73
                                        $polymod[$count_polymod - $i]
74
                                    )
75
                                ),
76
                                $p
77
                            );
78
                    }
79
                }
80
81
                $poly = array_slice($poly, 0, count($poly) - 1);
82
            }
83
84
            return $poly;
85
        }
86
87
        throw new \InvalidArgumentException('Unable to calculate polynomialReduceMod');
88
    }
89
90
    /**
91
     * @param $m1
92
     * @param $m2
93
     * @param $polymod
94
     * @param $p
95
     * @return array
96
     */
97
    public function polynomialMultiplyMod($m1, $m2, $polymod, $p): array
98
    {
99
        $prod = array();
100
        $cm1 = count($m1);
101
        $cm2 = count($m2);
102
        $zero = gmp_init(0, 10);
103
104
        for ($i = 0; $i < $cm1; $i++) {
105
            for ($j = 0; $j < $cm2; $j++) {
106
                $index = $i + $j;
107
                if (!isset($prod[$index])) {
108
                    $prod[$index] = $zero;
109
                }
110
                $prod[$index] =
111
                    $this->adapter->mod(
112
                        $this->adapter->add(
113
                            $prod[$index],
114
                            $this->adapter->mul(
115
                                $m1[$i],
116
                                $m2[$j]
117
                            )
118
                        ),
119
                        $p
120
                    );
121
            }
122
        }
123
124
        return $this->polynomialReduceMod($prod, $polymod, $p);
125
    }
126
127
    /**
128
     * @param array $base
129
     * @param \GMP $exponent
130
     * @param array $polymod
131
     * @param \GMP $p
132
     * @return array|int
133
     */
134
    public function polynomialPowMod($base, \GMP $exponent, $polymod, \GMP $p)
135
    {
136
        $adapter = $this->adapter;
137
        $zero = gmp_init(0, 10);
138
        $one = gmp_init(1, 10);
139
        $two = gmp_init(2, 10);
140
141
        if ($adapter->cmp($exponent, $p) < 0) {
142
            if ($adapter->equals($exponent, $zero)) {
143
                return $one;
144
            }
145
146
            $G = $base;
147
            $k = $exponent;
148
149
            if ($adapter->equals($adapter->mod($k, $two), $one)) {
150
                $s = $G;
151
            } else {
152
                $s = array($one);
153
            }
154
155
            while ($adapter->cmp($k, $one) > 0) {
156
                $k = $adapter->div($k, $two);
157
158
                $G = $this->polynomialMultiplyMod($G, $G, $polymod, $p);
159
                if ($adapter->equals($adapter->mod($k, $two), $one)) {
160
                    $s = $this->polynomialMultiplyMod($G, $s, $polymod, $p);
161
                }
162
            }
163
164
            return $s;
165
        }
166
167
        throw new \InvalidArgumentException('Unable to calculate polynomialPowMod');
168
169
    }
170
171
    /**
172
     * @param \GMP $a
173
     * @param \GMP $p
174
     * @return \GMP
175
     */
176
    public function squareRootModP(\GMP $a, \GMP $p)
177
    {
178
        $math = $this->adapter;
179
        $zero = gmp_init(0, 10);
180
        $one = gmp_init(1, 10);
181
        $two = gmp_init(2, 10);
182
        $four = gmp_init(4, 10);
183
        $eight = gmp_init(8, 10);
184
185
        $modMath = $math->getModularArithmetic($p);
186
        if ($math->cmp($one, $p) < 0) {
187
            if ($math->equals($a, $zero)) {
188
                return $zero;
189
            }
190
191
            if ($math->equals($p, $two)) {
192
                return $a;
193
            }
194
195
            $jac = $math->jacobi($a, $p);
196
            if ($jac == -1) {
197
                throw new \LogicException($math->toString($a)." has no square root modulo ".$math->toString($p));
198
            }
199
200
            if ($math->equals($math->mod($p, $four), gmp_init(3, 10))) {
201
                return $modMath->pow($a, $math->div($math->add($p, $one), $four));
202
            }
203
204
            if ($math->equals($math->mod($p, $eight), gmp_init(5, 10))) {
205
                $d = $modMath->pow($a, $math->div($math->sub($p, $one), $four));
206
                if ($math->equals($d, $one)) {
207
                    return $modMath->pow($a, $math->div($math->add($p, gmp_init(3, 10)), $eight));
208
                }
209
210
                if ($math->equals($d, $math->sub($p, $one))) {
211
                    return $modMath->mul(
212
                        $math->mul(
213
                            $two,
214
                            $a
215
                        ),
216
                        $modMath->pow(
217
                            $math->mul(
218
                                $four,
219
                                $a
220
                            ),
221
                            $math->div(
222
                                $math->sub(
223
                                    $p,
224
                                    gmp_init(5, 10)
225
                                ),
226
                                $eight
227
                            )
228
                        )
229
                    );
230
                }
231
                //shouldn't get here
232
            }
233
234
            for ($b = gmp_init(2, 10); $math->cmp($b, $p) < 0; $b = gmp_add($b, gmp_init(1, 10))) {
1 ignored issue
show
Bug introduced by
It seems like $b defined by gmp_add($b, gmp_init(1, 10)) on line 234 can also be of type resource; however, Jose\Component\Core\Util\Ecc\Math\GmpMath::cmp() does only seem to accept object<GMP>, 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...
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
235
                if ($math->jacobi(
236
                    $math->sub(
237
                        $math->mul($b, $b),
1 ignored issue
show
Bug introduced by
It seems like $b defined by gmp_add($b, gmp_init(1, 10)) on line 234 can also be of type resource; however, Jose\Component\Core\Util\Ecc\Math\GmpMath::mul() does only seem to accept object<GMP>, 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...
238
                        $math->mul($four, $a)
239
                    ),
240
                    $p
241
                ) == -1
242
                ) {
243
                    $f = array($a, $math->sub($zero, $b), $one);
1 ignored issue
show
Bug introduced by
It seems like $b defined by gmp_add($b, gmp_init(1, 10)) on line 234 can also be of type resource; however, Jose\Component\Core\Util\Ecc\Math\GmpMath::sub() does only seem to accept object<GMP>, 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...
244
245
                    $ff = $this->polynomialPowMod(
246
                        array($zero, $one),
247
                        $math->div(
248
                            $math->add(
249
                                $p,
250
                                $one
251
                            ),
252
                            $two
253
                        ),
254
                        $f,
255
                        $p
256
                    );
257
258
                    if ($math->equals($ff[1], $zero)) {
259
                        return $ff[0];
260
                    }
261
                    // if we got here no b was found
262
                }
263
            }
264
        }
265
266
        throw new \InvalidArgumentException('Unable to calculate square root mod p!');
267
268
    }
269
}
270