Subversion Repositories Applications.annuaire

Rev

Rev 42 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
42 aurelien 1
<?php
2
 
3
/**
4
 * BigMath: A math library wrapper that abstracts out the underlying
5
 * long integer library.
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * LICENSE: See the COPYING file included in this distribution.
10
 *
11
 * @access private
12
 * @package OpenID
13
 * @author JanRain, Inc. <openid@janrain.com>
14
 * @copyright 2005 Janrain, Inc.
15
 * @license http://www.gnu.org/copyleft/lesser.html LGPL
16
 */
17
 
18
/**
19
 * Needed for random number generation
20
 */
21
require_once 'Auth/OpenID/CryptUtil.php';
22
 
23
/**
24
 * The superclass of all big-integer math implementations
25
 * @access private
26
 * @package OpenID
27
 */
28
class Auth_OpenID_MathLibrary {
29
    /**
30
     * Given a long integer, returns the number converted to a binary
31
     * string.  This function accepts long integer values of arbitrary
32
     * magnitude and uses the local large-number math library when
33
     * available.
34
     *
35
     * @param integer $long The long number (can be a normal PHP
36
     * integer or a number created by one of the available long number
37
     * libraries)
38
     * @return string $binary The binary version of $long
39
     */
40
    function longToBinary($long)
41
    {
42
        $cmp = $this->cmp($long, 0);
43
        if ($cmp < 0) {
44
            $msg = __FUNCTION__ . " takes only positive integers.";
45
            trigger_error($msg, E_USER_ERROR);
46
            return null;
47
        }
48
 
49
        if ($cmp == 0) {
50
            return "\x00";
51
        }
52
 
53
        $bytes = array();
54
 
55
        while ($this->cmp($long, 0) > 0) {
56
            array_unshift($bytes, $this->mod($long, 256));
57
            $long = $this->div($long, pow(2, 8));
58
        }
59
 
60
        if ($bytes && ($bytes[0] > 127)) {
61
            array_unshift($bytes, 0);
62
        }
63
 
64
        $string = '';
65
        foreach ($bytes as $byte) {
66
            $string .= pack('C', $byte);
67
        }
68
 
69
        return $string;
70
    }
71
 
72
    /**
73
     * Given a binary string, returns the binary string converted to a
74
     * long number.
75
     *
76
     * @param string $binary The binary version of a long number,
77
     * probably as a result of calling longToBinary
78
     * @return integer $long The long number equivalent of the binary
79
     * string $str
80
     */
81
    function binaryToLong($str)
82
    {
83
        if ($str === null) {
84
            return null;
85
        }
86
 
87
        // Use array_merge to return a zero-indexed array instead of a
88
        // one-indexed array.
89
        $bytes = array_merge(unpack('C*', $str));
90
 
91
        $n = $this->init(0);
92
 
93
        if ($bytes && ($bytes[0] > 127)) {
94
            trigger_error("bytesToNum works only for positive integers.",
95
                          E_USER_WARNING);
96
            return null;
97
        }
98
 
99
        foreach ($bytes as $byte) {
100
            $n = $this->mul($n, pow(2, 8));
101
            $n = $this->add($n, $byte);
102
        }
103
 
104
        return $n;
105
    }
106
 
107
    function base64ToLong($str)
108
    {
109
        $b64 = base64_decode($str);
110
 
111
        if ($b64 === false) {
112
            return false;
113
        }
114
 
115
        return $this->binaryToLong($b64);
116
    }
117
 
118
    function longToBase64($str)
119
    {
120
        return base64_encode($this->longToBinary($str));
121
    }
122
 
123
    /**
124
     * Returns a random number in the specified range.  This function
125
     * accepts $start, $stop, and $step values of arbitrary magnitude
126
     * and will utilize the local large-number math library when
127
     * available.
128
     *
129
     * @param integer $start The start of the range, or the minimum
130
     * random number to return
131
     * @param integer $stop The end of the range, or the maximum
132
     * random number to return
133
     * @param integer $step The step size, such that $result - ($step
134
     * * N) = $start for some N
135
     * @return integer $result The resulting randomly-generated number
136
     */
137
    function rand($stop)
138
    {
139
        static $duplicate_cache = array();
140
 
141
        // Used as the key for the duplicate cache
142
        $rbytes = $this->longToBinary($stop);
143
 
144
        if (array_key_exists($rbytes, $duplicate_cache)) {
145
            list($duplicate, $nbytes) = $duplicate_cache[$rbytes];
146
        } else {
147
            if ($rbytes[0] == "\x00") {
148
                $nbytes = strlen($rbytes) - 1;
149
            } else {
150
                $nbytes = strlen($rbytes);
151
            }
152
 
153
            $mxrand = $this->pow(256, $nbytes);
154
 
155
            // If we get a number less than this, then it is in the
156
            // duplicated range.
157
            $duplicate = $this->mod($mxrand, $stop);
158
 
159
            if (count($duplicate_cache) > 10) {
160
                $duplicate_cache = array();
161
            }
162
 
163
            $duplicate_cache[$rbytes] = array($duplicate, $nbytes);
164
        }
165
 
166
        do {
167
            $bytes = "\x00" . Auth_OpenID_CryptUtil::getBytes($nbytes);
168
            $n = $this->binaryToLong($bytes);
169
            // Keep looping if this value is in the low duplicated range
170
        } while ($this->cmp($n, $duplicate) < 0);
171
 
172
        return $this->mod($n, $stop);
173
    }
174
}
175
 
