4
4
class SecureFuncs
5
5
{
6
6
7
- public static $ secret ;
7
+ /**
8
+ * @var int The key length
9
+ */
10
+ private static $ _keyLength = 32 ;
8
11
9
12
/**
10
13
* @param $input
@@ -67,7 +70,7 @@ public static function getFormToken($id, $token, $limit_time = 300)
67
70
// If time limit is set, check if isset
68
71
if ($ limit_time !== false ) {
69
72
// if time < limit time return true/false
70
- if (empty ($ _SESSION ['formtoken_time ' ][$ id ]) || $ _SESSION ['formtoken_time ' ][$ id ] < time () - $ limit_time ){
73
+ if (empty ($ _SESSION ['formtoken_time ' ][$ id ]) || $ _SESSION ['formtoken_time ' ][$ id ] < time () - $ limit_time ) {
71
74
$ valid = false ;
72
75
}
73
76
}
@@ -213,4 +216,170 @@ public static function pseudoBytes($length = 1)
213
216
}
214
217
}
215
218
219
+ /**
220
+ * Scrypt functions
221
+ *
222
+ * Based on php-scrypt - https://github.com/DomBlack/php-scrypt
223
+ */
224
+
225
+ /**
226
+ * Generates a random salt
227
+ *
228
+ * @param int $length The length of the salt
229
+ *
230
+ * @return string The salt
231
+ */
232
+ public static function generateSalt ($ length = 8 )
233
+ {
234
+ $ buffer = '' ;
235
+ $ buffer_valid = false ;
236
+ if (function_exists ('mcrypt_create_iv ' ) && !defined ('PHALANGER ' )) {
237
+ $ buffer = mcrypt_create_iv ($ length , MCRYPT_DEV_URANDOM );
238
+ if ($ buffer ) {
239
+ $ buffer_valid = true ;
240
+ }
241
+ }
242
+ if (!$ buffer_valid && function_exists ('openssl_random_pseudo_bytes ' )) {
243
+ $ cryptoStrong = false ;
244
+ $ buffer = openssl_random_pseudo_bytes ($ length , $ cryptoStrong );
245
+ if ($ buffer && $ cryptoStrong ) {
246
+ $ buffer_valid = true ;
247
+ }
248
+ }
249
+ if (!$ buffer_valid && is_readable ('/dev/urandom ' )) {
250
+ $ f = fopen ('/dev/urandom ' , 'r ' );
251
+ $ read = static ::strlen ($ buffer );
252
+ while ($ read < $ length ) {
253
+ $ buffer .= fread ($ f , $ length - $ read );
254
+ $ read = static ::strlen ($ buffer );
255
+ }
256
+ fclose ($ f );
257
+ if ($ read >= $ length ) {
258
+ $ buffer_valid = true ;
259
+ }
260
+ }
261
+ if (!$ buffer_valid || static ::strlen ($ buffer ) < $ length ) {
262
+ $ bl = static ::strlen ($ buffer );
263
+ for ($ i = 0 ; $ i < $ length ; $ i ++) {
264
+ if ($ i < $ bl ) {
265
+ $ buffer [$ i ] = $ buffer [$ i ] ^ chr (mt_rand (0 , 255 ));
266
+ } else {
267
+ $ buffer .= chr (mt_rand (0 , 255 ));
268
+ }
269
+ }
270
+ }
271
+ $ salt = str_replace (array ('+ ' , '$ ' ), array ('. ' , '' ), base64_encode ($ buffer ));
272
+
273
+ return $ salt ;
274
+ }
275
+
276
+ /**
277
+ * Create a password hash
278
+ *
279
+ * @param string $password The clear text password
280
+ * @param string|bool $salt The salt to use, or null to generate a random one
281
+ * @param int $N The CPU difficultly (must be a power of 2, > 1)
282
+ * @param int $r The memory difficultly
283
+ * @param int $p The parallel difficultly
284
+ *
285
+ * @throws \Exception
286
+ *
287
+ * @return string The hashed password
288
+ */
289
+ public static function scrypthash ($ password , $ salt = false , $ N = 16384 , $ r = 8 , $ p = 1 )
290
+ {
291
+ // Check if scrypt extension is available
292
+ if (!extension_loaded ('scrypt ' )) {
293
+ throw new \Exception ('Missing scrypt extension ' );
294
+ }
295
+
296
+ if ($ N == 0 || ($ N & ($ N - 1 )) != 0 ) {
297
+ throw new \InvalidArgumentException ("N must be > 0 and a power of 2 " );
298
+ }
299
+
300
+ if ($ N > PHP_INT_MAX / 128 / $ r ) {
301
+ throw new \InvalidArgumentException ("Parameter N is too large " );
302
+ }
303
+
304
+ if ($ r > PHP_INT_MAX / 128 / $ p ) {
305
+ throw new \InvalidArgumentException ("Parameter r is too large " );
306
+ }
307
+
308
+ if ($ salt === false ) {
309
+ $ salt = self ::generateSalt ();
310
+ } else {
311
+ // Remove dollar signs from the salt, as we use that as a separator.
312
+ $ salt = str_replace (array ('+ ' , '$ ' ), array ('. ' , '' ), base64_encode ($ salt ));
313
+ }
314
+
315
+ $ hash = scrypt ($ password , $ salt , $ N , $ r , $ p , self ::$ _keyLength );
316
+
317
+ return $ N . '$ ' . $ r . '$ ' . $ p . '$ ' . $ salt . '$ ' . $ hash ;
318
+ }
319
+
320
+ /**
321
+ * Check a clear text password against a hash
322
+ *
323
+ * @param string $password The clear text password
324
+ * @param string $hash The hashed password
325
+ *
326
+ * @throws \Exception
327
+ *
328
+ * @return boolean If the clear text matches
329
+ */
330
+ public static function scryptcheck ($ password , $ hash )
331
+ {
332
+ // Check if scrypt extension is available
333
+ if (!extension_loaded ('scrypt ' )) {
334
+ throw new \Exception ('Missing scrypt extension ' );
335
+ }
336
+
337
+ // Is there actually a hash?
338
+ if (!$ hash ) {
339
+ return false ;
340
+ }
341
+
342
+ list ($ N , $ r , $ p , $ salt , $ hash ) = explode ('$ ' , $ hash );
343
+
344
+ // No empty fields?
345
+ if (empty ($ N ) or empty ($ r ) or empty ($ p ) or empty ($ salt ) or empty ($ hash )) {
346
+ return false ;
347
+ }
348
+
349
+ // Are numeric values numeric?
350
+ if (!is_numeric ($ N ) or !is_numeric ($ r ) or !is_numeric ($ p )) {
351
+ return false ;
352
+ }
353
+
354
+ $ calculated = scrypt ($ password , $ salt , $ N , $ r , $ p , self ::$ _keyLength );
355
+
356
+ // Use compareStrings to avoid timeing attacks
357
+ return self ::compareStrings ($ hash , $ calculated );
358
+ }
359
+
360
+ /**
361
+ * Prevent timing attacks
362
+ *
363
+ * @param string $expected
364
+ * @param string $actual
365
+ *
366
+ * @return boolean If the two strings match.
367
+ */
368
+ public static function compareStrings ($ expected , $ actual )
369
+ {
370
+ $ expected = (string )$ expected ;
371
+ $ actual = (string )$ actual ;
372
+ $ lenExpected = static ::strlen ($ expected );
373
+ $ lenActual = static ::strlen ($ actual );
374
+ $ len = min ($ lenExpected , $ lenActual );
375
+
376
+ $ result = 0 ;
377
+ for ($ i = 0 ; $ i < $ len ; $ i ++) {
378
+ $ result |= ord ($ expected [$ i ]) ^ ord ($ actual [$ i ]);
379
+ }
380
+ $ result |= $ lenExpected ^ $ lenActual ;
381
+
382
+ return ($ result === 0 );
383
+ }
384
+
216
385
}
0 commit comments