Failed Conditions
Push — v7 ( 986b39...d5cb12 )
by Florent
04:01
created

Curve   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 321
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 34
lcom 1
cbo 4
dl 0
loc 321
rs 9.2
c 0
b 0
f 0

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A getA() 0 4 1
A getB() 0 4 1
A getPrime() 0 4 1
A getSize() 0 4 1
A getPoint() 0 15 4
B getPublicKeyFrom() 0 13 5
A contains() 0 19 1
B add() 0 42 5
B mul() 0 40 5
A cmp() 0 8 2
A equals() 0 4 1
A __toString() 0 4 1
A validate() 0 6 3
B getDouble() 0 29 2
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2017 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\Component\Core\Util\Ecc\Primitives;
13
14
use Jose\Component\Core\Util\Ecc\Crypto\Key\PublicKey;
15
use Jose\Component\Core\Util\Ecc\Math\GmpMath;
16
use Jose\Component\Core\Util\Ecc\Math\ModularArithmetic;
17
18
/**
19
 * This class is a representation of an EC over a field modulo a prime number.
20
 *
21
 * Important objectives for this class are:
22
 * - Does the curve contain a point?
23
 * - Comparison of two curves.
24
 */
25
final class Curve
26
{
27
    /**
28
     * Elliptic curve over the field of integers modulo a prime.
29
     *
30
     * @var \GMP
31
     */
32
    private $a;
33
34
    /**
35
     * @var \GMP
36
     */
37
    private $b;
38
39
    /**
40
     * @var \GMP
41
     */
42
    private $prime;
43
44
    /**
45
     * Binary length of keys associated with these curve parameters.
46
     *
47
     * @var int
48
     */
49
    private $size;
50
51
    /**
52
     * @var Point
53
     */
54
    private $generator;
55
56
    /**
57
     * @param int   $size
58
     * @param \GMP  $prime
59
     * @param \GMP  $a
60
     * @param \GMP  $b
61
     * @param Point $generator
62
     */
63
    public function __construct(int $size, \GMP $prime, \GMP $a, \GMP $b, Point $generator)
64
    {
65
        $this->size = $size;
66
        $this->prime = $prime;
67
        $this->a = $a;
68
        $this->b = $b;
69
        $this->generator = $generator;
70
    }
71
72
    /**
73
     * @return \GMP
74
     */
75
    public function getA(): \GMP
76
    {
77
        return $this->a;
78
    }
79
80
    /**
81
     * @return \GMP
82
     */
83
    public function getB(): \GMP
84
    {
85
        return $this->b;
86
    }
87
88
    /**
89
     * @return \GMP
90
     */
91
    public function getPrime(): \GMP
92
    {
93
        return $this->prime;
94
    }
95
96
    /**
97
     * @return int
98
     */
99
    public function getSize(): int
100
    {
101
        return $this->size;
102
    }
103
104
    /**
105
     * @param \GMP      $x
106
     * @param \GMP      $y
107
     * @param \GMP|null $order
108
     *
109
     * @return Point
110
     */
111
    public function getPoint(\GMP $x, \GMP $y, ?\GMP $order = null): Point
112
    {
113
        if (!$this->contains($x, $y)) {
114
            throw new \RuntimeException('Curve '.$this->__toString().' does not contain point ('.GmpMath::toString($x).', '.GmpMath::toString($y).')');
115
        }
116
        $point = Point::create($x, $y, $order);
117
        if (!is_null($order)) {
118
            $mul = $this->mul($point, $order);
119
            if (!$mul->isInfinity()) {
120
                throw new \RuntimeException('SELF * ORDER MUST EQUAL INFINITY. ('.(string) $mul.' found instead)');
121
            }
122
        }
123
124
        return $point;
125
    }
126
127
    /**
128
     * @param \GMP  $x
129
     * @param \GMP  $y
130
     *
131
     * @return PublicKey
132
     */
133
    public function getPublicKeyFrom(\GMP $x, \GMP $y): PublicKey
134
    {
135
        $point = $this->getPoint($x, $y);
136
137
        if (GmpMath::cmp($point->getX(), gmp_init(0, 10)) < 0 || GmpMath::cmp($this->generator->getOrder(), $point->getX()) <= 0
138
            || GmpMath::cmp($point->getY(), gmp_init(0, 10)) < 0 || GmpMath::cmp($this->generator->getOrder(), $point->getY()) <= 0
139
        ) {
140
            throw new \RuntimeException('Generator point has x and y out of range.');
141
        }
142
143
144
        return PublicKey::create($point);
145
    }
146
147
    /**
148
     * @param \GMP $x
149
     * @param \GMP $y
150
     *
151
     * @return bool
152
     */
153
    public function contains(\GMP $x, \GMP $y): bool
154
    {
155
        $eq_zero = GmpMath::equals(
156
            ModularArithmetic::sub(
157
                GmpMath::pow($y, 2),
158
                GmpMath::add(
159
                    GmpMath::add(
160
                        GmpMath::pow($x, 3),
161
                        GmpMath::mul($this->getA(), $x)
162
                    ),
163
                    $this->getB()
164
                ),
165
                $this->getPrime()
166
            ),
167
            gmp_init(0, 10)
168
        );
169
170
        return $eq_zero;
171
    }
172
173
    /**
174
     * @param Point $one
175
     * @param Point $two
176
     *
177
     * @return Point
178
     */
179
    public function add(Point $one, Point $two): Point
180
    {
181
        /*if (!$this->equals($two->getCurve())) {
182
            throw new \RuntimeException('The Elliptic Curves do not match.');
183
        }*/
184
185
        if ($two->isInfinity()) {
186
            return clone $one;
187
        }
188
189
        if ($one->isInfinity()) {
190
            return clone $two;
191
        }
192
193
        if (GmpMath::equals($two->getX(), $one->getX())) {
194
            if (GmpMath::equals($two->getY(), $one->getY())) {
195
                return $this->getDouble($one);
196
            } else {
197
                return Point::infinity();
198
            }
199
        }
200
201
        $slope = ModularArithmetic::div(
202
            GmpMath::sub($two->getY(), $one->getY()),
203
            GmpMath::sub($two->getX(), $one->getX()),
204
            $this->getPrime()
205
        );
206
207
        $xR = ModularArithmetic::sub(
208
            GmpMath::sub(GmpMath::pow($slope, 2), $one->getX()),
209
            $two->getX(),
210
            $this->getPrime()
211
        );
212
213
        $yR = ModularArithmetic::sub(
214
            GmpMath::mul($slope, GmpMath::sub($one->getX(), $xR)),
215
            $one->getY(),
216
            $this->getPrime()
217
        );
218
219
        return $this->getPoint($xR, $yR, $one->getOrder());
220
    }
221
222
    /**
223
     * @param Point $one
224
     * @param \GMP  $n
225
     *
226
     * @return Point
227
     */
228
    public function mul(Point $one, \GMP $n): Point
229
    {
230
        if ($one->isInfinity()) {
231
            return Point::infinity();
232
        }
233
234
        /** @var \GMP $zero */
235
        $zero = gmp_init(0, 10);
236
        if (GmpMath::cmp($one->getOrder(), $zero) > 0) {
237
            $n = GmpMath::mod($n, $one->getOrder());
238
        }
239
240
        if (GmpMath::equals($n, $zero)) {
241
            return Point::infinity();
242
        }
243
244
        /** @var Point[] $r */
245
        $r = [
246
            Point::infinity(),
247
            clone $one,
248
        ];
249
250
        $k = $this->getSize();
251
        $n = str_pad(GmpMath::baseConvert(GmpMath::toString($n), 10, 2), $k, '0', STR_PAD_LEFT);
252
253
        for ($i = 0; $i < $k; ++$i) {
254
            $j = $n[$i];
255
256
            Point::cswap($r[0], $r[1], $j ^ 1);
257
258
            $r[0] = $this->add($r[0],$r[1]);
259
            $r[1] = $this->getDouble($r[1]);
260
261
            Point::cswap($r[0], $r[1], $j ^ 1);
262
        }
263
264
        $this->validate($r[0]);
265
266
        return $r[0];
267
    }
268
269
    /**
270
     * @param Curve $other
271
     *
272
     * @return int
273
     */
274
    public function cmp(Curve $other): int
275
    {
276
        $equal = GmpMath::equals($this->getA(), $other->getA());
277
        $equal &= GmpMath::equals($this->getB(), $other->getB());
278
        $equal &= GmpMath::equals($this->getPrime(), $other->getPrime());
279
280
        return $equal ? 0 : 1;
281
    }
282
283
    /**
284
     * @param Curve $other
285
     *
286
     * @return bool
287
     */
288
    public function equals(Curve $other): bool
289
    {
290
        return $this->cmp($other) === 0;
291
    }
292
293
    /**
294
     * @return string
295
     */
296
    public function __toString(): string
297
    {
298
        return 'curve(' . GmpMath::toString($this->getA()) . ', ' . GmpMath::toString($this->getB()) . ', ' . GmpMath::toString($this->getPrime()) . ')';
299
    }
300
301
    /**
302
     * @param Point $point
303
     */
304
    private function validate(Point $point)
305
    {
306
        if (!$point->isInfinity() && !$this->contains($point->getX(), $point->getY())) {
307
            throw new \RuntimeException('Invalid point');
308
        }
309
    }
310
311
    /**
312
     * @param Point $point
313
     *
314
     * @return Point
315
     */
316
    public function getDouble(Point $point): Point
317
    {
318
        if ($point->isInfinity()) {
319
            return Point::infinity();
320
        }
321
322
        $a = $this->getA();
323
        $threeX2 = GmpMath::mul(gmp_init(3, 10), GmpMath::pow($point->getX(), 2));
324
325
        $tangent = ModularArithmetic::div(
326
            GmpMath::add($threeX2, $a),
327
            GmpMath::mul(gmp_init(2, 10), $point->getY()),
328
            $this->getPrime()
329
        );
330
331
        $x3 = ModularArithmetic::sub(
332
            GmpMath::pow($tangent, 2),
333
            GmpMath::mul(gmp_init(2, 10), $point->getX()),
334
            $this->getPrime()
335
        );
336
337
        $y3 = ModularArithmetic::sub(
338
            GmpMath::mul($tangent, GmpMath::sub($point->getX(), $x3)),
339
            $point->getY(),
340
            $this->getPrime()
341
        );
342
343
        return $this->getPoint($x3, $y3, $point->getOrder());
344
    }
345
}
346