Failed Conditions
Push — v7 ( d5cb12...d36fb1 )
by Florent
02:58
created

Curve::mul()   B

Complexity

Conditions 5
Paths 7

Size

Total Lines 40
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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