diff options
Diffstat (limited to 'Auth/OpenID/CryptUtil.php')
-rw-r--r-- | Auth/OpenID/CryptUtil.php | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/Auth/OpenID/CryptUtil.php b/Auth/OpenID/CryptUtil.php new file mode 100644 index 0000000..a20a5ed --- /dev/null +++ b/Auth/OpenID/CryptUtil.php @@ -0,0 +1,855 @@ +<?php + +/** + * CryptUtil: A suite of wrapper utility functions for the OpenID + * library. + * + * PHP versions 4 and 5 + * + * LICENSE: See the COPYING file included in this distribution. + * + * @package OpenID + * @author JanRain, Inc. <openid@janrain.com> + * @copyright 2005 Janrain, Inc. + * @license http://www.gnu.org/copyleft/lesser.html LGPL + */ + +/** + * Require the HMAC/SHA-1 implementation for creating such hashes. + */ +require_once('HMACSHA1.php'); + +if (!defined('Auth_OpenID_RAND_SOURCE')) { + /** + * The filename for a source of random bytes. Define this yourself + * if you have a different source of randomness. + */ + define('Auth_OpenID_RAND_SOURCE', '/dev/urandom'); +} + +/** + * Auth_OpenID_CryptUtil houses static utility functions. + * + * @package OpenID + * @static + */ +class Auth_OpenID_CryptUtil { + /** + * Get the specified number of random bytes. + * + * Attempts to use a cryptographically secure (not predictable) + * source of randomness if available. If there is no high-entropy + * randomness source available, it will fail. As a last resort, + * for non-critical systems, define + * <code>Auth_OpenID_USE_INSECURE_RAND</code>, and the code will + * fall back on a pseudo-random number generator. + * + * @param int $num_bytes The length of the return value + * @return string $bytes random bytes + */ + function getBytes($num_bytes) + { + $bytes = ''; + $f = @fopen(Auth_OpenID_RAND_SOURCE, "r"); + if ($f === false) { + if (!defined('Auth_OpenID_USE_INSECURE_RAND')) { + trigger_error('Set Auth_OpenID_USE_INSECURE_RAND to ' . + 'continue with insecure random.', + E_USER_ERROR); + } + $bytes = ''; + for ($i = 0; $i < $num_bytes; $i += 4) { + $bytes .= pack('L', mt_rand()); + } + $bytes = substr($bytes, 0, $num_bytes); + } else { + $bytes = fread($f, $num_bytes); + fclose($f); + } + return $bytes; + } + + /** + * Computes the maximum integer value for this PHP installation. + * + * @return int $max_int_value The maximum integer value for this + * PHP installation + */ + function maxint() + { + /** + * quick-and-dirty function for PHP int size -- assumes + * largest integer is of form 2^n - 1 + */ + $to_test = pow(2, 16); + while (1) { + $last = $to_test; + $to_test = 2 * $to_test; + if (($to_test < $last) || (!is_int($to_test))) { + return($last + ($last - 1)); + } + } + } + + /** + * Computes the SHA1 hash. + * + * @param string $str The input string. + * @return string The resulting SHA1 hash, in binary form. + */ + function sha1($str) + { + return Auth_OpenID_sha1_raw($str); + } + + /** + * Computes an HMAC-SHA1 digest. + * + * @param string $key The key used to generate the HMAC-SHA1 digest + * @param string $text The text to be hashed + * @return string $digest The raw HMAC-SHA1 digest + */ + function hmacSha1($key, $text) + { + return Auth_OpenID_HMACSHA1($key, $text); + } + + /** + * Converts a base64-encoded string to its raw binary equivalent. + * + * @param string $str The base64-encoded string to decode + * @return string $raw The decoded binary data + */ + function fromBase64($str) + { + return base64_decode($str); + } + + /** + * Converts a raw binary string to its base64-encoded equivalent. + * + * @param string $str The raw binary data to encode + * @return string $raw The base64-encoded version of $str + */ + function toBase64($str) + { + return base64_encode($str); + } + + /** + * Given a long integer, returns the number converted to a binary + * string. This function accepts long integer values of arbitrary + * magnitude and uses the local large-number math library when + * available. + * + * @param integer $long The long number (can be a normal PHP + * integer or a number created by one of the available long number + * libraries) + * @return string $binary The binary version of $long + */ + function longToBinary($long) + { + + $lib =& Auth_OpenID_MathLibrary::getLibWrapper(); + + $cmp = $lib->cmp($long, 0); + if ($cmp < 0) { + print "longToBytes takes only positive integers."; + return null; + } + + if ($cmp == 0) { + return "\x00"; + } + + $bytes = array(); + + while ($lib->cmp($long, 0) > 0) { + array_unshift($bytes, $lib->mod($long, 256)); + $long = $lib->div($long, pow(2, 8)); + } + + if ($bytes && ($bytes[0] > 127)) { + array_unshift($bytes, 0); + } + + $string = ''; + foreach ($bytes as $byte) { + $string .= pack('C', $byte); + } + + return $string; + } + + /** + * Given a long integer, returns the number converted to a binary + * string. This function accepts "long" numbers within the PHP + * integer range (usually 32 bits). + * + * @param integer $long The long number (can be a normal PHP + * integer or a number created by one of the available long number + * libraries) + * @return string $binary The binary version of $long + */ + function longToBinary_platform($long) + { + + if ($long < 0) { + print "longToBytes_platform takes only positive integers."; + return null; + } + + return pack('N', $long); + } + + /** + * Given a binary string, returns the binary string converted to a + * long number. + * + * @param string $binary The binary version of a long number, + * probably as a result of calling longToBinary + * @return integer $long The long number equivalent of the binary + * string $str + */ + function binaryToLong($str) + { + $lib =& Auth_OpenID_MathLibrary::getLibWrapper(); + + if ($str === null) { + return null; + } + + // Use array_merge to return a zero-indexed array instead of a + // one-indexed array. + $bytes = array_merge(unpack('C*', $str)); + + $n = $lib->init(0); + + if ($bytes && ($bytes[0] > 127)) { + trigger_error("bytesToNum works only for positive integers.", + E_USER_WARNING); + return null; + } + + foreach ($bytes as $byte) { + $n = $lib->mul($n, pow(2, 8)); + $n = $lib->add($n, $byte); + } + + return $n; + } + + /** + * Given a binary string, returns the binary string converted to a + * long number. + * + * @param string $binary The binary version of a long number, + * probably as a result of calling longToBinary + * @return integer $long The long number equivalent of the binary + * string $str + */ + function binaryToLong_platform($str) + { + if ($str === null) { + return null; + } + + $data = unpack('Nx', $str); + return $data['x']; + } + + /** + * Converts a base64-encoded string to a long number. + * + * @param string $str A base64-encoded string + * @return integer $long A long number + */ + function base64ToLong($str) + { + return Auth_OpenID_CryptUtil::binaryToLong( + Auth_OpenID_CryptUtil::fromBase64($str)); + } + + /** + * Converts a long number to its base64-encoded representation. + * + * @param integer $long The long number to be converted + * @return string $str The base64-encoded version of $long + */ + function longToBase64($long) + { + return Auth_OpenID_CryptUtil::toBase64( + Auth_OpenID_CryptUtil::longToBinary($long)); + } + + /** + * Given two strings of equal length, computes the exclusive-OR of + * the two strings' ordinal values and returns the resulting + * string. + * + * @param string $x A string + * @param string $y A string + * @return string $result The result of $x XOR $y + */ + function strxor($x, $y) + { + if (strlen($x) != strlen($y)) { + return null; + } + + $str = ""; + for ($i = 0; $i < strlen($x); $i++) { + $str .= chr(ord($x[$i]) ^ ord($y[$i])); + } + + return $str; + } + + /** + * Reverses a string or array. + * + * @param mixed $list A string or an array + * @return mixed $result The reversed string or array + */ + function reversed($list) + { + if (is_string($list)) { + return strrev($list); + } else if (is_array($list)) { + return array_reverse($list); + } else { + return null; + } + } + + /** + * Returns a random number in the specified range. This function + * accepts $start, $stop, and $step values of arbitrary magnitude + * and will utilize the local large-number math library when + * available. + * + * @param integer $start The start of the range, or the minimum + * random number to return + * @param integer $stop The end of the range, or the maximum + * random number to return + * @param integer $step The step size, such that $result - ($step + + * * N) = $start for some N + * @return integer $result The resulting randomly-generated number + */ + function randrange($start, $stop = null, $step = 1) + { + + static $Auth_OpenID_CryptUtil_duplicate_cache = array(); + $lib =& Auth_OpenID_MathLibrary::getLibWrapper(); + + if ($stop == null) { + $stop = $start; + $start = 0; + } + + $r = $lib->div($lib->sub($stop, $start), $step); + + // DO NOT MODIFY THIS VALUE. + $rbytes = Auth_OpenID_CryptUtil::longToBinary($r); + + if (array_key_exists($rbytes, $Auth_OpenID_CryptUtil_duplicate_cache)) { + list($duplicate, $nbytes) = + $Auth_OpenID_CryptUtil_duplicate_cache[$rbytes]; + } else { + if ($rbytes[0] == "\x00") { + $nbytes = strlen($rbytes) - 1; + } else { + $nbytes = strlen($rbytes); + } + + $mxrand = $lib->pow(256, $nbytes); + + // If we get a number less than this, then it is in the + // duplicated range. + $duplicate = $lib->mod($mxrand, $r); + + if (count($Auth_OpenID_CryptUtil_duplicate_cache) > 10) { + $Auth_OpenID_CryptUtil_duplicate_cache = array(); + } + + $Auth_OpenID_CryptUtil_duplicate_cache[$rbytes] = + array($duplicate, $nbytes); + } + + while (1) { + $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes); + $n = Auth_OpenID_CryptUtil::binaryToLong($bytes); + // Keep looping if this value is in the low duplicated + // range + if ($lib->cmp($n, $duplicate) >= 0) { + break; + } + } + + return $lib->add($start, $lib->mul($lib->mod($n, $r), $step)); + } + + /** + * Returns a random number in the specified range. This function + * accepts $start, $stop, and $step values within the platform + * integer range. + * + * @param integer $start The start of the range, or the minimum + * random number to return + * @param integer $stop The end of the range, or the maximum + * random number to return + * @param integer $step The step size, such that $result - ($step + * * N) = $start for some N + * @return integer $result The resulting randomly-generated number + */ + function randrange_platform($start, $stop = null, $step = 1) + { + + static $Auth_OpenID_CryptUtil_duplicate_cache = array(); + + if ($stop == null) { + $stop = $start; + $start = 0; + } + + $r = ($stop - $start) / $step; + + // DO NOT MODIFY THIS VALUE. + $rbytes = Auth_OpenID_CryptUtil::longToBinary_platform($r); + + if (array_key_exists($rbytes, $Auth_OpenID_CryptUtil_duplicate_cache)) { + list($duplicate, $nbytes) = + $Auth_OpenID_CryptUtil_duplicate_cache[$rbytes]; + } else { + if ($rbytes[0] == "\x00") { + $nbytes = strlen($rbytes) - 1; + } else { + $nbytes = strlen($rbytes); + } + + $mxrand = pow(256, $nbytes); + + // If we get a number less than this, then it is in the + // duplicated range. + $duplicate = $mxrand % $r; + + if (count($Auth_OpenID_CryptUtil_duplicate_cache) > 10) { + $Auth_OpenID_CryptUtil_duplicate_cache = array(); + } + + $Auth_OpenID_CryptUtil_duplicate_cache[$rbytes] = + array($duplicate, $nbytes); + } + + while (1) { + $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes); + $n = Auth_OpenID_CryptUtil::binaryToLong_platform($bytes); + // Keep looping if this value is in the low duplicated + // range + if ($n >= $duplicate) { + break; + } + } + + return $start + ($n % $r) * $step; + } + + /** + * Produce a string of length random bytes, chosen from chrs. If + * $chrs is null, the resulting string may contain any characters. + * + * @param integer $length The length of the resulting + * randomly-generated string + * @param string $chrs A string of characters from which to choose + * to build the new string + * @return string $result A string of randomly-chosen characters + * from $chrs + */ + function randomString($length, $chrs = null) + { + if ($chrs === null) { + return Auth_OpenID_CryptUtil::getBytes($length); + } else { + $n = strlen($chrs); + $str = ""; + for ($i = 0; $i < $length; $i++) { + $str .= $chrs[Auth_OpenID_CryptUtil::randrange_platform($n)]; + } + return $str; + } + } +} + +/** + * Exposes math library functionality. + * + * Auth_OpenID_MathWrapper is a base class that defines the interface + * to a math library like GMP or BCmath. This library will attempt to + * use an available long number implementation. If a library like GMP + * is found, the appropriate Auth_OpenID_MathWrapper subclass will be + * instantiated and used for mathematics operations on large numbers. + * This base class wraps only native PHP functionality. See + * Auth_OpenID_MathWrapper subclasses for access to particular long + * number implementations. + * + * @package OpenID + */ +class Auth_OpenID_MathWrapper { + /** + * The type of the Auth_OpenID_MathWrapper class. This value + * describes the library or module being wrapped. Users of + * Auth_OpenID_MathWrapper instances should check this value if + * they care about the type of math functionality being exposed. + */ + var $type = 'dumb'; + + /** + * Returns a random number in the specified range. + */ + function random($min, $max) + { + return mt_rand($min, $max); + } + + /** + * Returns $base raised to the $exponent power. + */ + function pow($base, $exponent) + { + return pow($base, $exponent); + } + + /** + * Returns the sum of $x and $y. + */ + function add($x, $y) + { + return $x + $y; + } + + /** + * Returns -1 if $x < $y, 0 if $x == $y, and 1 if $x > $y. + */ + function cmp($x, $y) + { + if ($x > $y) { + return 1; + } else if ($x < $y) { + return -1; + } else { + return 0; + } + } + + /** + * "Initializes" a new number. This may simply return the + * specified number or it may call a library function for this + * purpose. The base may be ignored depending on the + * implementation. + */ + function init($number, $base = 10) + { + return $number; + } + + /** + * Returns the result of $base mod $modulus. + */ + function mod($base, $modulus) + { + return $base % $modulus; + } + + /** + * Returns the product of $x and $y. + */ + function mul($x, $y) + { + return $x * $y; + } + + /** + * Returns the difference of $x and $y. + */ + function sub($x, $y) + { + return $x - $y; + } + + /** + * Returns $x / $y. + */ + function div($x, $y) + { + return $x / $y; + } + + /** + * Returns ($base to the $exponent power) mod $modulus. In some + * long number implementations, this may be optimized. This + * placeholder implementation performs it manually. + */ + function powmod($base, $exponent, $modulus) + { + $square = $this->mod($base, $modulus); + $result = '1'; + while($this->cmp($exponent, 0) > 0) { + if ($this->mod($exponent, 2)) { + $result = $this->mod($this->mul($result, $square), $modulus); + } + $square = $this->mod($this->mul($square, $square), $modulus); + $exponent = $this->div($exponent, 2); + } + return $result; + } +} + +/** + * Exposes BCmath math library functionality. + * + * Auth_OpenID_BcMathWrapper implements the Auth_OpenID_MathWrapper + * interface and wraps the functionality provided by the BCMath + * library. + * + * @package OpenID + */ +class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathWrapper { + var $type = 'bcmath'; + + function random($min, $max) + { + return mt_rand($min, $max); + } + + function add($x, $y) + { + return bcadd($x, $y); + } + + function sub($x, $y) + { + return bcsub($x, $y); + } + + function pow($base, $exponent) + { + return bcpow($base, $exponent); + } + + function cmp($x, $y) + { + return bccomp($x, $y); + } + + function init($number, $base = 10) + { + return $number; + } + + function mod($base, $modulus) + { + return bcmod($base, $modulus); + } + + function mul($x, $y) + { + return bcmul($x, $y); + } + + function div($x, $y) + { + return bcdiv($x, $y); + } + + function powmod($base, $exponent, $modulus) + { + if (false && function_exists('bcpowmod')) { + return bcpowmod($base, $exponent, $modulus); + } else { + return parent::powmod($base, $exponent, $modulus); + } + } + +} + +/** + * Exposes GMP math library functionality. + * + * Auth_OpenID_GmpMathWrapper implements the Auth_OpenID_MathWrapper + * interface and wraps the functionality provided by the GMP library. + * + * @package OpenID + */ +class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathWrapper { + var $type = 'gmp'; + + function random($min, $max) + { + return gmp_random($max); + } + + function add($x, $y) + { + return gmp_add($x, $y); + } + + function sub($x, $y) + { + return gmp_sub($x, $y); + } + + function pow($base, $exponent) + { + return gmp_pow($base, $exponent); + } + + function cmp($x, $y) + { + return gmp_cmp($x, $y); + } + + function init($number, $base = 10) + { + return gmp_init($number, $base); + } + + function mod($base, $modulus) + { + return gmp_mod($base, $modulus); + } + + function mul($x, $y) + { + return gmp_mul($x, $y); + } + + function div($x, $y) + { + return gmp_div_q($x, $y); + } + + function powmod($base, $exponent, $modulus) + { + return gmp_powm($base, $exponent, $modulus); + } +} + +$_Auth_OpenID___mathLibrary = null; + +/** + * Define the supported extensions. An extension array has keys + * 'modules', 'extension', and 'class'. 'modules' is an array of PHP + * module names which the loading code will attempt to load. These + * values will be suffixed with a library file extension (e.g. ".so"). + * 'extension' is the name of a PHP extension which will be tested + * before 'modules' are loaded. 'class' is the string name of a + * Auth_OpenID_MathWrapper subclass which should be instantiated if a + * given extension is present. + * + * You can define new math library implementations and add them to + * this array. + */ +$_Auth_OpenID_supported_extensions = array( + array('modules' => array('gmp', 'php_gmp'), + 'extension' => 'gmp', + 'class' => 'Auth_OpenID_GmpMathWrapper'), + array('modules' => array('bcmath', 'php_bcmath'), + 'extension' => 'bcmath', + 'class' => 'Auth_OpenID_BcMathWrapper') + ); + + /** + * Auth_OpenID_MathLibrary checks for the presence of long number + * extension modules and returns an instance of Auth_OpenID_MathWrapper + * which exposes the module's functionality. + * + * @static + * @package OpenID + */ +class Auth_OpenID_MathLibrary { + + /** + * A method to access an available long number implementation. + * + * Checks for the existence of an extension module described by + * the local Auth_OpenID_supported_extensions array and returns an + * instance of a wrapper for that extension module. If no + * extension module is found, an instance of + * Auth_OpenID_MathWrapper is returned, which wraps the native PHP + * integer implementation. The proper calling convention for this + * method is $lib =& Auth_OpenID_MathLibrary::getLibWrapper(). + * + * This function checks for the existence of specific long number + * implementations in the following order: GMP followed by BCmath. + * + * @return Auth_OpenID_MathWrapper $instance An instance of + * Auth_OpenID_MathWrapper or one of its subclasses + */ + function &getLibWrapper() + { + // The instance of Auth_OpenID_MathWrapper that we choose to + // supply will be stored here, so that subseqent calls to this + // method will return a reference to the same object. + global $_Auth_OpenID___mathLibrary; + + if (defined('Auth_OpenID_NO_MATH_SUPPORT')) { + $_Auth_OpenID___mathLibrary = null; + return $_Auth_OpenID___mathLibrary; + } + + global $_Auth_OpenID_supported_extensions; + + // If this method has not been called before, look at + // $Auth_OpenID_supported_extensions and try to find an + // extension that works. + if (!$_Auth_OpenID___mathLibrary) { + $loaded = false; + $tried = array(); + + foreach ($_Auth_OpenID_supported_extensions as $extension) { + $tried[] = $extension['extension']; + + // See if the extension specified is already loaded. + if ($extension['extension'] && + extension_loaded($extension['extension'])) { + $loaded = true; + } + + // Try to load dynamic modules. + if (!$loaded) { + foreach ($extension['modules'] as $module) { + if (@dl($module . "." . PHP_SHLIB_SUFFIX)) { + $loaded = true; + break; + } + } + } + + // If the load succeeded, supply an instance of + // Auth_OpenID_MathWrapper which wraps the specified + // module's functionality. + if ($loaded) { + $classname = $extension['class']; + $_Auth_OpenID___mathLibrary = new $classname(); + break; + } + } + + // If no extensions were found, fall back to + // Auth_OpenID_MathWrapper so at least some platform-size + // math can be performed. + if (!$_Auth_OpenID___mathLibrary) { + $triedstr = implode(", ", $tried); + $msg = 'This PHP installation has no big integer math ' . + 'library. Define Auth_OpenID_NO_MATH_SUPPORT to use ' . + 'this library in dumb mode. Tried: ' . $triedstr; + trigger_error($msg, E_USER_ERROR); + } + } + + return $_Auth_OpenID___mathLibrary; + } +} + +?>
\ No newline at end of file |