1<?php
2
3/**
4 * Pure-PHP (EC)DH implementation
5 *
6 * PHP version 5
7 *
8 * Here's an example of how to compute a shared secret with this library:
9 * <code>
10 * <?php
11 * include 'vendor/autoload.php';
12 *
13 * $ourPrivate = \phpseclib3\Crypt\DH::createKey();
14 * $secret = DH::computeSecret($ourPrivate, $theirPublic);
15 *
16 * ?>
17 * </code>
18 *
19 * @category  Crypt
20 * @package   DH
21 * @author    Jim Wigginton <terrafrost@php.net>
22 * @copyright 2016 Jim Wigginton
23 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
24 * @link      http://phpseclib.sourceforge.net
25 */
26
27namespace phpseclib3\Crypt;
28
29use phpseclib3\Crypt\Common\AsymmetricKey;
30use phpseclib3\Crypt\DH\Parameters;
31use phpseclib3\Crypt\DH\PrivateKey;
32use phpseclib3\Crypt\DH\PublicKey;
33use phpseclib3\Exception\NoKeyLoadedException;
34use phpseclib3\Exception\UnsupportedOperationException;
35use phpseclib3\Math\BigInteger;
36
37/**
38 * Pure-PHP (EC)DH implementation
39 *
40 * @package DH
41 * @author  Jim Wigginton <terrafrost@php.net>
42 * @access  public
43 */
44abstract class DH extends AsymmetricKey
45{
46    /**
47     * Algorithm Name
48     *
49     * @var string
50     * @access private
51     */
52    const ALGORITHM = 'DH';
53
54    /**
55     * DH prime
56     *
57     * @var \phpseclib3\Math\BigInteger
58     * @access private
59     */
60    protected $prime;
61
62    /**
63     * DH Base
64     *
65     * Prime divisor of p-1
66     *
67     * @var \phpseclib3\Math\BigInteger
68     * @access private
69     */
70    protected $base;
71
72    /**
73     * Create DH parameters
74     *
75     * This method is a bit polymorphic. It can take any of the following:
76     *  - two BigInteger's (prime and base)
77     *  - an integer representing the size of the prime in bits (the base is assumed to be 2)
78     *  - a string (eg. diffie-hellman-group14-sha1)
79     *
80     * @access public
81     * @return Parameters
82     */
83    public static function createParameters(...$args)
84    {
85        $params = new Parameters();
86        if (count($args) == 2 && $args[0] instanceof BigInteger && $args[1] instanceof BigInteger) {
87            //if (!$args[0]->isPrime()) {
88            //    throw new \InvalidArgumentException('The first parameter should be a prime number');
89            //}
90            $params->prime = $args[0];
91            $params->base = $args[1];
92            return $params;
93        } elseif (count($args) == 1 && is_numeric($args[0])) {
94            $params->prime = BigInteger::randomPrime($args[0]);
95            $params->base = new BigInteger(2);
96            return $params;
97        } elseif (count($args) != 1 || !is_string($args[0])) {
98            throw new \InvalidArgumentException('Valid parameters are either: two BigInteger\'s (prime and base), a single integer (the length of the prime; base is assumed to be 2) or a string');
99        }
100        switch ($args[0]) {
101            // see http://tools.ietf.org/html/rfc2409#section-6.2 and
102            // http://tools.ietf.org/html/rfc2412, appendex E
103            case 'diffie-hellman-group1-sha1':
104                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
105                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
106                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
107                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
108                break;
109            // see http://tools.ietf.org/html/rfc3526#section-3
110            case 'diffie-hellman-group14-sha1': // 2048-bit MODP Group
111            case 'diffie-hellman-group14-sha256':
112                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
113                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
114                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
115                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
116                         '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
117                         '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
118                         'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
119                         '3995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF';
120                break;
121            // see https://tools.ietf.org/html/rfc3526#section-4
122            case 'diffie-hellman-group15-sha512': // 3072-bit MODP Group
123                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
124                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
125                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
126                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
127                         '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
128                         '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
129                         'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
130                         '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
131                         'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
132                         'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
133                         'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
134                         '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A93AD2CAFFFFFFFFFFFFFFFF';
135                break;
136            // see https://tools.ietf.org/html/rfc3526#section-5
137            case 'diffie-hellman-group16-sha512': // 4096-bit MODP Group
138                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
139                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
140                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
141                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
142                         '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
143                         '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
144                         'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
145                         '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
146                         'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
147                         'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
148                         'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
149                         '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
150                         '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
151                         'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
152                         '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
153                         '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C934063199FFFFFFFFFFFFFFFF';
154                break;
155            // see https://tools.ietf.org/html/rfc3526#section-6
156            case 'diffie-hellman-group17-sha512': // 6144-bit MODP Group
157                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
158                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
159                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
160                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
161                         '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
162                         '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
163                         'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
164                         '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
165                         'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
166                         'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
167                         'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
168                         '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
169                         '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
170                         'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
171                         '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
172                         '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
173                         'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
174                         'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
175                         'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
176                         'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
177                         '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
178                         'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
179                         'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
180                         '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DCC4024FFFFFFFFFFFFFFFF';
181                break;
182            // see https://tools.ietf.org/html/rfc3526#section-7
183            case 'diffie-hellman-group18-sha512': // 8192-bit MODP Group
184                $prime = 'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74' .
185                         '020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F1437' .
186                         '4FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' .
187                         'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF05' .
188                         '98DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB' .
189                         '9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3B' .
190                         'E39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF695581718' .
191                         '3995497CEA956AE515D2261898FA051015728E5A8AAAC42DAD33170D04507A33' .
192                         'A85521ABDF1CBA64ECFB850458DBEF0A8AEA71575D060C7DB3970F85A6E1E4C7' .
193                         'ABF5AE8CDB0933D71E8C94E04A25619DCEE3D2261AD2EE6BF12FFA06D98A0864' .
194                         'D87602733EC86A64521F2B18177B200CBBE117577A615D6C770988C0BAD946E2' .
195                         '08E24FA074E5AB3143DB5BFCE0FD108E4B82D120A92108011A723C12A787E6D7' .
196                         '88719A10BDBA5B2699C327186AF4E23C1A946834B6150BDA2583E9CA2AD44CE8' .
197                         'DBBBC2DB04DE8EF92E8EFC141FBECAA6287C59474E6BC05D99B2964FA090C3A2' .
198                         '233BA186515BE7ED1F612970CEE2D7AFB81BDD762170481CD0069127D5B05AA9' .
199                         '93B4EA988D8FDDC186FFB7DC90A6C08F4DF435C93402849236C3FAB4D27C7026' .
200                         'C1D4DCB2602646DEC9751E763DBA37BDF8FF9406AD9E530EE5DB382F413001AE' .
201                         'B06A53ED9027D831179727B0865A8918DA3EDBEBCF9B14ED44CE6CBACED4BB1B' .
202                         'DB7F1447E6CC254B332051512BD7AF426FB8F401378CD2BF5983CA01C64B92EC' .
203                         'F032EA15D1721D03F482D7CE6E74FEF6D55E702F46980C82B5A84031900B1C9E' .
204                         '59E7C97FBEC7E8F323A97A7E36CC88BE0F1D45B7FF585AC54BD407B22B4154AA' .
205                         'CC8F6D7EBF48E1D814CC5ED20F8037E0A79715EEF29BE32806A1D58BB7C5DA76' .
206                         'F550AA3D8A1FBFF0EB19CCB1A313D55CDA56C9EC2EF29632387FE8D76E3C0468' .
207                         '043E8F663F4860EE12BF2D5B0B7474D6E694F91E6DBE115974A3926F12FEE5E4' .
208                         '38777CB6A932DF8CD8BEC4D073B931BA3BC832B68D9DD300741FA7BF8AFC47ED' .
209                         '2576F6936BA424663AAB639C5AE4F5683423B4742BF1C978238F16CBE39D652D' .
210                         'E3FDB8BEFC848AD922222E04A4037C0713EB57A81A23F0C73473FC646CEA306B' .
211                         '4BCBC8862F8385DDFA9D4B7FA2C087E879683303ED5BDD3A062B3CF5B3A278A6' .
212                         '6D2A13F83F44F82DDF310EE074AB6A364597E899A0255DC164F31CC50846851D' .
213                         'F9AB48195DED7EA1B1D510BD7EE74D73FAF36BC31ECFA268359046F4EB879F92' .
214                         '4009438B481C6CD7889A002ED5EE382BC9190DA6FC026E479558E4475677E9AA' .
215                         '9E3050E2765694DFC81F56E880B96E7160C980DD98EDD3DFFFFFFFFFFFFFFFFF';
216                break;
217            default:
218                throw new \InvalidArgumentException('Invalid named prime provided');
219        }
220
221        $params->prime = new BigInteger($prime, 16);
222        $params->base = new BigInteger(2);
223
224        return $params;
225    }
226
227    /**
228     * Create public / private key pair.
229     *
230     * The rationale for the second parameter is described in http://tools.ietf.org/html/rfc4419#section-6.2 :
231     *
232     * "To increase the speed of the key exchange, both client and server may
233     *  reduce the size of their private exponents.  It should be at least
234     *  twice as long as the key material that is generated from the shared
235     *  secret.  For more details, see the paper by van Oorschot and Wiener
236     *  [VAN-OORSCHOT]."
237     *
238     * $length is in bits
239     *
240     * @param Parameters $params
241     * @param int $length optional
242     * @access public
243     * @return DH\PrivateKey
244     */
245    public static function createKey(Parameters $params, $length = 0)
246    {
247        $one = new BigInteger(1);
248        if ($length) {
249            $max = $one->bitwise_leftShift($length);
250            $max = $max->subtract($one);
251        } else {
252            $max = $params->prime->subtract($one);
253        }
254
255        $key = new PrivateKey();
256        $key->prime = $params->prime;
257        $key->base = $params->base;
258        $key->privateKey = BigInteger::randomRange($one, $max);
259        $key->publicKey = $key->base->powMod($key->privateKey, $key->prime);
260        return $key;
261    }
262
263    /**
264     * Compute Shared Secret
265     *
266     * @param PrivateKey|EC $private
267     * @param PublicKey|BigInteger|string $public
268     * @access public
269     * @return mixed
270     */
271    public static function computeSecret($private, $public)
272    {
273        if ($private instanceof PrivateKey) { // DH\PrivateKey
274            switch (true) {
275                case $public instanceof PublicKey:
276                    if (!$private->prime->equals($public->prime) || !$private->base->equals($public->base)) {
277                        throw new \InvalidArgumentException('The public and private key do not share the same prime and / or base numbers');
278                    }
279                    return $public->publicKey->powMod($private->privateKey, $private->prime)->toBytes(true);
280                case is_string($public):
281                    $public = new BigInteger($public, -256);
282                    // fall-through
283                case $public instanceof BigInteger:
284                    return $public->powMod($private->privateKey, $private->prime)->toBytes(true);
285                default:
286                    throw new \InvalidArgumentException('$public needs to be an instance of DH\PublicKey, a BigInteger or a string');
287            }
288        }
289
290        if ($private instanceof EC\PrivateKey) {
291            switch (true) {
292                case $public instanceof EC\PublicKey:
293                    $public = $public->getEncodedCoordinates();
294                    // fall-through
295                case is_string($public):
296                    $point = $private->multiply($public);
297                    switch ($private->getCurve()) {
298                        case 'Curve25519':
299                        case 'Curve448':
300                            $secret = $point;
301                            break;
302                        default:
303                            // according to https://www.secg.org/sec1-v2.pdf#page=33 only X is returned
304                            $secret = substr($point, 1, (strlen($point) - 1) >> 1);
305                    }
306                    /*
307                    if (($secret[0] & "\x80") === "\x80") {
308                        $secret = "\0$secret";
309                    }
310                    */
311                    return $secret;
312                default:
313                    throw new \InvalidArgumentException('$public needs to be an instance of EC\PublicKey or a string (an encoded coordinate)');
314            }
315        }
316    }
317
318    /**
319     * Load the key
320     *
321     * @param string $key
322     * @param string $password optional
323     * @return AsymmetricKey
324     */
325    public static function load($key, $password = false)
326    {
327        try {
328            return EC::load($key, $password);
329        } catch (NoKeyLoadedException $e) {
330        }
331
332        return parent::load($key, $password);
333    }
334
335    /**
336     * OnLoad Handler
337     *
338     * @return bool
339     * @access protected
340     * @param array $components
341     */
342    protected static function onLoad($components)
343    {
344        if (!isset($components['privateKey']) && !isset($components['publicKey'])) {
345            $new = new Parameters();
346        } else {
347            $new = isset($components['privateKey']) ?
348                new PrivateKey() :
349                new PublicKey();
350        }
351
352        $new->prime = $components['prime'];
353        $new->base = $components['base'];
354
355        if (isset($components['privateKey'])) {
356            $new->privateKey = $components['privateKey'];
357        }
358        if (isset($components['publicKey'])) {
359            $new->publicKey = $components['publicKey'];
360        }
361
362        return $new;
363    }
364
365    /**
366     * Determines which hashing function should be used
367     *
368     * @access public
369     * @param string $hash
370     */
371    public function withHash($hash)
372    {
373        throw new UnsupportedOperationException('DH does not use a hash algorithm');
374    }
375
376    /**
377     * Returns the hash algorithm currently being used
378     *
379     * @access public
380     */
381    public function getHash()
382    {
383        throw new UnsupportedOperationException('DH does not use a hash algorithm');
384    }
385
386    /**
387     * Returns the parameters
388     *
389     * A public / private key is only returned if the currently loaded "key" contains an x or y
390     * value.
391     *
392     * @see self::getPublicKey()
393     * @access public
394     * @return mixed
395     */
396    public function getParameters()
397    {
398        $type = self::validatePlugin('Keys', 'PKCS1', 'saveParameters');
399
400        $key = $type::saveParameters($this->prime, $this->base);
401        return self::load($key, 'PKCS1');
402    }
403}
404