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 }