176
/**
177
 * Exposes BCmath math library functionality.
178
 *
179
 * {@link Auth_OpenID_BcMathWrapper} wraps the functionality provided
180
 * by the BCMath extension.
181
 *
182
 * @access private
183
 * @package OpenID
184
 */
185
class Auth_OpenID_BcMathWrapper extends Auth_OpenID_MathLibrary{
186
    var $type = 'bcmath';
187
 
188
    function add($x, $y)
189
    {
190
        return bcadd($x, $y);
191
    }
192
 
193
    function sub($x, $y)
194
    {
195
        return bcsub($x, $y);
196
    }
197
 
198
    function pow($base, $exponent)
199
    {
200
        return bcpow($base, $exponent);
201
    }
202
 
203
    function cmp($x, $y)
204
    {
205
        return bccomp($x, $y);
206
    }
207
 
208
    function init($number, $base = 10)
209
    {
210
        return $number;
211
    }
212
 
213
    function mod($base, $modulus)
214
    {
215
        return bcmod($base, $modulus);
216
    }
217
 
218
    function mul($x, $y)
219
    {
220
        return bcmul($x, $y);
221
    }
222
 
223
    function div($x, $y)
224
    {
225
        return bcdiv($x, $y);
226
    }
227
 
228
    /**
229
     * Same as bcpowmod when bcpowmod is missing
230
     *
231
     * @access private
232
     */
233
    function _powmod($base, $exponent, $modulus)
234
    {
235
        $square = $this->mod($base, $modulus);
236
        $result = 1;
237
        while($this->cmp($exponent, 0) > 0) {
238
            if ($this->mod($exponent, 2)) {
239
                $result = $this->mod($this->mul($result, $square), $modulus);
240
            }
241
            $square = $this->mod($this->mul($square, $square), $modulus);
242
            $exponent = $this->div($exponent, 2);
243
        }
244
        return $result;
245
    }
246
 
247
    function powmod($base, $exponent, $modulus)
248
    {
249
        if (function_exists('bcpowmod')) {
250
            return bcpowmod($base, $exponent, $modulus);
251
        } else {
252
            return $this->_powmod($base, $exponent, $modulus);
253
        }
254
    }
255
 
256
    function toString($num)
257
    {
258
        return $num;
259
    }
260
}
261
 
262
/**
263
 * Exposes GMP math library functionality.
264
 *
265
 * {@link Auth_OpenID_GmpMathWrapper} wraps the functionality provided
266
 * by the GMP extension.
267
 *
268
 * @access private
269
 * @package OpenID
270
 */
271
class Auth_OpenID_GmpMathWrapper extends Auth_OpenID_MathLibrary{
272
    var $type = 'gmp';
273
 
274
    function add($x, $y)
275
    {
276
        return gmp_add($x, $y);
277
    }
278
 
279
    function sub($x, $y)
280
    {
281
        return gmp_sub($x, $y);
282
    }
283
 
284
    function pow($base, $exponent)
285
    {
286
        return gmp_pow($base, $exponent);
287
    }
288
 
289
    function cmp($x, $y)
290
    {
291
        return gmp_cmp($x, $y);
292
    }
293
 
294
    function init($number, $base = 10)
295
    {
296
        return gmp_init($number, $base);
297
    }
298
 
299
    function mod($base, $modulus)
300
    {
301
        return gmp_mod($base, $modulus);
302
    }
303
 
304
    function mul($x, $y)
305
    {
306
        return gmp_mul($x, $y);
307
    }
308
 
309
    function div($x, $y)
310
    {
311
        return gmp_div_q($x, $y);
312
    }
313
 
314
    function powmod($base, $exponent, $modulus)
315
    {
316
        return gmp_powm($base, $exponent, $modulus);
317
    }
318
 
319
    function toString($num)
320
    {
321
        return gmp_strval($num);
322
    }
323
}
324
 
