Joomla Platform  13.1
Documentation des API du framework Joomla Platform
 Tout Classes Espaces de nommage Fichiers Fonctions Variables Pages
crypt.php
Aller à la documentation de ce fichier.
1 <?php
2 /**
3  * @package Joomla.Platform
4  * @subpackage Crypt
5  *
6  * @copyright Copyright (C) 2005 - 2011 Open Source Matters, Inc. All rights reserved.
7  * @license GNU General Public License version 2 or later; see LICENSE
8  */
9 
10 defined('JPATH_PLATFORM') or die;
11 
12 /**
13  * JCrypt is a Joomla Platform class for handling basic encryption/decryption of data.
14  *
15  * @package Joomla.Platform
16  * @subpackage Crypt
17  * @since 12.1
18  */
19 class JCrypt
20 {
21  /**
22  * @var JCryptCipher The encryption cipher object.
23  * @since 12.1
24  */
25  private $_cipher;
26 
27  /**
28  * @var JCryptKey The encryption key[/pair)].
29  * @since 12.1
30  */
31  private $_key;
32 
33  /**
34  * Object Constructor takes an optional key to be used for encryption/decryption. If no key is given then the
35  * secret word from the configuration object is used.
36  *
37  * @param JCryptCipher $cipher The encryption cipher object.
38  * @param JCryptKey $key The encryption key[/pair)].
39  *
40  * @since 12.1
41  */
42  public function __construct(JCryptCipher $cipher = null, JCryptKey $key = null)
43  {
44  // Set the encryption key[/pair)].
45  $this->_key = $key;
46 
47  // Set the encryption cipher.
48  $this->_cipher = isset($cipher) ? $cipher : new JCryptCipherSimple;
49  }
50 
51  /**
52  * Method to decrypt a data string.
53  *
54  * @param string $data The encrypted string to decrypt.
55  *
56  * @return string The decrypted data string.
57  *
58  * @since 12.1
59  * @throws InvalidArgumentException
60  */
61  public function decrypt($data)
62  {
63  try
64  {
65  return $this->_cipher->decrypt($data, $this->_key);
66  }
67  catch (InvalidArgumentException $e)
68  {
69  return false;
70  }
71  }
72 
73  /**
74  * Method to encrypt a data string.
75  *
76  * @param string $data The data string to encrypt.
77  *
78  * @return string The encrypted data string.
79  *
80  * @since 12.1
81  */
82  public function encrypt($data)
83  {
84  return $this->_cipher->encrypt($data, $this->_key);
85  }
86 
87  /**
88  * Method to generate a new encryption key[/pair] object.
89  *
90  * @param array $options Key generation options.
91  *
92  * @return JCryptKey
93  *
94  * @since 12.1
95  */
96  public function generateKey(array $options = array())
97  {
98  return $this->_cipher->generateKey($options);
99  }
100 
101  /**
102  * Method to set the encryption key[/pair] object.
103  *
104  * @param JCryptKey $key The key object to set.
105  *
106  * @return JCrypt
107  *
108  * @since 12.1
109  */
110  public function setKey(JCryptKey $key)
111  {
112  $this->_key = $key;
113 
114  return $this;
115  }
116 
117  /**
118  * Generate random bytes.
119  *
120  * @param integer $length Length of the random data to generate
121  *
122  * @return string Random binary data
123  *
124  * @since 12.1
125  */
126  public static function genRandomBytes($length = 16)
127  {
128  $length = (int) $length;
129  $sslStr = '';
130 
131  /*
132  * If a secure randomness generator exists and we don't
133  * have a buggy PHP version use it.
134  */
135  if (function_exists('openssl_random_pseudo_bytes')
136  && (version_compare(PHP_VERSION, '5.3.4') >= 0 || IS_WIN))
137  {
138  $sslStr = openssl_random_pseudo_bytes($length, $strong);
139 
140  if ($strong)
141  {
142  return $sslStr;
143  }
144  }
145 
146  /*
147  * Collect any entropy available in the system along with a number
148  * of time measurements of operating system randomness.
149  */
150  $bitsPerRound = 2;
151  $maxTimeMicro = 400;
152  $shaHashLength = 20;
153  $randomStr = '';
154  $total = $length;
155 
156  // Check if we can use /dev/urandom.
157  $urandom = false;
158  $handle = null;
159 
160  // This is PHP 5.3.3 and up
161  if (function_exists('stream_set_read_buffer') && @is_readable('/dev/urandom'))
162  {
163  $handle = @fopen('/dev/urandom', 'rb');
164 
165  if ($handle)
166  {
167  $urandom = true;
168  }
169  }
170 
171  while ($length > strlen($randomStr))
172  {
173  $bytes = ($total > $shaHashLength)? $shaHashLength : $total;
174  $total -= $bytes;
175 
176  /*
177  * Collect any entropy available from the PHP system and filesystem.
178  * If we have ssl data that isn't strong, we use it once.
179  */
180  $entropy = rand() . uniqid(mt_rand(), true) . $sslStr;
181  $entropy .= implode('', @fstat(fopen(__FILE__, 'r')));
182  $entropy .= memory_get_usage();
183  $sslStr = '';
184 
185  if ($urandom)
186  {
187  stream_set_read_buffer($handle, 0);
188  $entropy .= @fread($handle, $bytes);
189  }
190  else
191  {
192  /*
193  * There is no external source of entropy so we repeat calls
194  * to mt_rand until we are assured there's real randomness in
195  * the result.
196  *
197  * Measure the time that the operations will take on average.
198  */
199  $samples = 3;
200  $duration = 0;
201 
202  for ($pass = 0; $pass < $samples; ++$pass)
203  {
204  $microStart = microtime(true) * 1000000;
205  $hash = sha1(mt_rand(), true);
206 
207  for ($count = 0; $count < 50; ++$count)
208  {
209  $hash = sha1($hash, true);
210  }
211 
212  $microEnd = microtime(true) * 1000000;
213  $entropy .= $microStart . $microEnd;
214 
215  if ($microStart >= $microEnd)
216  {
217  $microEnd += 1000000;
218  }
219 
220  $duration += $microEnd - $microStart;
221  }
222 
223  $duration = $duration / $samples;
224 
225  /*
226  * Based on the average time, determine the total rounds so that
227  * the total running time is bounded to a reasonable number.
228  */
229  $rounds = (int) (($maxTimeMicro / $duration) * 50);
230 
231  /*
232  * Take additional measurements. On average we can expect
233  * at least $bitsPerRound bits of entropy from each measurement.
234  */
235  $iter = $bytes * (int) ceil(8 / $bitsPerRound);
236 
237  for ($pass = 0; $pass < $iter; ++$pass)
238  {
239  $microStart = microtime(true);
240  $hash = sha1(mt_rand(), true);
241 
242  for ($count = 0; $count < $rounds; ++$count)
243  {
244  $hash = sha1($hash, true);
245  }
246 
247  $entropy .= $microStart . microtime(true);
248  }
249  }
250 
251  $randomStr .= sha1($entropy, true);
252  }
253 
254  if ($urandom)
255  {
256  @fclose($handle);
257  }
258 
259  return substr($randomStr, 0, $length);
260  }
261 
262  /**
263  * A timing safe comparison method. This defeats hacking
264  * attempts that use timing based attack vectors.
265  *
266  * @param string $known A known string to check against.
267  * @param string $unknown An unknown string to check.
268  *
269  * @return boolean True if the two strings are exactly the same.
270  *
271  * @since 3.2
272  */
273  public static function timingSafeCompare($known, $unknown)
274  {
275  // Prevent issues if string length is 0
276  $known .= chr(0);
277  $unknown .= chr(0);
278 
279  $knownLength = strlen($known);
280  $unknownLength = strlen($unknown);
281 
282  // Set the result to the difference between the lengths
283  $result = $knownLength - $unknownLength;
284 
285  // Note that we ALWAYS iterate over the user-supplied length to prevent leaking length info.
286  for ($i = 0; $i < $unknownLength; $i++)
287  {
288  // Using % here is a trick to prevent notices. It's safe, since if the lengths are different, $result is already non-0
289  $result |= (ord($known[$i % $knownLength]) ^ ord($unknown[$i]));
290  }
291 
292  // They are only identical strings if $result is exactly 0...
293  return $result === 0;
294  }
295 
296  /**
297  * Tests for the availability of updated crypt().
298  * Based on a method by Anthony Ferrera
299  *
300  * @return boolean True if updated crypt() is available.
301  *
302  * @note To be removed when PHP 5.3.7 or higher is the minimum supported version.
303  * @see https://github.com/ircmaxell/password_compat/blob/master/version-test.php
304  * @since 3.2
305  */
306  public static function hasStrongPasswordSupport()
307  {
308  static $pass = null;
309 
310  if (is_null($pass))
311  {
312  // Check to see whether crypt() is supported.
313  if (version_compare(PHP_VERSION, '5.3.7', '>=') === true)
314  {
315  // We have safe PHP version.
316  $pass = true;
317  }
318  else
319  {
320  // We need to test if we have patched PHP version.
321  jimport('compat.password.lib.version_test');
322  $test = new version_test;
323  $pass = $test->version_test();
324  }
325 
326  if ($pass && !defined('PASSWORD_DEFAULT'))
327  {
328  // Always make sure that the password hashing API has been defined.
329  include_once JPATH_ROOT . '/libraries/compat/password/lib/password.php';
330  }
331  }
332 
333  return $pass;
334  }
335 }