Subversion Repositories oidplus

Rev

Rev 874 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
827 daniel-mar 1
<?php
2
 
3
/**
4
 * Curves over y^2 + x*y = x^3 + a*x^2 + b
5
 *
6
 * These are curves used in SEC 2 over prime fields: http://www.secg.org/SEC2-Ver-1.0.pdf
7
 * The curve is a weierstrass curve with a[3] and a[2] set to 0.
8
 *
9
 * Uses Jacobian Coordinates for speed if able:
10
 *
11
 * https://en.wikipedia.org/wiki/Jacobian_curve
12
 * https://en.wikibooks.org/wiki/Cryptography/Prime_Curve/Jacobian_Coordinates
13
 *
14
 * PHP version 5 and 7
15
 *
16
 * @author    Jim Wigginton <terrafrost@php.net>
17
 * @copyright 2017 Jim Wigginton
18
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
19
 * @link      http://pear.php.net/package/Math_BigInteger
20
 */
21
 
22
namespace phpseclib3\Crypt\EC\BaseCurves;
23
 
24
use phpseclib3\Math\BigInteger;
25
use phpseclib3\Math\BinaryField;
26
use phpseclib3\Math\BinaryField\Integer as BinaryInteger;
27
 
28
/**
29
 * Curves over y^2 + x*y = x^3 + a*x^2 + b
30
 *
31
 * @author  Jim Wigginton <terrafrost@php.net>
32
 */
