File indexing completed on 2024-12-22 05:36:34
0001 <?php 0002 /** 0003 * Zend Framework 0004 * 0005 * LICENSE 0006 * 0007 * This source file is subject to the new BSD license that is bundled 0008 * with this package in the file LICENSE.txt. 0009 * It is also available through the world-wide-web at this URL: 0010 * http://framework.zend.com/license/new-bsd 0011 * If you did not receive a copy of the license and are unable to 0012 * obtain it through the world-wide-web, please send an email 0013 * to license@zend.com so we can send you a copy immediately. 0014 * 0015 * @category Zend 0016 * @package Zend_Crypt 0017 * @subpackage DiffieHellman 0018 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0019 * @license http://framework.zend.com/license/new-bsd New BSD License 0020 * @version $Id$ 0021 */ 0022 0023 /** 0024 * PHP implementation of the Diffie-Hellman public key encryption algorithm. 0025 * Allows two unassociated parties to establish a joint shared secret key 0026 * to be used in encrypting subsequent communications. 0027 * 0028 * @category Zend 0029 * @package Zend_Crypt 0030 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0031 * @license http://framework.zend.com/license/new-bsd New BSD License 0032 */ 0033 class Zend_Crypt_DiffieHellman 0034 { 0035 0036 /** 0037 * Static flag to select whether to use PHP5.3's openssl extension 0038 * if available. 0039 * 0040 * @var boolean 0041 */ 0042 public static $useOpenssl = true; 0043 0044 /** 0045 * Default large prime number; required by the algorithm. 0046 * 0047 * @var string 0048 */ 0049 private $_prime = null; 0050 0051 /** 0052 * The default generator number. This number must be greater than 0 but 0053 * less than the prime number set. 0054 * 0055 * @var string 0056 */ 0057 private $_generator = null; 0058 0059 /** 0060 * A private number set by the local user. It's optional and will 0061 * be generated if not set. 0062 * 0063 * @var string 0064 */ 0065 private $_privateKey = null; 0066 0067 /** 0068 * BigInteger support object courtesy of Zend_Crypt_Math 0069 * 0070 * @var Zend_Crypt_Math_BigInteger 0071 */ 0072 private $_math = null; 0073 0074 /** 0075 * The public key generated by this instance after calling generateKeys(). 0076 * 0077 * @var string 0078 */ 0079 private $_publicKey = null; 0080 0081 /** 0082 * The shared secret key resulting from a completed Diffie Hellman 0083 * exchange 0084 * 0085 * @var string 0086 */ 0087 private $_secretKey = null; 0088 0089 /** 0090 * Constants 0091 */ 0092 const BINARY = 'binary'; 0093 const NUMBER = 'number'; 0094 const BTWOC = 'btwoc'; 0095 0096 /** 0097 * Constructor; if set construct the object using the parameter array to 0098 * set values for Prime, Generator and Private. 0099 * If a Private Key is not set, one will be generated at random. 0100 * 0101 * @param string $prime 0102 * @param string $generator 0103 * @param string $privateKey 0104 * @param string $privateKeyType 0105 */ 0106 public function __construct($prime, $generator, $privateKey = null, $privateKeyType = self::NUMBER) 0107 { 0108 $this->setPrime($prime); 0109 $this->setGenerator($generator); 0110 if ($privateKey !== null) { 0111 $this->setPrivateKey($privateKey, $privateKeyType); 0112 } 0113 $this->setBigIntegerMath(); 0114 } 0115 0116 /** 0117 * Generate own public key. If a private number has not already been 0118 * set, one will be generated at this stage. 0119 * 0120 * @return Zend_Crypt_DiffieHellman 0121 */ 0122 public function generateKeys() 0123 { 0124 if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) { 0125 $details = array(); 0126 $details['p'] = $this->getPrime(); 0127 $details['g'] = $this->getGenerator(); 0128 if ($this->hasPrivateKey()) { 0129 $details['priv_key'] = $this->getPrivateKey(); 0130 } 0131 $opensslKeyResource = openssl_pkey_new( array('dh' => $details) ); 0132 $data = openssl_pkey_get_details($opensslKeyResource); 0133 $this->setPrivateKey($data['dh']['priv_key'], self::BINARY); 0134 $this->setPublicKey($data['dh']['pub_key'], self::BINARY); 0135 } else { 0136 // Private key is lazy generated in the absence of PHP 5.3's ext/openssl 0137 $publicKey = $this->_math->powmod($this->getGenerator(), $this->getPrivateKey(), $this->getPrime()); 0138 $this->setPublicKey($publicKey); 0139 } 0140 return $this; 0141 } 0142 0143 /** 0144 * Setter for the value of the public number 0145 * 0146 * @param string $number 0147 * @param string $type 0148 * @throws Zend_Crypt_DiffieHellman_Exception 0149 * @return Zend_Crypt_DiffieHellman 0150 */ 0151 public function setPublicKey($number, $type = self::NUMBER) 0152 { 0153 if ($type == self::BINARY) { 0154 $number = $this->_math->fromBinary($number); 0155 } 0156 if (!preg_match("/^\d+$/", $number)) { 0157 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0158 throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); 0159 } 0160 $this->_publicKey = (string) $number; 0161 return $this; 0162 } 0163 0164 /** 0165 * Returns own public key for communication to the second party to this 0166 * transaction. 0167 * 0168 * @param string $type 0169 * @throws Zend_Crypt_DiffieHellman_Exception 0170 * @return string 0171 */ 0172 public function getPublicKey($type = self::NUMBER) 0173 { 0174 if ($this->_publicKey === null) { 0175 // require_once 'Zend/Crypt/DiffieHellman/Exception.php'; 0176 throw new Zend_Crypt_DiffieHellman_Exception('A public key has not yet been generated using a prior call to generateKeys()'); 0177 } 0178 if ($type == self::BINARY) { 0179 return $this->_math->toBinary($this->_publicKey); 0180 } elseif ($type == self::BTWOC) { 0181 return $this->_math->btwoc($this->_math->toBinary($this->_publicKey)); 0182 } 0183 return $this->_publicKey; 0184 } 0185 0186 /** 0187 * Compute the shared secret key based on the public key received from the 0188 * the second party to this transaction. This should agree to the secret 0189 * key the second party computes on our own public key. 0190 * Once in agreement, the key is known to only to both parties. 0191 * By default, the function expects the public key to be in binary form 0192 * which is the typical format when being transmitted. 0193 * 0194 * If you need the binary form of the shared secret key, call 0195 * getSharedSecretKey() with the optional parameter for Binary output. 0196 * 0197 * @param string $publicKey 0198 * @param string $type 0199 * @param string $output 0200 * @throws Zend_Crypt_DiffieHellman_Exception 0201 * @return mixed 0202 */ 0203 public function computeSecretKey($publicKey, $type = self::NUMBER, $output = self::NUMBER) 0204 { 0205 if ($type == self::BINARY) { 0206 $publicKey = $this->_math->fromBinary($publicKey); 0207 } 0208 if (!preg_match("/^\d+$/", $publicKey)) { 0209 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0210 throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); 0211 } 0212 if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) { 0213 $this->_secretKey = openssl_dh_compute_key($publicKey, $this->getPublicKey()); 0214 } else { 0215 $this->_secretKey = $this->_math->powmod($publicKey, $this->getPrivateKey(), $this->getPrime()); 0216 } 0217 return $this->getSharedSecretKey($output); 0218 } 0219 0220 /** 0221 * Return the computed shared secret key from the DiffieHellman transaction 0222 * 0223 * @param string $type 0224 * @throws Zend_Crypt_DiffieHellman_Exception 0225 * @return string 0226 */ 0227 public function getSharedSecretKey($type = self::NUMBER) 0228 { 0229 if (!isset($this->_secretKey)) { 0230 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0231 throw new Zend_Crypt_DiffieHellman_Exception('A secret key has not yet been computed; call computeSecretKey()'); 0232 } 0233 if ($type == self::BINARY) { 0234 return $this->_math->toBinary($this->_secretKey); 0235 } elseif ($type == self::BTWOC) { 0236 return $this->_math->btwoc($this->_math->toBinary($this->_secretKey)); 0237 } 0238 return $this->_secretKey; 0239 } 0240 0241 /** 0242 * Setter for the value of the prime number 0243 * 0244 * @param string $number 0245 * @throws Zend_Crypt_DiffieHellman_Exception 0246 * @return Zend_Crypt_DiffieHellman 0247 */ 0248 public function setPrime($number) 0249 { 0250 if (!preg_match("/^\d+$/", $number) || $number < 11) { 0251 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0252 throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number or too small: should be a large natural number prime'); 0253 } 0254 $this->_prime = (string) $number; 0255 return $this; 0256 } 0257 0258 /** 0259 * Getter for the value of the prime number 0260 * 0261 * @throws Zend_Crypt_DiffieHellman_Exception 0262 * @return string 0263 */ 0264 public function getPrime() 0265 { 0266 if (!isset($this->_prime)) { 0267 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0268 throw new Zend_Crypt_DiffieHellman_Exception('No prime number has been set'); 0269 } 0270 return $this->_prime; 0271 } 0272 0273 /** 0274 * Setter for the value of the generator number 0275 * 0276 * @param string $number 0277 * @throws Zend_Crypt_DiffieHellman_Exception 0278 * @return Zend_Crypt_DiffieHellman 0279 */ 0280 public function setGenerator($number) 0281 { 0282 if (!preg_match("/^\d+$/", $number) || $number < 2) { 0283 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0284 throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number greater than 1'); 0285 } 0286 $this->_generator = (string) $number; 0287 return $this; 0288 } 0289 0290 /** 0291 * Getter for the value of the generator number 0292 * 0293 * @throws Zend_Crypt_DiffieHellman_Exception 0294 * @return string 0295 */ 0296 public function getGenerator() 0297 { 0298 if (!isset($this->_generator)) { 0299 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0300 throw new Zend_Crypt_DiffieHellman_Exception('No generator number has been set'); 0301 } 0302 return $this->_generator; 0303 } 0304 0305 /** 0306 * Setter for the value of the private number 0307 * 0308 * @param string $number 0309 * @param string $type 0310 * @throws Zend_Crypt_DiffieHellman_Exception 0311 * @return Zend_Crypt_DiffieHellman 0312 */ 0313 public function setPrivateKey($number, $type = self::NUMBER) 0314 { 0315 if ($type == self::BINARY) { 0316 $number = $this->_math->fromBinary($number); 0317 } 0318 if (!preg_match("/^\d+$/", $number)) { 0319 // require_once('Zend/Crypt/DiffieHellman/Exception.php'); 0320 throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); 0321 } 0322 $this->_privateKey = (string) $number; 0323 return $this; 0324 } 0325 0326 /** 0327 * Getter for the value of the private number 0328 * 0329 * @param string $type 0330 * @return string 0331 */ 0332 public function getPrivateKey($type = self::NUMBER) 0333 { 0334 if (!$this->hasPrivateKey()) { 0335 $this->setPrivateKey($this->_generatePrivateKey(), self::BINARY); 0336 } 0337 if ($type == self::BINARY) { 0338 return $this->_math->toBinary($this->_privateKey); 0339 } elseif ($type == self::BTWOC) { 0340 return $this->_math->btwoc($this->_math->toBinary($this->_privateKey)); 0341 } 0342 return $this->_privateKey; 0343 } 0344 0345 /** 0346 * Check whether a private key currently exists. 0347 * 0348 * @return boolean 0349 */ 0350 public function hasPrivateKey() 0351 { 0352 return isset($this->_privateKey); 0353 } 0354 0355 /** 0356 * Setter to pass an extension parameter which is used to create 0357 * a specific BigInteger instance for a specific extension type. 0358 * Allows manual setting of the class in case of an extension 0359 * problem or bug. 0360 * 0361 * @param string $extension 0362 * @return void 0363 */ 0364 public function setBigIntegerMath($extension = null) 0365 { 0366 /** 0367 * @see Zend_Crypt_Math 0368 */ 0369 // require_once 'Zend/Crypt/Math.php'; 0370 $this->_math = new Zend_Crypt_Math($extension); 0371 } 0372 0373 /** 0374 * In the event a private number/key has not been set by the user, 0375 * or generated by ext/openssl, a best attempt will be made to 0376 * generate a random key. Having a random number generator installed 0377 * on linux/bsd is highly recommended! The alternative is not recommended 0378 * for production unless without any other option. 0379 * 0380 * @return string 0381 */ 0382 protected function _generatePrivateKey() 0383 { 0384 $rand = $this->_math->rand($this->getGenerator(), $this->getPrime()); 0385 return $rand; 0386 } 0387 0388 }