325
/**
326
 * Define the supported extensions.  An extension array has keys
327
 * 'modules', 'extension', and 'class'.  'modules' is an array of PHP
328
 * module names which the loading code will attempt to load.  These
329
 * values will be suffixed with a library file extension (e.g. ".so").
330
 * 'extension' is the name of a PHP extension which will be tested
331
 * before 'modules' are loaded.  'class' is the string name of a
332
 * {@link Auth_OpenID_MathWrapper} subclass which should be
333
 * instantiated if a given extension is present.
334
 *
335
 * You can define new math library implementations and add them to
336
 * this array.
337
 */
338
global $_Auth_OpenID_math_extensions;
339
$_Auth_OpenID_math_extensions = array(
340
    array('modules' => array('gmp', 'php_gmp'),
341
          'extension' => 'gmp',
342
          'class' => 'Auth_OpenID_GmpMathWrapper'),
343
    array('modules' => array('bcmath', 'php_bcmath'),
344
          'extension' => 'bcmath',
345
          'class' => 'Auth_OpenID_BcMathWrapper')
346
    );
347
 
348
/**
349
 * Detect which (if any) math library is available
350
 */
351
function Auth_OpenID_detectMathLibrary($exts)
352
{
353
    $loaded = false;
354
 
355
    foreach ($exts as $extension) {
356
        // See if the extension specified is already loaded.
357
        if ($extension['extension'] &&
358
            extension_loaded($extension['extension'])) {
359
            $loaded = true;
360
        }
361
 
362
        // Try to load dynamic modules.
363
        if (!$loaded) {
364
            foreach ($extension['modules'] as $module) {
365
                if (@dl($module . "." . PHP_SHLIB_SUFFIX)) {
366
                    $loaded = true;
367
                    break;
368
                }
369
            }
370
        }
371
 
372
        // If the load succeeded, supply an instance of
373
        // Auth_OpenID_MathWrapper which wraps the specified
374
        // module's functionality.
375
        if ($loaded) {
376
            return $extension;
377
        }
378
    }
379
 
380
    return false;
381
}
382
 
383
/**
384
 * {@link Auth_OpenID_getMathLib} checks for the presence of long
385
 * number extension modules and returns an instance of
386
 * {@link Auth_OpenID_MathWrapper} which exposes the module's
387
 * functionality.
388
 *
389
 * Checks for the existence of an extension module described by the
390
 * local {@link Auth_OpenID_math_extensions} array and returns an
391
 * instance of a wrapper for that extension module.  If no extension
392
 * module is found, an instance of {@link Auth_OpenID_MathWrapper} is
393
 * returned, which wraps the native PHP integer implementation.  The
394
 * proper calling convention for this method is $lib =&
395
 * Auth_OpenID_getMathLib().
396
 *
397
 * This function checks for the existence of specific long number
398
 * implementations in the following order: GMP followed by BCmath.
399
 *
400
 * @return Auth_OpenID_MathWrapper $instance An instance of
401
 * {@link Auth_OpenID_MathWrapper} or one of its subclasses
402
 *
403
 * @package OpenID
404
 */
405
function &Auth_OpenID_getMathLib()
406
{
407
    // The instance of Auth_OpenID_MathWrapper that we choose to
408
    // supply will be stored here, so that subseqent calls to this
409
    // method will return a reference to the same object.
410
    static $lib = null;
411
 
412
    if (isset($lib)) {
413
        return $lib;
414
    }
415
 
416
    if (defined('Auth_OpenID_NO_MATH_SUPPORT')) {
417
        $null = null;
418
        return $null;
419
    }
420
 
421
    // If this method has not been called before, look at
422
    // $Auth_OpenID_math_extensions and try to find an extension that
423
    // works.
424
    global $_Auth_OpenID_math_extensions;
425
    $ext = Auth_OpenID_detectMathLibrary($_Auth_OpenID_math_extensions);
426
    if ($ext === false) {
427
        $tried = array();
428
        foreach ($_Auth_OpenID_math_extensions as $extinfo) {
429
            $tried[] = $extinfo['extension'];
430
        }
431
        $triedstr = implode(", ", $tried);
432
 
433
        define('Auth_OpenID_NO_MATH_SUPPORT', true);
434
        return null;
435
    }
436
 
437
    // Instantiate a new wrapper
438
    $class = $ext['class'];
439
    $lib = new $class();
440
 
441
    return $lib;
442
}
443
 
444
?>