33
class Binary extends Base
34
{
35
    /**
36
     * Binary Field Integer factory
37
     *
38
     * @var \phpseclib3\Math\BinaryField
39
     */
40
    protected $factory;
41
 
42
    /**
43
     * Cofficient for x^1
44
     *
45
     * @var object
46
     */
47
    protected $a;
48
 
49
    /**
50
     * Cofficient for x^0
51
     *
52
     * @var object
53
     */
54
    protected $b;
55
 
56
    /**
57
     * Base Point
58
     *
59
     * @var object
60
     */
61
    protected $p;
62
 
63
    /**
64
     * The number one over the specified finite field
65
     *
66
     * @var object
67
     */
68
    protected $one;
69
 
70
    /**
71
     * The modulo
72
     *
73
     * @var BigInteger
74
     */
75
    protected $modulo;
76
 
77
    /**
78
     * The Order
79
     *
80
     * @var BigInteger
81
     */
82
    protected $order;
83
 
84
    /**
85
     * Sets the modulo
86
     */
87
    public function setModulo(...$modulo)
88
    {
89
        $this->modulo = $modulo;
90
        $this->factory = new BinaryField(...$modulo);
91
 
92
        $this->one = $this->factory->newInteger("\1");
93
    }
94
 
95
    /**
96
     * Set coefficients a and b
97
     *
98
     * @param string $a
99
     * @param string $b
100
     */
101
    public function setCoefficients($a, $b)
102
    {
103
        if (!isset($this->factory)) {
104
            throw new \RuntimeException('setModulo needs to be called before this method');
105
        }
106
        $this->a = $this->factory->newInteger(pack('H*', $a));
107
        $this->b = $this->factory->newInteger(pack('H*', $b));
108
    }
109
 
110
    /**
111
     * Set x and y coordinates for the base point
112
     *
113
     * @param string|BinaryInteger $x
114
     * @param string|BinaryInteger $y
115
     */
116
    public function setBasePoint($x, $y)
117
    {
118
        switch (true) {
119
            case !is_string($x) && !$x instanceof BinaryInteger:
120
                throw new \UnexpectedValueException('Argument 1 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer');
121
            case !is_string($y) && !$y instanceof BinaryInteger:
122
                throw new \UnexpectedValueException('Argument 2 passed to Binary::setBasePoint() must be a string or an instance of BinaryField\Integer');
123
        }
124
        if (!isset($this->factory)) {
125
            throw new \RuntimeException('setModulo needs to be called before this method');
126
        }
127
        $this->p = [
128
            is_string($x) ? $this->factory->newInteger(pack('H*', $x)) : $x,
129
            is_string($y) ? $this->factory->newInteger(pack('H*', $y)) : $y
130
        ];
131
    }
132
 
133
    /**
134
     * Retrieve the base point as an array
135
     *
136
     * @return array
137
     */
138
    public function getBasePoint()
139
    {
140
        if (!isset($this->factory)) {
141
            throw new \RuntimeException('setModulo needs to be called before this method');
142
        }
143
        /*
144
        if (!isset($this->p)) {
145
            throw new \RuntimeException('setBasePoint needs to be called before this method');
146
        }
147
        */
148
        return $this->p;
149
    }
150
 
151
    /**
152
     * Adds two points on the curve
153
     *
154
     * @return FiniteField[]
155
     */
156
    public function addPoint(array $p, array $q)
157
    {
158
        if (!isset($this->factory)) {
159
            throw new \RuntimeException('setModulo needs to be called before this method');
160
        }
161
 
162
        if (!count($p) || !count($q)) {
163
            if (count($q)) {
164
                return $q;
165
            }
166
            if (count($p)) {
167
                return $p;
168
            }
169
            return [];
170
        }
171
 
172
        if (!isset($p[2]) || !isset($q[2])) {
173
            throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
174
        }
175
 
176
        if ($p[0]->equals($q[0])) {
177
            return !$p[1]->equals($q[1]) ? [] : $this->doublePoint($p);
178
        }
179
 
180
        // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
181
 
182
        list($x1, $y1, $z1) = $p;
183
        list($x2, $y2, $z2) = $q;
184
 
185
        $o1 = $z1->multiply($z1);
186
        $b = $x2->multiply($o1);
187
 
188
        if ($z2->equals($this->one)) {
189
            $d = $y2->multiply($o1)->multiply($z1);
190
            $e = $x1->add($b);
191
            $f = $y1->add($d);
192
            $z3 = $e->multiply($z1);
193
            $h = $f->multiply($x2)->add($z3->multiply($y2));
194
            $i = $f->add($z3);
195
            $g = $z3->multiply($z3);
196
            $p1 = $this->a->multiply($g);
197
            $p2 = $f->multiply($i);
198
            $p3 = $e->multiply($e)->multiply($e);
199
            $x3 = $p1->add($p2)->add($p3);
200
            $y3 = $i->multiply($x3)->add($g->multiply($h));
201
 
202
            return [$x3, $y3, $z3];
203
        }
204
 
205
        $o2 = $z2->multiply($z2);
206
        $a = $x1->multiply($o2);
207
        $c = $y1->multiply($o2)->multiply($z2);
208
        $d = $y2->multiply($o1)->multiply($z1);
209
        $e = $a->add($b);
210
        $f = $c->add($d);
211
        $g = $e->multiply($z1);
212
        $h = $f->multiply($x2)->add($g->multiply($y2));
213
        $z3 = $g->multiply($z2);
214
        $i = $f->add($z3);
215
        $p1 = $this->a->multiply($z3->multiply($z3));
216
        $p2 = $f->multiply($i);
217
        $p3 = $e->multiply($e)->multiply($e);
218
        $x3 = $p1->add($p2)->add($p3);
219
        $y3 = $i->multiply($x3)->add($g->multiply($g)->multiply($h));
220
 
221
        return [$x3, $y3, $z3];
222
    }
223
 
224
    /**
225
     * Doubles a point on a curve
226
     *
227
     * @return FiniteField[]
228
     */
229
    public function doublePoint(array $p)
230
    {
231
        if (!isset($this->factory)) {
232
            throw new \RuntimeException('setModulo needs to be called before this method');
233
        }
234
 
235
        if (!count($p)) {
236
            return [];
237
        }
238
 
239
        if (!isset($p[2])) {
240
            throw new \RuntimeException('Affine coordinates need to be manually converted to "Jacobi" coordinates or vice versa');
241
        }
242
 
243
        // formulas from http://hyperelliptic.org/EFD/g12o/auto-shortw-jacobian.html
244
 
245
        list($x1, $y1, $z1) = $p;
246
 
247
        $a = $x1->multiply($x1);
248
        $b = $a->multiply($a);
249
 
250
        if ($z1->equals($this->one)) {
251
            $x3 = $b->add($this->b);
252
            $z3 = clone $x1;
253
            $p1 = $a->add($y1)->add($z3)->multiply($this->b);
254
            $p2 = $a->add($y1)->multiply($b);
255
            $y3 = $p1->add($p2);
256
 
257
            return [$x3, $y3, $z3];
258
        }
259
 
260
        $c = $z1->multiply($z1);
261
        $d = $c->multiply($c);
262
        $x3 = $b->add($this->b->multiply($d->multiply($d)));
263
        $z3 = $x1->multiply($c);
264
        $p1 = $b->multiply($z3);
265
        $p2 = $a->add($y1->multiply($z1))->add($z3)->multiply($x3);
266
        $y3 = $p1->add($p2);
267
 
268
        return [$x3, $y3, $z3];
269
    }
270
 
271
    /**
272
     * Returns the X coordinate and the derived Y coordinate
273
     *
274
     * Not supported because it is covered by patents.
275
     * Quoting https://www.openssl.org/docs/man1.1.0/apps/ecparam.html ,
276
     *
277
     * "Due to patent issues the compressed option is disabled by default for binary curves
278
     *  and can be enabled by defining the preprocessor macro OPENSSL_EC_BIN_PT_COMP at
279
     *  compile time."
280
     *
281
     * @return array
282
     */
283
    public function derivePoint($m)
284
    {
285
        throw new \RuntimeException('Point compression on binary finite field elliptic curves is not supported');
286
    }
287
 
288
    /**
289
     * Tests whether or not the x / y values satisfy the equation
290
     *
291
     * @return boolean
292
     */
293
    public function verifyPoint(array $p)
294
    {
295
        list($x, $y) = $p;
296
        $lhs = $y->multiply($y);
297
        $lhs = $lhs->add($x->multiply($y));
298
        $x2 = $x->multiply($x);
299
        $x3 = $x2->multiply($x);
300
        $rhs = $x3->add($this->a->multiply($x2))->add($this->b);
301
 
302
        return $lhs->equals($rhs);
303
    }
304
 
305
    /**
306
     * Returns the modulo
307
     *
308
     * @return \phpseclib3\Math\BigInteger
309
     */
310
    public function getModulo()
311
    {
312
        return $this->modulo;
313
    }
314
 
315
    /**
316
     * Returns the a coefficient
317
     *
318
     * @return \phpseclib3\Math\PrimeField\Integer
319
     */
320
    public function getA()
321
    {
322
        return $this->a;
323
    }
324
 
325
    /**
326
     * Returns the a coefficient
327
     *
328
     * @return \phpseclib3\Math\PrimeField\Integer
329
     */
330
    public function getB()
331
    {
332
        return $this->b;
333
    }
334
 
335
    /**
336
     * Returns the affine point
337
     *
338
     * A Jacobian Coordinate is of the form (x, y, z).
339
     * To convert a Jacobian Coordinate to an Affine Point
340
     * you do (x / z^2, y / z^3)
341
     *
342
     * @return \phpseclib3\Math\PrimeField\Integer[]
343
     */
344
    public function convertToAffine(array $p)
345
    {
346
        if (!isset($p[2])) {
347
            return $p;
348
        }
349
        list($x, $y, $z) = $p;
350
        $z = $this->one->divide($z);
351
        $z2 = $z->multiply($z);
352
        return [
353
            $x->multiply($z2),
354
            $y->multiply($z2)->multiply($z)
355
        ];
356
    }
357
 
358
    /**
359
     * Converts an affine point to a jacobian coordinate
360
     *
361
     * @return \phpseclib3\Math\PrimeField\Integer[]
362
     */
363
    public function convertToInternal(array $p)
364
    {
365
        if (isset($p[2])) {
366
            return $p;
367
        }
368
 
369
        $p[2] = clone $this->one;
370
        $p['fresh'] = true;
371
        return $p;
372
    }
373
}