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