PHP OpenID consumer
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

bigmath.inc.php 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. <?php
  2. /**
  3. * BigMath: A math library wrapper that abstracts out the underlying
  4. * long integer library.
  5. *
  6. * Original code (C) 2005 JanRain <openid@janrain.com>
  7. * Modifications (C) 2007 Stephen Bounds.
  8. * Further modifications (C) 2008-2010 Chris Smith.
  9. *
  10. * Licensed under the LGPL.
  11. */
  12. /**
  13. * Base BigMath class which will be extended by a big-integer math library
  14. * such as bcmath or gmp.
  15. */
  16. abstract class BigMath {
  17. /** File handle for our random data source. */
  18. protected $randsource = false;
  19. /** Duplicate cache for rand(). */
  20. protected $duplicate_cache = array();
  21. /** Singleton reference to our bigmath class. */
  22. private static $me = null;
  23. /**
  24. * Converts the specified positive integer to the shortest possible
  25. * big-endian, two's complement representation.
  26. *
  27. * @param long The integer to be converted
  28. * @return the btwoc representation of the integer
  29. */
  30. public function btwoc($long) {
  31. $cmp = $this->cmp($long, 0);
  32. if ($cmp < 0) {
  33. throw new Exception('$long must be a positive integer.');
  34. } else if ($cmp == 0) {
  35. return "\x00";
  36. }
  37. $bytes = array();
  38. while ($this->cmp($long, 0) > 0) {
  39. array_unshift($bytes, $this->mod($long, 256));
  40. $long = $this->div($long, 256);
  41. }
  42. if ($bytes && ($bytes[0] > 127)) {
  43. array_unshift($bytes, 0);
  44. }
  45. // Convert to \xHH\xHH... format and return
  46. $string = '';
  47. foreach ($bytes as $byte) {
  48. $string .= pack('C', $byte);
  49. }
  50. return $string;
  51. }
  52. /**
  53. * Converts the specified btwoc representation of an integer back to the
  54. * original integer.
  55. *
  56. * @param str The btwoc representation to be "undone"
  57. * @return The corresponding integer
  58. */
  59. public function btwoc_undo($str) {
  60. if ($str == null) {
  61. return null;
  62. }
  63. $bytes = array_values(unpack('C*', $str));
  64. $n = $this->init(0);
  65. if ($bytes && ($bytes[0] > 127)) {
  66. throw new Exception('$str must represent a positive integer');
  67. }
  68. foreach ($bytes as $byte) {
  69. $n = $this->mul($n, 256);
  70. $n = $this->add($n, $byte);
  71. }
  72. return $n;
  73. }
  74. /**
  75. * Returns a random number up to the specified maximum.
  76. *
  77. * @param max The maximum value to return
  78. * @return A random number between 0 and the specified max
  79. */
  80. public function rand($max) {
  81. // Used as the key for the duplicate cache
  82. $rbytes = $this->btwoc($max);
  83. if (array_key_exists($rbytes, $this->duplicate_cache)) {
  84. list($duplicate, $nbytes) = $this->duplicate_cache[$rbytes];
  85. } else {
  86. if ($rbytes[0] == "\x00") {
  87. $nbytes = strlen($rbytes) - 1;
  88. } else {
  89. $nbytes = strlen($rbytes);
  90. }
  91. $mxrand = $this->pow(256, $nbytes);
  92. // If we get a number less than this, then it is in the
  93. // duplicated range.
  94. $duplicate = $this->mod($mxrand, $max);
  95. if (count($this->duplicate_cache) > 10) {
  96. $this->duplicate_cache = array();
  97. }
  98. $this->duplicate_cache[$rbytes] = array($duplicate, $nbytes);
  99. }
  100. do {
  101. $bytes = "\x00" . $this->getRandomBytes($nbytes);
  102. $n = $this->btwoc_undo($bytes);
  103. // Keep looping if this value is in the low duplicated range
  104. } while ($this->cmp($n, $duplicate) < 0);
  105. return $this->mod($n, $max);
  106. }
  107. /**
  108. * Get the specified number of random bytes.
  109. *
  110. * Attempts to use a cryptographically secure (not predictable)
  111. * source of randomness. If there is no high-entropy
  112. * randomness source available, it will fail.
  113. * @param num_bytes The number of bytes to retrieve
  114. * @return The specified number of random bytes
  115. */
  116. public function getRandomBytes($num_bytes) {
  117. if (!$this->randsource) {
  118. $this->randsource = @fopen('/dev/urandom', 'r');
  119. }
  120. if ($this->randsource) {
  121. return fread($this->randsource, $num_bytes);
  122. } else {
  123. // pseudorandom used
  124. $bytes = '';
  125. for ($i = 0; $i < $num_bytes; $i += 4) {
  126. $bytes .= pack('L', mt_rand());
  127. }
  128. return substr($bytes, 0, $num_bytes);
  129. }
  130. }
  131. public abstract function init($number, $base = 10);
  132. public abstract function add($x, $y);
  133. public abstract function sub($x, $y);
  134. public abstract function mul($x, $y);
  135. public abstract function div($x, $y);
  136. public abstract function cmp($x, $y);
  137. public abstract function mod($base, $modulus);
  138. public abstract function pow($base, $exponent);
  139. public abstract function powmod($base, $exponent, $modulus);
  140. public abstract function toString($num);
  141. /**
  142. * Detect which math library is available
  143. *
  144. * @return The extension details of the first available extension,
  145. * or false if no extensions are available.
  146. */
  147. private static function BigMath_Detect() {
  148. $extensions = array(
  149. array('modules' => array('gmp', 'php_gmp'),
  150. 'extension' => 'gmp',
  151. 'class' => 'BigMath_GmpMathWrapper'),
  152. array('modules' => array('bcmath', 'php_bcmath'),
  153. 'extension' => 'bcmath',
  154. 'class' => 'BigMath_BcMathWrapper')
  155. );
  156. $loaded = false;
  157. foreach ($extensions as $ext) {
  158. // See if the extension specified is already loaded.
  159. if ($ext['extension'] && extension_loaded($ext['extension'])) {
  160. $loaded = true;
  161. }
  162. // Try to load dynamic modules.
  163. if (!$loaded && function_exists('dl')) {
  164. foreach ($ext['modules'] as $module) {
  165. if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
  166. $loaded = true;
  167. break;
  168. }
  169. }
  170. }
  171. if ($loaded) {
  172. return $ext;
  173. }
  174. }
  175. return false;
  176. }
  177. /**
  178. * Returns a singleton instance of the best possible BigMath class.
  179. *
  180. * @return A singleton instance to a BigMath class.
  181. */
  182. public static function &getBigMath() {
  183. if (self::$me == null) {
  184. $ext = self::BigMath_Detect();
  185. $class = $ext['class'];
  186. self::$me = new $class();
  187. }
  188. return self::$me;
  189. }
  190. }
  191. /**
  192. * Exposes BCmath math library functionality.
  193. */
  194. class BigMath_BcMathWrapper extends BigMath {
  195. public function init($number, $base = 10) { return $number; }
  196. public function add($x, $y) { return bcadd($x, $y); }
  197. public function sub($x, $y) { return bcsub($x, $y); }
  198. public function mul($x, $y) { return bcmul($x, $y); }
  199. public function div($x, $y) { return bcdiv($x, $y); }
  200. public function cmp($x, $y) { return bccomp($x, $y); }
  201. public function mod($base, $modulus) { return bcmod($base, $modulus); }
  202. public function pow($base, $exponent) { return bcpow($base, $exponent); }
  203. public function powmod($base, $exponent, $modulus) { return bcpowmod($base, $exponent, $modulus); }
  204. public function toString($num) { return $num; }
  205. }
  206. /**
  207. * Exposes GMP math library functionality.
  208. */
  209. class BigMath_GmpMathWrapper extends BigMath {
  210. public function init($number, $base = 10) { return gmp_init($number, $base); }
  211. public function add($x, $y) { return gmp_add($x, $y); }
  212. public function sub($x, $y) { return gmp_sub($x, $y); }
  213. public function mul($x, $y) { return gmp_mul($x, $y); }
  214. public function div($x, $y) { return gmp_div_q($x, $y); }
  215. public function cmp($x, $y) { return gmp_cmp($x, $y); }
  216. public function mod($base, $modulus) { return gmp_mod($base, $modulus); }
  217. public function pow($base, $exponent) { return gmp_pow($base, $exponent); }
  218. public function powmod($base, $exponent, $modulus) { return gmp_powm($base, $exponent, $modulus); }
  219. public function toString($num) { return gmp_strval($num); }
  220. }
  221. ?>