<?php
/**
* Lithium: the most rad php framework
*
* @copyright Copyright 2010, Union of RAD (http://union-of-rad.org)
* Copyright 2009, Cake Software Foundation, Inc. (http://cakefoundation.org)
* @license http://opensource.org/licenses/mit-license.php The MIT License
*/
namespace lithium\security;
use \Exception;
/**
* Cryptographic utility class. Includes a random number generator, and a base64
* encoder for use with DES and XDES.
*
* @see lithium\security\Password
*/
class Crypto {
/**
* A closure which, given a number of bytes, returns that amount of
* random bytes.
*
* @var Closure
*/
protected static $_source;
/**
* Generates random bytes for use in UUIDs and password salts, using
* (when available) a cryptographically strong random number generator.
*
* {{{
* $bits = Crypto::random(8); // 64 bits
* $hex = bin2hex($bits); // [0-9a-f]+
* }}}
*
* @param integer $bytes The number of random bytes to generate
* @return string Random bytes
*/
public static function random($bytes) {
$source = static::$_source ?: static::_source();
return $source($bytes);
}
/**
* Generates random bytes encoded into an `./0-9A-Za-z` alphabet, for use
* as salt when hashing passwords for instance.
*
* Note: this is not the same as `base64_encode()`, which encodes bytes
* using an `A-Za-z0-9+/` alphabet.
*
* @param integer $bytes The number of random bytes to generate.
* @return string The random bytes encoded in the `./0-9A-Za-z` alphabet.
* @see lithium\security\Crypto::random()
*/
public static function random64($bytes) {
// The alphabet used by base64_encode() is different than the one we
// should be using. When considering the meaty part of the resulting
// string, however, a bijection allows to go the from one to another.
// Given that we're working on random bytes, we can use safely use
// base64_encode() without losing any entropy, sparing ourselves the
// hassle of maintaining more code than needed.
$encoded = base64_encode(static::random($bytes));
return strtr(rtrim($encoded, '='), '+', '.');
}
/**
* Initializes Crypto::$_source using the best available random
* number generator.
*
* When available, /dev/urandom and COM gets used on *unix and
* Windows systems, respectively.
*
* If all else fails, a Mersenne Twister gets used. (Strictly
* speaking, this fallback is inadequate, but good enough.)
*
* @return Closure The random number generator.
*/
protected static function _source() {
switch (true) {
case isset(static::$_source);
return static::$_source;
case is_readable('/dev/urandom') && $fp = fopen('/dev/urandom', 'rb'):
return static::$_source = function($bytes) use (&$fp) {
return fread($fp, $bytes);
};
case class_exists('COM', 0):
// http://msdn.microsoft.com/en-us/library/aa388182(VS.85).aspx
try {
$com = new COM('CAPICOM.Utilities.1');
return static::$_source = function($bytes) use ($com) {
return base64_decode($com->GetRandom($bytes,0));
};
} catch (Exception $e) {
}
default:
// fallback to using mt_rand() if all else fails
return static::$_source = function($bytes) {
$rand = '';
for ($i = 0; $i < $bytes; $i++) {
$rand .= chr(mt_rand(0, 255));
}
return $rand;
};
}
}
}
?>