Failed Conditions
Push — v7 ( 3de5c8...73ebba )
by Florent
01:54
created

Curve::getPoint()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.2
c 0
b 0
f 0
cc 4
eloc 9
nc 4
nop 3
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\Encryption\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 ('.Math::toString($x).', '.Math::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
        $zero = gmp_init(0, 10);
132
        if (Math::cmp($x, $zero) < 0 || Math::cmp($this->generator->getOrder(), $x) <= 0 || Math::cmp($y, $zero) < 0 || Math::cmp($this->generator->getOrder(), $y) <= 0)
133
        {
134
            throw new \RuntimeException('Generator point has x and y out of range.');
135
        }
136
        $point = $this->getPoint($x, $y);
137
138
        return PublicKey::create($point);
139
    }
140
141
    /**
142
     * @param \GMP $x
143
     * @param \GMP $y
144
     *
145
     * @return bool
146
     */
147
    public function contains(\GMP $x, \GMP $y): bool
148
    {
149
        $eq_zero = Math::equals(
150
            ModularArithmetic::sub(
151
                Math::pow($y, 2),
152
                Math::add(
153
                    Math::add(
154
                        Math::pow($x, 3),
155
                        Math::mul($this->getA(), $x)
156
                    ),
157
                    $this->getB()
158
                ),
159
                $this->getPrime()
160
            ),
161
            gmp_init(0, 10)
162
        );
163
164
        return $eq_zero;
165
    }
166
167
    /**
168
     * @param Point $one
169
     * @param Point $two
170
     *
171
     * @return Point
172
     */
173
    public function add(Point $one, Point $two): Point
174
    {
175
        if ($two->isInfinity()) {
176
            return clone $one;
177
        }
178
179
        if ($one->isInfinity()) {
180
            return clone $two;
181
        }
182
183
        if (Math::equals($two->getX(), $one->getX())) {
184
            if (Math::equals($two->getY(), $one->getY())) {
185
                return $this->getDouble($one);
186
            } else {
187
                return Point::infinity();
188
            }
189
        }
190
191
        $slope = ModularArithmetic::div(
192
            Math::sub($two->getY(), $one->getY()),
193
            Math::sub($two->getX(), $one->getX()),
194
            $this->getPrime()
195
        );
196
197
        $xR = ModularArithmetic::sub(
198
            Math::sub(Math::pow($slope, 2), $one->getX()),
199
            $two->getX(),
200
            $this->getPrime()
201
        );
202
203
        $yR = ModularArithmetic::sub(
204
            Math::mul($slope, Math::sub($one->getX(), $xR)),
205
            $one->getY(),
206
            $this->getPrime()
207
        );
208
209
        return $this->getPoint($xR, $yR, $one->getOrder());
210
    }
211
212
    /**
213
     * @param Point $one
214
     * @param \GMP  $n
215
     *
216
     * @return Point
217
     */
218
    public function mul(Point $one, \GMP $n): Point
219
    {
220
        if ($one->isInfinity()) {
221
            return Point::infinity();
222
        }
223
224
        /** @var \GMP $zero */
225
        $zero = gmp_init(0, 10);
226
        if (Math::cmp($one->getOrder(), $zero) > 0) {
227
            $n = Math::mod($n, $one->getOrder());
228
        }
229
230
        if (Math::equals($n, $zero)) {
231
            return Point::infinity();
232
        }
233
234
        /** @var Point[] $r */
235
        $r = [
236
            Point::infinity(),
237
            clone $one,
238
        ];
239
240
        $k = $this->getSize();
241
        $n = str_pad(Math::baseConvert(Math::toString($n), 10, 2), $k, '0', STR_PAD_LEFT);
242
243
        for ($i = 0; $i < $k; ++$i) {
244
            $j = $n[$i];
245
            Point::cswap($r[0], $r[1], $j ^ 1);
246
            $r[0] = $this->add($r[0], $r[1]);
247
            $r[1] = $this->getDouble($r[1]);
248
            Point::cswap($r[0], $r[1], $j ^ 1);
249
        }
250
251
        $this->validate($r[0]);
252
253
        return $r[0];
254
    }
255
256
    /**
257
     * @param Curve $other
258
     *
259
     * @return int
260
     */
261
    public function cmp(Curve $other): int
262
    {
263
        $equal = Math::equals($this->getA(), $other->getA());
264
        $equal &= Math::equals($this->getB(), $other->getB());
265
        $equal &= Math::equals($this->getPrime(), $other->getPrime());
266
267
        return $equal ? 0 : 1;
268
    }
269
270
    /**
271
     * @param Curve $other
272
     *
273
     * @return bool
274
     */
275
    public function equals(Curve $other): bool
276
    {
277
        return $this->cmp($other) === 0;
278
    }
279
280
    /**
281
     * @return string
282
     */
283
    public function __toString(): string
284
    {
285
        return 'curve('.Math::toString($this->getA()).', '.Math::toString($this->getB()).', '.Math::toString($this->getPrime()).')';
286
    }
287
288
    /**
289
     * @param Point $point
290
     */
291
    private function validate(Point $point)
292
    {
293
        if (!$point->isInfinity() && !$this->contains($point->getX(), $point->getY())) {
294
            throw new \RuntimeException('Invalid point');
295
        }
296
    }
297
298
    /**
299
     * @param Point $point
300
     *
301
     * @return Point
302
     */
303
    public function getDouble(Point $point): Point
304
    {
305
        if ($point->isInfinity()) {
306
            return Point::infinity();
307
        }
308
309
        $a = $this->getA();
310
        $threeX2 = Math::mul(gmp_init(3, 10), Math::pow($point->getX(), 2));
311
312
        $tangent = ModularArithmetic::div(
313
            Math::add($threeX2, $a),
314
            Math::mul(gmp_init(2, 10), $point->getY()),
315
            $this->getPrime()
316
        );
317
318
        $x3 = ModularArithmetic::sub(
319
            Math::pow($tangent, 2),
320
            Math::mul(gmp_init(2, 10), $point->getX()),
321
            $this->getPrime()
322
        );
323
324
        $y3 = ModularArithmetic::sub(
325
            Math::mul($tangent, Math::sub($point->getX(), $x3)),
326
            $point->getY(),
327
            $this->getPrime()
328
        );
329
330
        return $this->getPoint($x3, $y3, $point->getOrder());
331
    }
332
}
333