Elgg  Version 1.11
ElggCrypto.php
Go to the documentation of this file.
1 <?php
10 class ElggCrypto {
11 
15  const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789';
16 
20  const CHARS_HEX = '0123456789abcdef';
21 
55  public function getRandomBytes($length) {
56  $SSLstr = '4'; // http://xkcd.com/221/
57 
62  if (function_exists('openssl_random_pseudo_bytes') && substr(PHP_OS, 0, 3) !== 'WIN') {
63  $SSLstr = openssl_random_pseudo_bytes($length, $strong);
64  if ($strong) {
65  return $SSLstr;
66  }
67  }
68 
74  if (function_exists('mcrypt_create_iv') && substr(PHP_OS, 0, 3) !== 'WIN') {
75  $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
76  if ($str !== false) {
77  return $str;
78  }
79  }
80 
88  $str = '';
89  $bits_per_round = 2; // bits of entropy collected in each clock drift round
90  $msec_per_round = 400; // expected running time of each round in microseconds
91  $hash_len = 20; // SHA-1 Hash length
92  $total = $length; // total bytes of entropy to collect
93 
94  $handle = @fopen('/dev/urandom', 'rb');
95  if ($handle && function_exists('stream_set_read_buffer')) {
96  @stream_set_read_buffer($handle, 0);
97  }
98 
99  do {
100  $bytes = ($total > $hash_len) ? $hash_len : $total;
101  $total -= $bytes;
102 
103  //collect any entropy available from the PHP system and filesystem
104  $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
105  $entropy .= implode('', @fstat(@fopen(__FILE__, 'r')));
106  $entropy .= memory_get_usage() . getmypid();
107  $entropy .= serialize($_ENV) . serialize($_SERVER);
108  if (function_exists('posix_times')) {
109  $entropy .= serialize(posix_times());
110  }
111  if (function_exists('zend_thread_id')) {
112  $entropy .= zend_thread_id();
113  }
114 
115  if ($handle) {
116  $entropy .= @fread($handle, $bytes);
117  } else {
118  // Measure the time that the operations will take on average
119  for ($i = 0; $i < 3; $i++) {
120  $c1 = microtime(true);
121  $var = sha1(mt_rand());
122  for ($j = 0; $j < 50; $j++) {
123  $var = sha1($var);
124  }
125  $c2 = microtime(true);
126  $entropy .= $c1 . $c2;
127  }
128 
129  // Based on the above measurement determine the total rounds
130  // in order to bound the total running time.
131  $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
132 
133  // Take the additional measurements. On average we can expect
134  // at least $bits_per_round bits of entropy from each measurement.
135  $iter = $bytes * (int) (ceil(8 / $bits_per_round));
136 
137  for ($i = 0; $i < $iter; $i++) {
138  $c1 = microtime();
139  $var = sha1(mt_rand());
140  for ($j = 0; $j < $rounds; $j++) {
141  $var = sha1($var);
142  }
143  $c2 = microtime();
144  $entropy .= $c1 . $c2;
145  }
146  }
147 
148  // We assume sha1 is a deterministic extractor for the $entropy variable.
149  $str .= sha1($entropy, true);
150 
151  } while ($length > strlen($str));
152 
153  if ($handle) {
154  @fclose($handle);
155  }
156 
157  return substr($str, 0, $length);
158  }
159 
169  public function getHmac($data, $algo = 'sha256', $key = '') {
170  if (!$key) {
171  $key = _elgg_services()->siteSecret->get(true);
172  }
173  return new Elgg\Security\Hmac($key, [$this, 'areEqual'], $data, $algo);
174  }
175 
195  public function getRandomString($length, $chars = null) {
196  if ($length < 1) {
197  throw new \InvalidArgumentException('Length should be >= 1');
198  }
199 
200  if (empty($chars)) {
201  $numBytes = ceil($length * 0.75);
202  $bytes = $this->getRandomBytes($numBytes);
203  $string = substr(rtrim(base64_encode($bytes), '='), 0, $length);
204 
205  // Base64 URL
206  return strtr($string, '+/', '-_');
207  }
208 
209  if ($chars == self::CHARS_HEX) {
210  // hex is easy
211  $bytes = $this->getRandomBytes(ceil($length / 2));
212  return substr(bin2hex($bytes), 0, $length);
213  }
214 
215  $listLen = strlen($chars);
216 
217  if ($listLen == 1) {
218  return str_repeat($chars, $length);
219  }
220 
221  $bytes = $this->getRandomBytes($length);
222  $pos = 0;
223  $result = '';
224  for ($i = 0; $i < $length; $i++) {
225  $pos = ($pos + ord($bytes[$i])) % $listLen;
226  $result .= $chars[$pos];
227  }
228 
229  return $result;
230  }
231 
245  public function areEqual($str1, $str2) {
246  $len1 = $this->strlen($str1);
247  $len2 = $this->strlen($str2);
248  if ($len1 !== $len2) {
249  return false;
250  }
251 
252  $status = 0;
253  for ($i = 0; $i < $len1; $i++) {
254  $status |= (ord($str1[$i]) ^ ord($str2[$i]));
255  }
256 
257  return $status === 0;
258  }
259 
278  protected function strlen($binary_string) {
279  if (function_exists('mb_strlen')) {
280  return mb_strlen($binary_string, '8bit');
281  }
282  return strlen($binary_string);
283  }
284 }
areEqual($str1, $str2)
Are two strings equal (compared in constant time)?
Definition: ElggCrypto.php:245
const CHARS_PASSWORD
Character set for temp passwords (no risk of embedded profanity/glyphs that look similar) ...
Definition: ElggCrypto.php:15
getRandomBytes($length)
Generate a string of highly randomized bytes (over the full 8-bit range).
Definition: ElggCrypto.php:55
$data
Definition: opendd.php:13
$string
$key
Definition: summary.php:34
Component for creating HMAC tokens.
Definition: Hmac.php:7
_elgg_services()
Definition: autoloader.php:14
const CHARS_HEX
Character set for hexadecimal.
Definition: ElggCrypto.php:20
getRandomString($length, $chars=null)
Generate a random string of specified length.
Definition: ElggCrypto.php:195
strlen($binary_string)
Count the number of bytes in a string.
Definition: ElggCrypto.php:278
getHmac($data, $algo= 'sha256', $key= '')
Get an HMAC token builder/validator object.
Definition: ElggCrypto.php:169