web/lib/Zend/Crypt/DiffieHellman.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Crypt
       
    17  * @subpackage DiffieHellman
       
    18  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    19  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    20  * @version    $Id: DiffieHellman.php 22662 2010-07-24 17:37:36Z mabe $
       
    21  */
       
    22 
       
    23 /**
       
    24  * PHP implementation of the Diffie-Hellman public key encryption algorithm.
       
    25  * Allows two unassociated parties to establish a joint shared secret key
       
    26  * to be used in encrypting subsequent communications.
       
    27  *
       
    28  * @category   Zend
       
    29  * @package    Zend_Crypt
       
    30  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    31  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    32  */
       
    33 class Zend_Crypt_DiffieHellman
       
    34 {
       
    35 
       
    36     /**
       
    37      * Static flag to select whether to use PHP5.3's openssl extension
       
    38      * if available.
       
    39      *
       
    40      * @var boolean
       
    41      */
       
    42     public static $useOpenssl = true;
       
    43 
       
    44     /**
       
    45      * Default large prime number; required by the algorithm.
       
    46      *
       
    47      * @var string
       
    48      */
       
    49     private $_prime = null;
       
    50 
       
    51     /**
       
    52      * The default generator number. This number must be greater than 0 but
       
    53      * less than the prime number set.
       
    54      *
       
    55      * @var string
       
    56      */
       
    57     private $_generator = null;
       
    58 
       
    59     /**
       
    60      * A private number set by the local user. It's optional and will
       
    61      * be generated if not set.
       
    62      *
       
    63      * @var string
       
    64      */
       
    65     private $_privateKey = null;
       
    66 
       
    67     /**
       
    68      * BigInteger support object courtesy of Zend_Crypt_Math
       
    69      *
       
    70      * @var Zend_Crypt_Math_BigInteger
       
    71      */
       
    72     private $_math = null;
       
    73 
       
    74     /**
       
    75      * The public key generated by this instance after calling generateKeys().
       
    76      *
       
    77      * @var string
       
    78      */
       
    79     private $_publicKey = null;
       
    80 
       
    81     /**
       
    82      * The shared secret key resulting from a completed Diffie Hellman
       
    83      * exchange
       
    84      *
       
    85      * @var string
       
    86      */
       
    87     private $_secretKey = null;
       
    88 
       
    89     /**
       
    90      * Constants
       
    91      */
       
    92     const BINARY = 'binary';
       
    93     const NUMBER = 'number';
       
    94     const BTWOC  = 'btwoc';
       
    95 
       
    96     /**
       
    97      * Constructor; if set construct the object using the parameter array to
       
    98      * set values for Prime, Generator and Private.
       
    99      * If a Private Key is not set, one will be generated at random.
       
   100      *
       
   101      * @param string $prime
       
   102      * @param string $generator
       
   103      * @param string $privateKey
       
   104      * @param string $privateKeyType
       
   105      * @return void
       
   106      */
       
   107     public function __construct($prime, $generator, $privateKey = null, $privateKeyType = self::NUMBER)
       
   108     {
       
   109         $this->setPrime($prime);
       
   110         $this->setGenerator($generator);
       
   111         if ($privateKey !== null) {
       
   112             $this->setPrivateKey($privateKey, $privateKeyType);
       
   113         }
       
   114         $this->setBigIntegerMath();
       
   115     }
       
   116 
       
   117     /**
       
   118      * Generate own public key. If a private number has not already been
       
   119      * set, one will be generated at this stage.
       
   120      *
       
   121      * @return Zend_Crypt_DiffieHellman
       
   122      */
       
   123     public function generateKeys()
       
   124     {
       
   125         if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) {
       
   126             $details = array();
       
   127             $details['p'] = $this->getPrime();
       
   128             $details['g'] = $this->getGenerator();
       
   129             if ($this->hasPrivateKey()) {
       
   130                 $details['priv_key'] = $this->getPrivateKey();
       
   131             }
       
   132             $opensslKeyResource = openssl_pkey_new( array('dh' => $details) );
       
   133             $data = openssl_pkey_get_details($opensslKeyResource);
       
   134             $this->setPrivateKey($data['dh']['priv_key'], self::BINARY);
       
   135             $this->setPublicKey($data['dh']['pub_key'], self::BINARY);
       
   136         } else {
       
   137             // Private key is lazy generated in the absence of PHP 5.3's ext/openssl
       
   138             $publicKey = $this->_math->powmod($this->getGenerator(), $this->getPrivateKey(), $this->getPrime());
       
   139             $this->setPublicKey($publicKey);
       
   140         }
       
   141         return $this;
       
   142     }
       
   143 
       
   144     /**
       
   145      * Setter for the value of the public number
       
   146      *
       
   147      * @param string $number
       
   148      * @param string $type
       
   149      * @return Zend_Crypt_DiffieHellman
       
   150      */
       
   151     public function setPublicKey($number, $type = self::NUMBER)
       
   152     {
       
   153         if ($type == self::BINARY) {
       
   154             $number = $this->_math->fromBinary($number);
       
   155         }
       
   156         if (!preg_match("/^\d+$/", $number)) {
       
   157             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   158             throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
       
   159         }
       
   160         $this->_publicKey = (string) $number;
       
   161         return $this;
       
   162     }
       
   163 
       
   164     /**
       
   165      * Returns own public key for communication to the second party to this
       
   166      * transaction.
       
   167      *
       
   168      * @param string $type
       
   169      * @return string
       
   170      */
       
   171     public function getPublicKey($type = self::NUMBER)
       
   172     {
       
   173         if ($this->_publicKey === null) {
       
   174             require_once 'Zend/Crypt/DiffieHellman/Exception.php';
       
   175             throw new Zend_Crypt_DiffieHellman_Exception('A public key has not yet been generated using a prior call to generateKeys()');
       
   176         }
       
   177         if ($type == self::BINARY) {
       
   178             return $this->_math->toBinary($this->_publicKey);
       
   179         } elseif ($type == self::BTWOC) {
       
   180             return $this->_math->btwoc($this->_math->toBinary($this->_publicKey));
       
   181         }
       
   182         return $this->_publicKey;
       
   183     }
       
   184 
       
   185     /**
       
   186      * Compute the shared secret key based on the public key received from the
       
   187      * the second party to this transaction. This should agree to the secret
       
   188      * key the second party computes on our own public key.
       
   189      * Once in agreement, the key is known to only to both parties.
       
   190      * By default, the function expects the public key to be in binary form
       
   191      * which is the typical format when being transmitted.
       
   192      *
       
   193      * If you need the binary form of the shared secret key, call
       
   194      * getSharedSecretKey() with the optional parameter for Binary output.
       
   195      *
       
   196      * @param string $publicKey
       
   197      * @param string $type
       
   198      * @return mixed
       
   199      */
       
   200     public function computeSecretKey($publicKey, $type = self::NUMBER, $output = self::NUMBER)
       
   201     {
       
   202         if ($type == self::BINARY) {
       
   203             $publicKey = $this->_math->fromBinary($publicKey);
       
   204         }
       
   205         if (!preg_match("/^\d+$/", $publicKey)) {
       
   206             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   207             throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
       
   208         }
       
   209         if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) {
       
   210             $this->_secretKey = openssl_dh_compute_key($publicKey, $this->getPublicKey());
       
   211         } else {
       
   212             $this->_secretKey = $this->_math->powmod($publicKey, $this->getPrivateKey(), $this->getPrime());
       
   213         }
       
   214         return $this->getSharedSecretKey($output);
       
   215     }
       
   216 
       
   217     /**
       
   218      * Return the computed shared secret key from the DiffieHellman transaction
       
   219      *
       
   220      * @param string $type
       
   221      * @return string
       
   222      */
       
   223     public function getSharedSecretKey($type = self::NUMBER)
       
   224     {
       
   225         if (!isset($this->_secretKey)) {
       
   226             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   227             throw new Zend_Crypt_DiffieHellman_Exception('A secret key has not yet been computed; call computeSecretKey()');
       
   228         }
       
   229         if ($type == self::BINARY) {
       
   230             return $this->_math->toBinary($this->_secretKey);
       
   231         } elseif ($type == self::BTWOC) {
       
   232             return $this->_math->btwoc($this->_math->toBinary($this->_secretKey));
       
   233         }
       
   234         return $this->_secretKey;
       
   235     }
       
   236 
       
   237     /**
       
   238      * Setter for the value of the prime number
       
   239      *
       
   240      * @param string $number
       
   241      * @return Zend_Crypt_DiffieHellman
       
   242      */
       
   243     public function setPrime($number)
       
   244     {
       
   245         if (!preg_match("/^\d+$/", $number) || $number < 11) {
       
   246             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   247             throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number or too small: should be a large natural number prime');
       
   248         }
       
   249         $this->_prime = (string) $number;
       
   250         return $this;
       
   251     }
       
   252 
       
   253     /**
       
   254      * Getter for the value of the prime number
       
   255      *
       
   256      * @return string
       
   257      */
       
   258     public function getPrime()
       
   259     {
       
   260         if (!isset($this->_prime)) {
       
   261             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   262             throw new Zend_Crypt_DiffieHellman_Exception('No prime number has been set');
       
   263         }
       
   264         return $this->_prime;
       
   265     }
       
   266 
       
   267 
       
   268     /**
       
   269      * Setter for the value of the generator number
       
   270      *
       
   271      * @param string $number
       
   272      * @return Zend_Crypt_DiffieHellman
       
   273      */
       
   274     public function setGenerator($number)
       
   275     {
       
   276         if (!preg_match("/^\d+$/", $number) || $number < 2) {
       
   277             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   278             throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number greater than 1');
       
   279         }
       
   280         $this->_generator = (string) $number;
       
   281         return $this;
       
   282     }
       
   283 
       
   284     /**
       
   285      * Getter for the value of the generator number
       
   286      *
       
   287      * @return string
       
   288      */
       
   289     public function getGenerator()
       
   290     {
       
   291         if (!isset($this->_generator)) {
       
   292             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   293             throw new Zend_Crypt_DiffieHellman_Exception('No generator number has been set');
       
   294         }
       
   295         return $this->_generator;
       
   296     }
       
   297 
       
   298     /**
       
   299      * Setter for the value of the private number
       
   300      *
       
   301      * @param string $number
       
   302      * @param string $type
       
   303      * @return Zend_Crypt_DiffieHellman
       
   304      */
       
   305     public function setPrivateKey($number, $type = self::NUMBER)
       
   306     {
       
   307         if ($type == self::BINARY) {
       
   308             $number = $this->_math->fromBinary($number);
       
   309         }
       
   310         if (!preg_match("/^\d+$/", $number)) {
       
   311             require_once('Zend/Crypt/DiffieHellman/Exception.php');
       
   312             throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number');
       
   313         }
       
   314         $this->_privateKey = (string) $number;
       
   315         return $this;
       
   316     }
       
   317 
       
   318     /**
       
   319      * Getter for the value of the private number
       
   320      *
       
   321      * @param string $type
       
   322      * @return string
       
   323      */
       
   324     public function getPrivateKey($type = self::NUMBER)
       
   325     {
       
   326         if (!$this->hasPrivateKey()) {
       
   327             $this->setPrivateKey($this->_generatePrivateKey(), self::BINARY);
       
   328         }
       
   329         if ($type == self::BINARY) {
       
   330             return $this->_math->toBinary($this->_privateKey);
       
   331         } elseif ($type == self::BTWOC) {
       
   332             return $this->_math->btwoc($this->_math->toBinary($this->_privateKey));
       
   333         }
       
   334         return $this->_privateKey;
       
   335     }
       
   336 
       
   337     /**
       
   338      * Check whether a private key currently exists.
       
   339      *
       
   340      * @return boolean
       
   341      */
       
   342     public function hasPrivateKey()
       
   343     {
       
   344         return isset($this->_privateKey);
       
   345     }
       
   346 
       
   347     /**
       
   348      * Setter to pass an extension parameter which is used to create
       
   349      * a specific BigInteger instance for a specific extension type.
       
   350      * Allows manual setting of the class in case of an extension
       
   351      * problem or bug.
       
   352      *
       
   353      * @param string $extension
       
   354      * @return void
       
   355      */
       
   356     public function setBigIntegerMath($extension = null)
       
   357     {
       
   358         /**
       
   359          * @see Zend_Crypt_Math
       
   360          */
       
   361         require_once 'Zend/Crypt/Math.php';
       
   362         $this->_math = new Zend_Crypt_Math($extension);
       
   363     }
       
   364 
       
   365     /**
       
   366      * In the event a private number/key has not been set by the user,
       
   367      * or generated by ext/openssl, a best attempt will be made to
       
   368      * generate a random key. Having a random number generator installed
       
   369      * on linux/bsd is highly recommended! The alternative is not recommended
       
   370      * for production unless without any other option.
       
   371      *
       
   372      * @return string
       
   373      */
       
   374     protected function _generatePrivateKey()
       
   375     {
       
   376         $rand = $this->_math->rand($this->getGenerator(), $this->getPrime());
       
   377         return $rand;
       
   378     }
       
   379 
       
   380 }