Subversion Repositories oidplus

Rev

Rev 846 | Go to most recent revision | 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^3 + a*x + x
5
 *
6
 * Technically, a Montgomery curve has a coefficient for y^2 but for Curve25519 and Curve448 that
7
 * coefficient is 1.
8
 *
9
 * Curve25519 and Curve448 do not make use of the y coordinate, which makes it unsuitable for use
10
 * with ECDSA / EdDSA. A few other differences between Curve25519 and Ed25519 are discussed at
11
 * https://crypto.stackexchange.com/a/43058/4520
12
 *
13
 * More info:
14
 *
15
 * https://en.wikipedia.org/wiki/Montgomery_curve
16
 *
17
 * PHP version 5 and 7
18
 *
874 daniel-mar 19
 * @category  Crypt
20
 * @package   EC
827 daniel-mar 21
 * @author    Jim Wigginton <terrafrost@php.net>
22
 * @copyright 2019 Jim Wigginton
23
 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
24
 * @link      http://pear.php.net/package/Math_BigInteger
25
 */
26
 
27
namespace phpseclib3\Crypt\EC\BaseCurves;
28
 
29
use phpseclib3\Crypt\EC\Curves\Curve25519;
30
use phpseclib3\Math\BigInteger;
31
use phpseclib3\Math\PrimeField;
32
use phpseclib3\Math\PrimeField\Integer as PrimeInteger;
33
 
34
/**
35
 * Curves over y^2 = x^3 + a*x + x
36
 *
874 daniel-mar 37
 * @package EC
827 daniel-mar 38
 * @author  Jim Wigginton <terrafrost@php.net>
874 daniel-mar 39
 * @access  public
827 daniel-mar 40
 */
