|
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 } |