<?php /** * Zend Framework * * LICENSE * * This source file is subject to the new BSD license that is bundled * with this package in the file LICENSE.txt. * It is also available through the world-wide-web at this URL: * http://framework.zend.com/license/new-bsd * If you did not receive a copy of the license and are unable to * obtain it through the world-wide-web, please send an email * to license@zend.com so we can send you a copy immediately. * * @category Zend * @package Zend_Crypt * @subpackage DiffieHellman * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License * @version $Id$ */ /** * PHP implementation of the Diffie-Hellman public key encryption algorithm. * Allows two unassociated parties to establish a joint shared secret key * to be used in encrypting subsequent communications. * * @category Zend * @package Zend_Crypt * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com) * @license http://framework.zend.com/license/new-bsd New BSD License */ class Zend_Crypt_DiffieHellman { /** * Static flag to select whether to use PHP5.3's openssl extension * if available. * * @var boolean */ public static $useOpenssl = true; /** * Default large prime number; required by the algorithm. * * @var string */ private $_prime = null; /** * The default generator number. This number must be greater than 0 but * less than the prime number set. * * @var string */ private $_generator = null; /** * A private number set by the local user. It's optional and will * be generated if not set. * * @var string */ private $_privateKey = null; /** * BigInteger support object courtesy of Zend_Crypt_Math * * @var Zend_Crypt_Math_BigInteger */ private $_math = null; /** * The public key generated by this instance after calling generateKeys(). * * @var string */ private $_publicKey = null; /** * The shared secret key resulting from a completed Diffie Hellman * exchange * * @var string */ private $_secretKey = null; /** * Constants */ const BINARY = 'binary'; const NUMBER = 'number'; const BTWOC = 'btwoc'; /** * Constructor; if set construct the object using the parameter array to * set values for Prime, Generator and Private. * If a Private Key is not set, one will be generated at random. * * @param string $prime * @param string $generator * @param string $privateKey * @param string $privateKeyType * @return void */ public function __construct($prime, $generator, $privateKey = null, $privateKeyType = self::NUMBER) { $this->setPrime($prime); $this->setGenerator($generator); if ($privateKey !== null) { $this->setPrivateKey($privateKey, $privateKeyType); } $this->setBigIntegerMath(); } /** * Generate own public key. If a private number has not already been * set, one will be generated at this stage. * * @return Zend_Crypt_DiffieHellman */ public function generateKeys() { if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) { $details = array(); $details['p'] = $this->getPrime(); $details['g'] = $this->getGenerator(); if ($this->hasPrivateKey()) { $details['priv_key'] = $this->getPrivateKey(); } $opensslKeyResource = openssl_pkey_new( array('dh' => $details) ); $data = openssl_pkey_get_details($opensslKeyResource); $this->setPrivateKey($data['dh']['priv_key'], self::BINARY); $this->setPublicKey($data['dh']['pub_key'], self::BINARY); } else { // Private key is lazy generated in the absence of PHP 5.3's ext/openssl $publicKey = $this->_math->powmod($this->getGenerator(), $this->getPrivateKey(), $this->getPrime()); $this->setPublicKey($publicKey); } return $this; } /** * Setter for the value of the public number * * @param string $number * @param string $type * @return Zend_Crypt_DiffieHellman */ public function setPublicKey($number, $type = self::NUMBER) { if ($type == self::BINARY) { $number = $this->_math->fromBinary($number); } if (!preg_match("/^\d+$/", $number)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); } $this->_publicKey = (string) $number; return $this; } /** * Returns own public key for communication to the second party to this * transaction. * * @param string $type * @return string */ public function getPublicKey($type = self::NUMBER) { if ($this->_publicKey === null) { require_once 'Zend/Crypt/DiffieHellman/Exception.php'; throw new Zend_Crypt_DiffieHellman_Exception('A public key has not yet been generated using a prior call to generateKeys()'); } if ($type == self::BINARY) { return $this->_math->toBinary($this->_publicKey); } elseif ($type == self::BTWOC) { return $this->_math->btwoc($this->_math->toBinary($this->_publicKey)); } return $this->_publicKey; } /** * Compute the shared secret key based on the public key received from the * the second party to this transaction. This should agree to the secret * key the second party computes on our own public key. * Once in agreement, the key is known to only to both parties. * By default, the function expects the public key to be in binary form * which is the typical format when being transmitted. * * If you need the binary form of the shared secret key, call * getSharedSecretKey() with the optional parameter for Binary output. * * @param string $publicKey * @param string $type * @return mixed */ public function computeSecretKey($publicKey, $type = self::NUMBER, $output = self::NUMBER) { if ($type == self::BINARY) { $publicKey = $this->_math->fromBinary($publicKey); } if (!preg_match("/^\d+$/", $publicKey)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); } if (function_exists('openssl_dh_compute_key') && self::$useOpenssl !== false) { $this->_secretKey = openssl_dh_compute_key($publicKey, $this->getPublicKey()); } else { $this->_secretKey = $this->_math->powmod($publicKey, $this->getPrivateKey(), $this->getPrime()); } return $this->getSharedSecretKey($output); } /** * Return the computed shared secret key from the DiffieHellman transaction * * @param string $type * @return string */ public function getSharedSecretKey($type = self::NUMBER) { if (!isset($this->_secretKey)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('A secret key has not yet been computed; call computeSecretKey()'); } if ($type == self::BINARY) { return $this->_math->toBinary($this->_secretKey); } elseif ($type == self::BTWOC) { return $this->_math->btwoc($this->_math->toBinary($this->_secretKey)); } return $this->_secretKey; } /** * Setter for the value of the prime number * * @param string $number * @return Zend_Crypt_DiffieHellman */ public function setPrime($number) { if (!preg_match("/^\d+$/", $number) || $number < 11) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number or too small: should be a large natural number prime'); } $this->_prime = (string) $number; return $this; } /** * Getter for the value of the prime number * * @return string */ public function getPrime() { if (!isset($this->_prime)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('No prime number has been set'); } return $this->_prime; } /** * Setter for the value of the generator number * * @param string $number * @return Zend_Crypt_DiffieHellman */ public function setGenerator($number) { if (!preg_match("/^\d+$/", $number) || $number < 2) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number greater than 1'); } $this->_generator = (string) $number; return $this; } /** * Getter for the value of the generator number * * @return string */ public function getGenerator() { if (!isset($this->_generator)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('No generator number has been set'); } return $this->_generator; } /** * Setter for the value of the private number * * @param string $number * @param string $type * @return Zend_Crypt_DiffieHellman */ public function setPrivateKey($number, $type = self::NUMBER) { if ($type == self::BINARY) { $number = $this->_math->fromBinary($number); } if (!preg_match("/^\d+$/", $number)) { require_once('Zend/Crypt/DiffieHellman/Exception.php'); throw new Zend_Crypt_DiffieHellman_Exception('invalid parameter; not a positive natural number'); } $this->_privateKey = (string) $number; return $this; } /** * Getter for the value of the private number * * @param string $type * @return string */ public function getPrivateKey($type = self::NUMBER) { if (!$this->hasPrivateKey()) { $this->setPrivateKey($this->_generatePrivateKey(), self::BINARY); } if ($type == self::BINARY) { return $this->_math->toBinary($this->_privateKey); } elseif ($type == self::BTWOC) { return $this->_math->btwoc($this->_math->toBinary($this->_privateKey)); } return $this->_privateKey; } /** * Check whether a private key currently exists. * * @return boolean */ public function hasPrivateKey() { return isset($this->_privateKey); } /** * Setter to pass an extension parameter which is used to create * a specific BigInteger instance for a specific extension type. * Allows manual setting of the class in case of an extension * problem or bug. * * @param string $extension * @return void */ public function setBigIntegerMath($extension = null) { /** * @see Zend_Crypt_Math */ require_once 'Zend/Crypt/Math.php'; $this->_math = new Zend_Crypt_Math($extension); } /** * In the event a private number/key has not been set by the user, * or generated by ext/openssl, a best attempt will be made to * generate a random key. Having a random number generator installed * on linux/bsd is highly recommended! The alternative is not recommended * for production unless without any other option. * * @return string */ protected function _generatePrivateKey() { $rand = $this->_math->rand($this->getGenerator(), $this->getPrime()); return $rand; } }