41
class Montgomery extends Base
42
{
43
    /**
44
     * Prime Field Integer factory
45
     *
46
     * @var \phpseclib3\Math\PrimeField
47
     */
48
    protected $factory;
49
 
50
    /**
51
     * Cofficient for x
52
     *
53
     * @var object
54
     */
55
    protected $a;
56
 
57
    /**
58
     * Constant used for point doubling
59
     *
60
     * @var object
61
     */
62
    protected $a24;
63
 
64
    /**
65
     * The Number Zero
66
     *
67
     * @var object
68
     */
69
    protected $zero;
70
 
71
    /**
72
     * The Number One
73
     *
74
     * @var object
75
     */
76
    protected $one;
77
 
78
    /**
79
     * Base Point
80
     *
81
     * @var object
82
     */
83
    protected $p;
84
 
85
    /**
86
     * The modulo
87
     *
88
     * @var BigInteger
89
     */
90
    protected $modulo;
91
 
92
    /**
93
     * The Order
94
     *
95
     * @var BigInteger
96
     */
97
    protected $order;
98
 
99
    /**
100
     * Sets the modulo
101
     */
102
    public function setModulo(BigInteger $modulo)
103
    {
104
        $this->modulo = $modulo;
105
        $this->factory = new PrimeField($modulo);
106
        $this->zero = $this->factory->newInteger(new BigInteger());
107
        $this->one = $this->factory->newInteger(new BigInteger(1));
108
    }
109
 
110
    /**
111
     * Set coefficients a
112
     */
113
    public function setCoefficients(BigInteger $a)
114
    {
115
        if (!isset($this->factory)) {
116
            throw new \RuntimeException('setModulo needs to be called before this method');
117
        }
118
        $this->a = $this->factory->newInteger($a);
119
        $two = $this->factory->newInteger(new BigInteger(2));
120
        $four = $this->factory->newInteger(new BigInteger(4));
121
        $this->a24 = $this->a->subtract($two)->divide($four);
122
    }
123
 
124
    /**
125
     * Set x and y coordinates for the base point
126
     *
127
     * @param BigInteger|PrimeInteger $x
128
     * @param BigInteger|PrimeInteger $y
129
     * @return PrimeInteger[]
130
     */
131
    public function setBasePoint($x, $y)
132
    {
133
        switch (true) {
134
            case !$x instanceof BigInteger && !$x instanceof PrimeInteger:
135
                throw new \UnexpectedValueException('Argument 1 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
136
            case !$y instanceof BigInteger && !$y instanceof PrimeInteger:
137
                throw new \UnexpectedValueException('Argument 2 passed to Prime::setBasePoint() must be an instance of either BigInteger or PrimeField\Integer');
138
        }
139
        if (!isset($this->factory)) {
140
            throw new \RuntimeException('setModulo needs to be called before this method');
141
        }
142
        $this->p = [
143
            $x instanceof BigInteger ? $this->factory->newInteger($x) : $x,
144
            $y instanceof BigInteger ? $this->factory->newInteger($y) : $y
145
        ];
146
    }
147
 
148
    /**
149
     * Retrieve the base point as an array
150
     *
151
     * @return array
152
     */
153
    public function getBasePoint()
154
    {
155
        if (!isset($this->factory)) {
156
            throw new \RuntimeException('setModulo needs to be called before this method');
157
        }
158
        /*
159
        if (!isset($this->p)) {
160
            throw new \RuntimeException('setBasePoint needs to be called before this method');
161
        }
162
        */
163
        return $this->p;
164
    }
165
 
166
    /**
167
     * Doubles and adds a point on a curve
168
     *
169
     * See https://tools.ietf.org/html/draft-ietf-tls-curve25519-01#appendix-A.1.3
170
     *
171
     * @return FiniteField[][]
172
     */
173
    private function doubleAndAddPoint(array $p, array $q, PrimeInteger $x1)
174
    {
175
        if (!isset($this->factory)) {
176
            throw new \RuntimeException('setModulo needs to be called before this method');
177
        }
178
 
179
        if (!count($p) || !count($q)) {
180
            return [];
181
        }
182
 
183
        if (!isset($p[1])) {
184
            throw new \RuntimeException('Affine coordinates need to be manually converted to XZ coordinates');
185
        }
186
 
187
        list($x2, $z2) = $p;
188
        list($x3, $z3) = $q;
189
 
190
        $a = $x2->add($z2);
191
        $aa = $a->multiply($a);
192
        $b = $x2->subtract($z2);
193
        $bb = $b->multiply($b);
194
        $e = $aa->subtract($bb);
195
        $c = $x3->add($z3);
196
        $d = $x3->subtract($z3);
197
        $da = $d->multiply($a);
198
        $cb = $c->multiply($b);
199
        $temp = $da->add($cb);
200
        $x5 = $temp->multiply($temp);
201
        $temp = $da->subtract($cb);
202
        $z5 = $x1->multiply($temp->multiply($temp));
203
        $x4 = $aa->multiply($bb);
204
        $temp = static::class == Curve25519::class ? $bb : $aa;
205
        $z4 = $e->multiply($temp->add($this->a24->multiply($e)));
206
 
207
        return [
208
            [$x4, $z4],
209
            [$x5, $z5]
210
        ];
211
    }
212
 
213
    /**
214
     * Multiply a point on the curve by a scalar
215
     *
216
     * Uses the montgomery ladder technique as described here:
217
     *
218
     * https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder
219
     * https://github.com/phpecc/phpecc/issues/16#issuecomment-59176772
220
     *
221
     * @return array
222
     */
223
    public function multiplyPoint(array $p, BigInteger $d)
224
    {
225
        $p1 = [$this->one, $this->zero];
226
        $alreadyInternal = isset($x[1]);
227
        $p2 = $this->convertToInternal($p);
228
        $x = $p[0];
229
 
230
        $b = $d->toBits();
231
        $b = str_pad($b, 256, '0', STR_PAD_LEFT);
232
        for ($i = 0; $i < strlen($b); $i++) {
233
            $b_i = (int) $b[$i];
234
            if ($b_i) {
235
                list($p2, $p1) = $this->doubleAndAddPoint($p2, $p1, $x);
236
            } else {
237
                list($p1, $p2) = $this->doubleAndAddPoint($p1, $p2, $x);
238
            }
239
        }
240
 
241
        return $alreadyInternal ? $p1 : $this->convertToAffine($p1);
242
    }
243
 
244
    /**
245
     * Converts an affine point to an XZ coordinate
246
     *
247
     * From https://hyperelliptic.org/EFD/g1p/auto-montgom-xz.html
248
     *
249
     * XZ coordinates represent x y as X Z satsfying the following equations:
250
     *
251
     *   x=X/Z
252
     *
253
     * @return \phpseclib3\Math\PrimeField\Integer[]
254
     */
255
    public function convertToInternal(array $p)
256
    {
257
        if (empty($p)) {
258
            return [clone $this->zero, clone $this->one];
259
        }
260
 
261
        if (isset($p[1])) {
262
            return $p;
263
        }
264
 
265
        $p[1] = clone $this->one;
266
 
267
        return $p;
268
    }
269
 
270
    /**
271
     * Returns the affine point
272
     *
273
     * @return \phpseclib3\Math\PrimeField\Integer[]
274
     */
275
    public function convertToAffine(array $p)
276
    {
277
        if (!isset($p[1])) {
278
            return $p;
279
        }
280
        list($x, $z) = $p;
281
        return [$x->divide($z)];
282
    }
283
}