1<?php
2
3/**
4 * Base Class for all \phpseclib\Crypt\* cipher classes
5 *
6 * PHP version 5
7 *
8 * Internally for phpseclib developers:
9 *  If you plan to add a new cipher class, please note following rules:
10 *
11 *  - The new \phpseclib\Crypt\* cipher class should extend \phpseclib\Crypt\Base
12 *
13 *  - Following methods are then required to be overridden/overloaded:
14 *
15 *    - _encryptBlock()
16 *
17 *    - _decryptBlock()
18 *
19 *    - _setupKey()
20 *
21 *  - All other methods are optional to be overridden/overloaded
22 *
23 *  - Look at the source code of the current ciphers how they extend \phpseclib\Crypt\Base
24 *    and take one of them as a start up for the new cipher class.
25 *
26 *  - Please read all the other comments/notes/hints here also for each class var/method
27 *
28 * @category  Crypt
29 * @package   Base
30 * @author    Jim Wigginton <terrafrost@php.net>
31 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
32 * @copyright 2007 Jim Wigginton
33 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
34 * @link      http://phpseclib.sourceforge.net
35 */
36
37namespace phpseclib\Crypt;
38
39/**
40 * Base Class for all \phpseclib\Crypt\* cipher classes
41 *
42 * @package Base
43 * @author  Jim Wigginton <terrafrost@php.net>
44 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
45 */
46abstract class Base
47{
48    /**#@+
49     * @access public
50     * @see \phpseclib\Crypt\Base::encrypt()
51     * @see \phpseclib\Crypt\Base::decrypt()
52     */
53    /**
54     * Encrypt / decrypt using the Counter mode.
55     *
56     * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
57     *
58     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
59     */
60    const MODE_CTR = -1;
61    /**
62     * Encrypt / decrypt using the Electronic Code Book mode.
63     *
64     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
65     */
66    const MODE_ECB = 1;
67    /**
68     * Encrypt / decrypt using the Code Book Chaining mode.
69     *
70     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
71     */
72    const MODE_CBC = 2;
73    /**
74     * Encrypt / decrypt using the Cipher Feedback mode.
75     *
76     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
77     */
78    const MODE_CFB = 3;
79    /**
80     * Encrypt / decrypt using the Cipher Feedback mode (8bit)
81     */
82    const MODE_CFB8 = 38;
83    /**
84     * Encrypt / decrypt using the Output Feedback mode.
85     *
86     * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
87     */
88    const MODE_OFB = 4;
89    /**
90     * Encrypt / decrypt using streaming mode.
91     */
92    const MODE_STREAM = 5;
93    /**#@-*/
94
95    /**
96     * Whirlpool available flag
97     *
98     * @see \phpseclib\Crypt\Base::_hashInlineCryptFunction()
99     * @var bool
100     * @access private
101     */
102    static $WHIRLPOOL_AVAILABLE;
103
104    /**#@+
105     * @access private
106     * @see \phpseclib\Crypt\Base::__construct()
107     */
108    /**
109     * Base value for the internal implementation $engine switch
110     */
111    const ENGINE_INTERNAL = 1;
112    /**
113     * Base value for the mcrypt implementation $engine switch
114     */
115    const ENGINE_MCRYPT = 2;
116    /**
117     * Base value for the mcrypt implementation $engine switch
118     */
119    const ENGINE_OPENSSL = 3;
120    /**#@-*/
121
122    /**
123     * The Encryption Mode
124     *
125     * @see self::__construct()
126     * @var int
127     * @access private
128     */
129    var $mode;
130
131    /**
132     * The Block Length of the block cipher
133     *
134     * @var int
135     * @access private
136     */
137    var $block_size = 16;
138
139    /**
140     * The Key
141     *
142     * @see self::setKey()
143     * @var string
144     * @access private
145     */
146    var $key = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
147
148    /**
149     * The Initialization Vector
150     *
151     * @see self::setIV()
152     * @var string
153     * @access private
154     */
155    var $iv;
156
157    /**
158     * A "sliding" Initialization Vector
159     *
160     * @see self::enableContinuousBuffer()
161     * @see self::_clearBuffers()
162     * @var string
163     * @access private
164     */
165    var $encryptIV;
166
167    /**
168     * A "sliding" Initialization Vector
169     *
170     * @see self::enableContinuousBuffer()
171     * @see self::_clearBuffers()
172     * @var string
173     * @access private
174     */
175    var $decryptIV;
176
177    /**
178     * Continuous Buffer status
179     *
180     * @see self::enableContinuousBuffer()
181     * @var bool
182     * @access private
183     */
184    var $continuousBuffer = false;
185
186    /**
187     * Encryption buffer for CTR, OFB and CFB modes
188     *
189     * @see self::encrypt()
190     * @see self::_clearBuffers()
191     * @var array
192     * @access private
193     */
194    var $enbuffer;
195
196    /**
197     * Decryption buffer for CTR, OFB and CFB modes
198     *
199     * @see self::decrypt()
200     * @see self::_clearBuffers()
201     * @var array
202     * @access private
203     */
204    var $debuffer;
205
206    /**
207     * mcrypt resource for encryption
208     *
209     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
210     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
211     *
212     * @see self::encrypt()
213     * @var resource
214     * @access private
215     */
216    var $enmcrypt;
217
218    /**
219     * mcrypt resource for decryption
220     *
221     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
222     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
223     *
224     * @see self::decrypt()
225     * @var resource
226     * @access private
227     */
228    var $demcrypt;
229
230    /**
231     * Does the enmcrypt resource need to be (re)initialized?
232     *
233     * @see \phpseclib\Crypt\Twofish::setKey()
234     * @see \phpseclib\Crypt\Twofish::setIV()
235     * @var bool
236     * @access private
237     */
238    var $enchanged = true;
239
240    /**
241     * Does the demcrypt resource need to be (re)initialized?
242     *
243     * @see \phpseclib\Crypt\Twofish::setKey()
244     * @see \phpseclib\Crypt\Twofish::setIV()
245     * @var bool
246     * @access private
247     */
248    var $dechanged = true;
249
250    /**
251     * mcrypt resource for CFB mode
252     *
253     * mcrypt's CFB mode, in (and only in) buffered context,
254     * is broken, so phpseclib implements the CFB mode by it self,
255     * even when the mcrypt php extension is available.
256     *
257     * In order to do the CFB-mode work (fast) phpseclib
258     * use a separate ECB-mode mcrypt resource.
259     *
260     * @link http://phpseclib.sourceforge.net/cfb-demo.phps
261     * @see self::encrypt()
262     * @see self::decrypt()
263     * @see self::_setupMcrypt()
264     * @var resource
265     * @access private
266     */
267    var $ecb;
268
269    /**
270     * Optimizing value while CFB-encrypting
271     *
272     * Only relevant if $continuousBuffer enabled
273     * and $engine == self::ENGINE_MCRYPT
274     *
275     * It's faster to re-init $enmcrypt if
276     * $buffer bytes > $cfb_init_len than
277     * using the $ecb resource furthermore.
278     *
279     * This value depends of the chosen cipher
280     * and the time it would be needed for it's
281     * initialization [by mcrypt_generic_init()]
282     * which, typically, depends on the complexity
283     * on its internaly Key-expanding algorithm.
284     *
285     * @see self::encrypt()
286     * @var int
287     * @access private
288     */
289    var $cfb_init_len = 600;
290
291    /**
292     * Does internal cipher state need to be (re)initialized?
293     *
294     * @see self::setKey()
295     * @see self::setIV()
296     * @see self::disableContinuousBuffer()
297     * @var bool
298     * @access private
299     */
300    var $changed = true;
301
302    /**
303     * Padding status
304     *
305     * @see self::enablePadding()
306     * @var bool
307     * @access private
308     */
309    var $padding = true;
310
311    /**
312     * Is the mode one that is paddable?
313     *
314     * @see self::__construct()
315     * @var bool
316     * @access private
317     */
318    var $paddable = false;
319
320    /**
321     * Holds which crypt engine internaly should be use,
322     * which will be determined automatically on __construct()
323     *
324     * Currently available $engines are:
325     * - self::ENGINE_OPENSSL  (very fast, php-extension: openssl, extension_loaded('openssl') required)
326     * - self::ENGINE_MCRYPT   (fast, php-extension: mcrypt, extension_loaded('mcrypt') required)
327     * - self::ENGINE_INTERNAL (slower, pure php-engine, no php-extension required)
328     *
329     * @see self::_setEngine()
330     * @see self::encrypt()
331     * @see self::decrypt()
332     * @var int
333     * @access private
334     */
335    var $engine;
336
337    /**
338     * Holds the preferred crypt engine
339     *
340     * @see self::_setEngine()
341     * @see self::setPreferredEngine()
342     * @var int
343     * @access private
344     */
345    var $preferredEngine;
346
347    /**
348     * The mcrypt specific name of the cipher
349     *
350     * Only used if $engine == self::ENGINE_MCRYPT
351     *
352     * @link http://www.php.net/mcrypt_module_open
353     * @link http://www.php.net/mcrypt_list_algorithms
354     * @see self::_setupMcrypt()
355     * @var string
356     * @access private
357     */
358    var $cipher_name_mcrypt;
359
360    /**
361     * The openssl specific name of the cipher
362     *
363     * Only used if $engine == self::ENGINE_OPENSSL
364     *
365     * @link http://www.php.net/openssl-get-cipher-methods
366     * @var string
367     * @access private
368     */
369    var $cipher_name_openssl;
370
371    /**
372     * The openssl specific name of the cipher in ECB mode
373     *
374     * If OpenSSL does not support the mode we're trying to use (CTR)
375     * it can still be emulated with ECB mode.
376     *
377     * @link http://www.php.net/openssl-get-cipher-methods
378     * @var string
379     * @access private
380     */
381    var $cipher_name_openssl_ecb;
382
383    /**
384     * The default salt used by setPassword()
385     *
386     * @see self::setPassword()
387     * @var string
388     * @access private
389     */
390    var $password_default_salt = 'phpseclib/salt';
391
392    /**
393     * The name of the performance-optimized callback function
394     *
395     * Used by encrypt() / decrypt()
396     * only if $engine == self::ENGINE_INTERNAL
397     *
398     * @see self::encrypt()
399     * @see self::decrypt()
400     * @see self::_setupInlineCrypt()
401     * @see self::$use_inline_crypt
402     * @var Callback
403     * @access private
404     */
405    var $inline_crypt;
406
407    /**
408     * Holds whether performance-optimized $inline_crypt() can/should be used.
409     *
410     * @see self::encrypt()
411     * @see self::decrypt()
412     * @see self::inline_crypt
413     * @var mixed
414     * @access private
415     */
416    var $use_inline_crypt = true;
417
418    /**
419     * If OpenSSL can be used in ECB but not in CTR we can emulate CTR
420     *
421     * @see self::_openssl_ctr_process()
422     * @var bool
423     * @access private
424     */
425    var $openssl_emulate_ctr = false;
426
427    /**
428     * Determines what options are passed to openssl_encrypt/decrypt
429     *
430     * @see self::isValidEngine()
431     * @var mixed
432     * @access private
433     */
434    var $openssl_options;
435
436    /**
437     * Has the key length explicitly been set or should it be derived from the key, itself?
438     *
439     * @see self::setKeyLength()
440     * @var bool
441     * @access private
442     */
443    var $explicit_key_length = false;
444
445    /**
446     * Don't truncate / null pad key
447     *
448     * @see self::_clearBuffers()
449     * @var bool
450     * @access private
451     */
452    var $skip_key_adjustment = false;
453
454    /**
455     * Default Constructor.
456     *
457     * Determines whether or not the mcrypt extension should be used.
458     *
459     * $mode could be:
460     *
461     * - self::MODE_ECB
462     *
463     * - self::MODE_CBC
464     *
465     * - self::MODE_CTR
466     *
467     * - self::MODE_CFB
468     *
469     * - self::MODE_OFB
470     *
471     * If not explicitly set, self::MODE_CBC will be used.
472     *
473     * @param int $mode
474     * @access public
475     */
476    function __construct($mode = self::MODE_CBC)
477    {
478        // $mode dependent settings
479        switch ($mode) {
480            case self::MODE_ECB:
481                $this->paddable = true;
482                $this->mode = self::MODE_ECB;
483                break;
484            case self::MODE_CTR:
485            case self::MODE_CFB:
486            case self::MODE_CFB8:
487            case self::MODE_OFB:
488            case self::MODE_STREAM:
489                $this->mode = $mode;
490                break;
491            case self::MODE_CBC:
492            default:
493                $this->paddable = true;
494                $this->mode = self::MODE_CBC;
495        }
496
497        $this->_setEngine();
498    }
499
500    /**
501     * Sets the initialization vector. (optional)
502     *
503     * SetIV is not required when self::MODE_ECB (or ie for AES: \phpseclib\Crypt\AES::MODE_ECB) is being used.  If not explicitly set, it'll be assumed
504     * to be all zero's.
505     *
506     * @access public
507     * @param string $iv
508     * @internal Can be overwritten by a sub class, but does not have to be
509     */
510    function setIV($iv)
511    {
512        if ($this->mode == self::MODE_ECB) {
513            return;
514        }
515
516        $this->iv = $iv;
517        $this->changed = true;
518    }
519
520    /**
521     * Sets the key length.
522     *
523     * Keys with explicitly set lengths need to be treated accordingly
524     *
525     * @access public
526     * @param int $length
527     */
528    function setKeyLength($length)
529    {
530        $this->explicit_key_length = true;
531        $this->changed = true;
532        $this->_setEngine();
533    }
534
535    /**
536     * Returns the current key length in bits
537     *
538     * @access public
539     * @return int
540     */
541    function getKeyLength()
542    {
543        return $this->key_length << 3;
544    }
545
546    /**
547     * Returns the current block length in bits
548     *
549     * @access public
550     * @return int
551     */
552    function getBlockLength()
553    {
554        return $this->block_size << 3;
555    }
556
557    /**
558     * Sets the key.
559     *
560     * The min/max length(s) of the key depends on the cipher which is used.
561     * If the key not fits the length(s) of the cipher it will paded with null bytes
562     * up to the closest valid key length.  If the key is more than max length,
563     * we trim the excess bits.
564     *
565     * If the key is not explicitly set, it'll be assumed to be all null bytes.
566     *
567     * @access public
568     * @param string $key
569     * @internal Could, but not must, extend by the child Crypt_* class
570     */
571    function setKey($key)
572    {
573        if (!$this->explicit_key_length) {
574            $this->setKeyLength(strlen($key) << 3);
575            $this->explicit_key_length = false;
576        }
577
578        $this->key = $key;
579        $this->changed = true;
580        $this->_setEngine();
581    }
582
583    /**
584     * Sets the password.
585     *
586     * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
587     *     {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2} or pbkdf1:
588     *         $hash, $salt, $count, $dkLen
589     *
590     *         Where $hash (default = sha1) currently supports the following hashes: see: Crypt/Hash.php
591     *
592     * @see Crypt/Hash.php
593     * @param string $password
594     * @param string $method
595     * @return bool
596     * @access public
597     * @internal Could, but not must, extend by the child Crypt_* class
598     */
599    function setPassword($password, $method = 'pbkdf2')
600    {
601        $key = '';
602
603        switch ($method) {
604            default: // 'pbkdf2' or 'pbkdf1'
605                $func_args = func_get_args();
606
607                // Hash function
608                $hash = isset($func_args[2]) ? $func_args[2] : 'sha1';
609
610                // WPA and WPA2 use the SSID as the salt
611                $salt = isset($func_args[3]) ? $func_args[3] : $this->password_default_salt;
612
613                // RFC2898#section-4.2 uses 1,000 iterations by default
614                // WPA and WPA2 use 4,096.
615                $count = isset($func_args[4]) ? $func_args[4] : 1000;
616
617                // Keylength
618                if (isset($func_args[5])) {
619                    $dkLen = $func_args[5];
620                } else {
621                    $dkLen = $method == 'pbkdf1' ? 2 * $this->key_length : $this->key_length;
622                }
623
624                switch (true) {
625                    case $method == 'pbkdf1':
626                        $hashObj = new Hash();
627                        $hashObj->setHash($hash);
628                        if ($dkLen > $hashObj->getLength()) {
629                            user_error('Derived key too long');
630                            return false;
631                        }
632                        $t = $password . $salt;
633                        for ($i = 0; $i < $count; ++$i) {
634                            $t = $hashObj->hash($t);
635                        }
636                        $key = substr($t, 0, $dkLen);
637
638                        $this->setKey(substr($key, 0, $dkLen >> 1));
639                        $this->setIV(substr($key, $dkLen >> 1));
640
641                        return true;
642                    // Determining if php[>=5.5.0]'s hash_pbkdf2() function avail- and useable
643                    case !function_exists('hash_pbkdf2'):
644                    case !function_exists('hash_algos'):
645                    case !in_array($hash, hash_algos()):
646                        $i = 1;
647                        $hmac = new Hash();
648                        $hmac->setHash($hash);
649                        $hmac->setKey($password);
650                        while (strlen($key) < $dkLen) {
651                            $f = $u = $hmac->hash($salt . pack('N', $i++));
652                            for ($j = 2; $j <= $count; ++$j) {
653                                $u = $hmac->hash($u);
654                                $f^= $u;
655                            }
656                            $key.= $f;
657                        }
658                        $key = substr($key, 0, $dkLen);
659                        break;
660                    default:
661                        $key = hash_pbkdf2($hash, $password, $salt, $count, $dkLen, true);
662                }
663        }
664
665        $this->setKey($key);
666
667        return true;
668    }
669
670    /**
671     * Encrypts a message.
672     *
673     * $plaintext will be padded with additional bytes such that it's length is a multiple of the block size. Other cipher
674     * implementations may or may not pad in the same manner.  Other common approaches to padding and the reasons why it's
675     * necessary are discussed in the following
676     * URL:
677     *
678     * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
679     *
680     * An alternative to padding is to, separately, send the length of the file.  This is what SSH, in fact, does.
681     * strlen($plaintext) will still need to be a multiple of the block size, however, arbitrary values can be added to make it that
682     * length.
683     *
684     * @see self::decrypt()
685     * @access public
686     * @param string $plaintext
687     * @return string $ciphertext
688     * @internal Could, but not must, extend by the child Crypt_* class
689     */
690    function encrypt($plaintext)
691    {
692        if ($this->paddable) {
693            $plaintext = $this->_pad($plaintext);
694        }
695
696        if ($this->engine === self::ENGINE_OPENSSL) {
697            if ($this->changed) {
698                $this->_clearBuffers();
699                $this->changed = false;
700            }
701            switch ($this->mode) {
702                case self::MODE_STREAM:
703                    return openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
704                case self::MODE_ECB:
705                    $result = @openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
706                    return !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
707                case self::MODE_CBC:
708                    $result = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
709                    if (!defined('OPENSSL_RAW_DATA')) {
710                        $result = substr($result, 0, -$this->block_size);
711                    }
712                    if ($this->continuousBuffer) {
713                        $this->encryptIV = substr($result, -$this->block_size);
714                    }
715                    return $result;
716                case self::MODE_CTR:
717                    return $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer);
718                case self::MODE_CFB:
719                    // cfb loosely routines inspired by openssl's:
720                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
721                    $ciphertext = '';
722                    if ($this->continuousBuffer) {
723                        $iv = &$this->encryptIV;
724                        $pos = &$this->enbuffer['pos'];
725                    } else {
726                        $iv = $this->encryptIV;
727                        $pos = 0;
728                    }
729                    $len = strlen($plaintext);
730                    $i = 0;
731                    if ($pos) {
732                        $orig_pos = $pos;
733                        $max = $this->block_size - $pos;
734                        if ($len >= $max) {
735                            $i = $max;
736                            $len-= $max;
737                            $pos = 0;
738                        } else {
739                            $i = $len;
740                            $pos+= $len;
741                            $len = 0;
742                        }
743                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
744                        $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
745                        $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
746                        $plaintext = substr($plaintext, $i);
747                    }
748
749                    $overflow = $len % $this->block_size;
750
751                    if ($overflow) {
752                        $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
753                        $iv = $this->_string_pop($ciphertext, $this->block_size);
754
755                        $size = $len - $overflow;
756                        $block = $iv ^ substr($plaintext, -$overflow);
757                        $iv = substr_replace($iv, $block, 0, $overflow);
758                        $ciphertext.= $block;
759                        $pos = $overflow;
760                    } elseif ($len) {
761                        $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
762                        $iv = substr($ciphertext, -$this->block_size);
763                    }
764
765                    return $ciphertext;
766                case self::MODE_CFB8:
767                    $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->encryptIV);
768                    if ($this->continuousBuffer) {
769                        if (($len = strlen($ciphertext)) >= $this->block_size) {
770                            $this->encryptIV = substr($ciphertext, -$this->block_size);
771                        } else {
772                            $this->encryptIV = substr($this->encryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
773                        }
774                    }
775                    return $ciphertext;
776                case self::MODE_OFB:
777                    return $this->_openssl_ofb_process($plaintext, $this->encryptIV, $this->enbuffer);
778            }
779        }
780
781        if ($this->engine === self::ENGINE_MCRYPT) {
782            set_error_handler(array($this, 'do_nothing'));
783
784            if ($this->changed) {
785                $this->_setupMcrypt();
786                $this->changed = false;
787            }
788            if ($this->enchanged) {
789                mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
790                $this->enchanged = false;
791            }
792
793            // re: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
794            // using mcrypt's default handing of CFB the above would output two different things.  using phpseclib's
795            // rewritten CFB implementation the above outputs the same thing twice.
796            if ($this->mode == self::MODE_CFB && $this->continuousBuffer) {
797                $block_size = $this->block_size;
798                $iv = &$this->encryptIV;
799                $pos = &$this->enbuffer['pos'];
800                $len = strlen($plaintext);
801                $ciphertext = '';
802                $i = 0;
803                if ($pos) {
804                    $orig_pos = $pos;
805                    $max = $block_size - $pos;
806                    if ($len >= $max) {
807                        $i = $max;
808                        $len-= $max;
809                        $pos = 0;
810                    } else {
811                        $i = $len;
812                        $pos+= $len;
813                        $len = 0;
814                    }
815                    $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
816                    $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
817                    $this->enbuffer['enmcrypt_init'] = true;
818                }
819                if ($len >= $block_size) {
820                    if ($this->enbuffer['enmcrypt_init'] === false || $len > $this->cfb_init_len) {
821                        if ($this->enbuffer['enmcrypt_init'] === true) {
822                            mcrypt_generic_init($this->enmcrypt, $this->key, $iv);
823                            $this->enbuffer['enmcrypt_init'] = false;
824                        }
825                        $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % $block_size));
826                        $iv = substr($ciphertext, -$block_size);
827                        $len%= $block_size;
828                    } else {
829                        while ($len >= $block_size) {
830                            $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, $block_size);
831                            $ciphertext.= $iv;
832                            $len-= $block_size;
833                            $i+= $block_size;
834                        }
835                    }
836                }
837
838                if ($len) {
839                    $iv = mcrypt_generic($this->ecb, $iv);
840                    $block = $iv ^ substr($plaintext, -$len);
841                    $iv = substr_replace($iv, $block, 0, $len);
842                    $ciphertext.= $block;
843                    $pos = $len;
844                }
845
846                restore_error_handler();
847
848                return $ciphertext;
849            }
850
851            $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
852
853            if (!$this->continuousBuffer) {
854                mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
855            }
856
857            restore_error_handler();
858
859            return $ciphertext;
860        }
861
862        if ($this->changed) {
863            $this->_setup();
864            $this->changed = false;
865        }
866        if ($this->use_inline_crypt) {
867            $inline = $this->inline_crypt;
868            return $inline('encrypt', $this, $plaintext);
869        }
870
871        $buffer = &$this->enbuffer;
872        $block_size = $this->block_size;
873        $ciphertext = '';
874        switch ($this->mode) {
875            case self::MODE_ECB:
876                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
877                    $ciphertext.= $this->_encryptBlock(substr($plaintext, $i, $block_size));
878                }
879                break;
880            case self::MODE_CBC:
881                $xor = $this->encryptIV;
882                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
883                    $block = substr($plaintext, $i, $block_size);
884                    $block = $this->_encryptBlock($block ^ $xor);
885                    $xor = $block;
886                    $ciphertext.= $block;
887                }
888                if ($this->continuousBuffer) {
889                    $this->encryptIV = $xor;
890                }
891                break;
892            case self::MODE_CTR:
893                $xor = $this->encryptIV;
894                if (strlen($buffer['ciphertext'])) {
895                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
896                        $block = substr($plaintext, $i, $block_size);
897                        if (strlen($block) > strlen($buffer['ciphertext'])) {
898                            $buffer['ciphertext'].= $this->_encryptBlock($xor);
899                        }
900                        $this->_increment_str($xor);
901                        $key = $this->_string_shift($buffer['ciphertext'], $block_size);
902                        $ciphertext.= $block ^ $key;
903                    }
904                } else {
905                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
906                        $block = substr($plaintext, $i, $block_size);
907                        $key = $this->_encryptBlock($xor);
908                        $this->_increment_str($xor);
909                        $ciphertext.= $block ^ $key;
910                    }
911                }
912                if ($this->continuousBuffer) {
913                    $this->encryptIV = $xor;
914                    if ($start = strlen($plaintext) % $block_size) {
915                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
916                    }
917                }
918                break;
919            case self::MODE_CFB:
920                // cfb loosely routines inspired by openssl's:
921                // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
922                if ($this->continuousBuffer) {
923                    $iv = &$this->encryptIV;
924                    $pos = &$buffer['pos'];
925                } else {
926                    $iv = $this->encryptIV;
927                    $pos = 0;
928                }
929                $len = strlen($plaintext);
930                $i = 0;
931                if ($pos) {
932                    $orig_pos = $pos;
933                    $max = $block_size - $pos;
934                    if ($len >= $max) {
935                        $i = $max;
936                        $len-= $max;
937                        $pos = 0;
938                    } else {
939                        $i = $len;
940                        $pos+= $len;
941                        $len = 0;
942                    }
943                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
944                    $ciphertext = substr($iv, $orig_pos) ^ $plaintext;
945                    $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);
946                }
947                while ($len >= $block_size) {
948                    $iv = $this->_encryptBlock($iv) ^ substr($plaintext, $i, $block_size);
949                    $ciphertext.= $iv;
950                    $len-= $block_size;
951                    $i+= $block_size;
952                }
953                if ($len) {
954                    $iv = $this->_encryptBlock($iv);
955                    $block = $iv ^ substr($plaintext, $i);
956                    $iv = substr_replace($iv, $block, 0, $len);
957                    $ciphertext.= $block;
958                    $pos = $len;
959                }
960                break;
961            case self::MODE_CFB8:
962                $ciphertext = '';
963                $len = strlen($plaintext);
964                $iv = $this->encryptIV;
965
966                for ($i = 0; $i < $len; ++$i) {
967                    $ciphertext .= ($c = $plaintext[$i] ^ $this->_encryptBlock($iv));
968                    $iv = substr($iv, 1) . $c;
969                }
970
971                if ($this->continuousBuffer) {
972                    if ($len >= $block_size) {
973                        $this->encryptIV = substr($ciphertext, -$block_size);
974                    } else {
975                        $this->encryptIV = substr($this->encryptIV, $len - $block_size) . substr($ciphertext, -$len);
976                    }
977                }
978                break;
979            case self::MODE_OFB:
980                $xor = $this->encryptIV;
981                if (strlen($buffer['xor'])) {
982                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
983                        $block = substr($plaintext, $i, $block_size);
984                        if (strlen($block) > strlen($buffer['xor'])) {
985                            $xor = $this->_encryptBlock($xor);
986                            $buffer['xor'].= $xor;
987                        }
988                        $key = $this->_string_shift($buffer['xor'], $block_size);
989                        $ciphertext.= $block ^ $key;
990                    }
991                } else {
992                    for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
993                        $xor = $this->_encryptBlock($xor);
994                        $ciphertext.= substr($plaintext, $i, $block_size) ^ $xor;
995                    }
996                    $key = $xor;
997                }
998                if ($this->continuousBuffer) {
999                    $this->encryptIV = $xor;
1000                    if ($start = strlen($plaintext) % $block_size) {
1001                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
1002                    }
1003                }
1004                break;
1005            case self::MODE_STREAM:
1006                $ciphertext = $this->_encryptBlock($plaintext);
1007                break;
1008        }
1009
1010        return $ciphertext;
1011    }
1012
1013    /**
1014     * Decrypts a message.
1015     *
1016     * If strlen($ciphertext) is not a multiple of the block size, null bytes will be added to the end of the string until
1017     * it is.
1018     *
1019     * @see self::encrypt()
1020     * @access public
1021     * @param string $ciphertext
1022     * @return string $plaintext
1023     * @internal Could, but not must, extend by the child Crypt_* class
1024     */
1025    function decrypt($ciphertext)
1026    {
1027        if ($this->paddable) {
1028            // we pad with chr(0) since that's what mcrypt_generic does.  to quote from {@link http://www.php.net/function.mcrypt-generic}:
1029            // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
1030            $ciphertext = str_pad($ciphertext, strlen($ciphertext) + ($this->block_size - strlen($ciphertext) % $this->block_size) % $this->block_size, chr(0));
1031        }
1032
1033        if ($this->engine === self::ENGINE_OPENSSL) {
1034            if ($this->changed) {
1035                $this->_clearBuffers();
1036                $this->changed = false;
1037            }
1038            switch ($this->mode) {
1039                case self::MODE_STREAM:
1040                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
1041                    break;
1042                case self::MODE_ECB:
1043                    if (!defined('OPENSSL_RAW_DATA')) {
1044                        $ciphertext.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $this->key, true);
1045                    }
1046                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options);
1047                    break;
1048                case self::MODE_CBC:
1049                    if (!defined('OPENSSL_RAW_DATA')) {
1050                        $padding = str_repeat(chr($this->block_size), $this->block_size) ^ substr($ciphertext, -$this->block_size);
1051                        $ciphertext.= substr(@openssl_encrypt($padding, $this->cipher_name_openssl_ecb, $this->key, true), 0, $this->block_size);
1052                        $offset = 2 * $this->block_size;
1053                    } else {
1054                        $offset = $this->block_size;
1055                    }
1056                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
1057                    if ($this->continuousBuffer) {
1058                        $this->decryptIV = substr($ciphertext, -$offset, $this->block_size);
1059                    }
1060                    break;
1061                case self::MODE_CTR:
1062                    $plaintext = $this->_openssl_ctr_process($ciphertext, $this->decryptIV, $this->debuffer);
1063                    break;
1064                case self::MODE_CFB:
1065                    // cfb loosely routines inspired by openssl's:
1066                    // {@link http://cvs.openssl.org/fileview?f=openssl/crypto/modes/cfb128.c&v=1.3.2.2.2.1}
1067                    $plaintext = '';
1068                    if ($this->continuousBuffer) {
1069                        $iv = &$this->decryptIV;
1070                        $pos = &$this->buffer['pos'];
1071                    } else {
1072                        $iv = $this->decryptIV;
1073                        $pos = 0;
1074                    }
1075                    $len = strlen($ciphertext);
1076                    $i = 0;
1077                    if ($pos) {
1078                        $orig_pos = $pos;
1079                        $max = $this->block_size - $pos;
1080                        if ($len >= $max) {
1081                            $i = $max;
1082                            $len-= $max;
1083                            $pos = 0;
1084                        } else {
1085                            $i = $len;
1086                            $pos+= $len;
1087                            $len = 0;
1088                        }
1089                        // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $this->blocksize
1090                        $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
1091                        $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
1092                        $ciphertext = substr($ciphertext, $i);
1093                    }
1094                    $overflow = $len % $this->block_size;
1095                    if ($overflow) {
1096                        $plaintext.= openssl_decrypt(substr($ciphertext, 0, -$overflow), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
1097                        if ($len - $overflow) {
1098                            $iv = substr($ciphertext, -$overflow - $this->block_size, -$overflow);
1099                        }
1100                        $iv = openssl_encrypt(str_repeat("\0", $this->block_size), $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
1101                        $plaintext.= $iv ^ substr($ciphertext, -$overflow);
1102                        $iv = substr_replace($iv, substr($ciphertext, -$overflow), 0, $overflow);
1103                        $pos = $overflow;
1104                    } elseif ($len) {
1105                        $plaintext.= openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $iv);
1106                        $iv = substr($ciphertext, -$this->block_size);
1107                    }
1108                    break;
1109                case self::MODE_CFB8:
1110                    $plaintext = openssl_decrypt($ciphertext, $this->cipher_name_openssl, $this->key, $this->openssl_options, $this->decryptIV);
1111                    if ($this->continuousBuffer) {
1112                        if (($len = strlen($ciphertext)) >= $this->block_size) {
1113                            $this->decryptIV = substr($ciphertext, -$this->block_size);
1114                        } else {
1115                            $this->decryptIV = substr($this->decryptIV, $len - $this->block_size) . substr($ciphertext, -$len);
1116                        }
1117                    }
1118                    break;
1119                case self::MODE_OFB:
1120                    $plaintext = $this->_openssl_ofb_process($ciphertext, $this->decryptIV, $this->debuffer);
1121            }
1122
1123            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
1124        }
1125
1126        if ($this->engine === self::ENGINE_MCRYPT) {
1127            set_error_handler(array($this, 'do_nothing'));
1128            $block_size = $this->block_size;
1129            if ($this->changed) {
1130                $this->_setupMcrypt();
1131                $this->changed = false;
1132            }
1133            if ($this->dechanged) {
1134                mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1135                $this->dechanged = false;
1136            }
1137
1138            if ($this->mode == self::MODE_CFB && $this->continuousBuffer) {
1139                $iv = &$this->decryptIV;
1140                $pos = &$this->debuffer['pos'];
1141                $len = strlen($ciphertext);
1142                $plaintext = '';
1143                $i = 0;
1144                if ($pos) {
1145                    $orig_pos = $pos;
1146                    $max = $block_size - $pos;
1147                    if ($len >= $max) {
1148                        $i = $max;
1149                        $len-= $max;
1150                        $pos = 0;
1151                    } else {
1152                        $i = $len;
1153                        $pos+= $len;
1154                        $len = 0;
1155                    }
1156                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
1157                    $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
1158                    $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
1159                }
1160                if ($len >= $block_size) {
1161                    $cb = substr($ciphertext, $i, $len - $len % $block_size);
1162                    $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;
1163                    $iv = substr($cb, -$block_size);
1164                    $len%= $block_size;
1165                }
1166                if ($len) {
1167                    $iv = mcrypt_generic($this->ecb, $iv);
1168                    $plaintext.= $iv ^ substr($ciphertext, -$len);
1169                    $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);
1170                    $pos = $len;
1171                }
1172
1173                restore_error_handler();
1174
1175                return $plaintext;
1176            }
1177
1178            $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
1179
1180            if (!$this->continuousBuffer) {
1181                mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
1182            }
1183
1184            restore_error_handler();
1185
1186            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
1187        }
1188
1189        if ($this->changed) {
1190            $this->_setup();
1191            $this->changed = false;
1192        }
1193        if ($this->use_inline_crypt) {
1194            $inline = $this->inline_crypt;
1195            return $inline('decrypt', $this, $ciphertext);
1196        }
1197
1198        $block_size = $this->block_size;
1199
1200        $buffer = &$this->debuffer;
1201        $plaintext = '';
1202        switch ($this->mode) {
1203            case self::MODE_ECB:
1204                for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1205                    $plaintext.= $this->_decryptBlock(substr($ciphertext, $i, $block_size));
1206                }
1207                break;
1208            case self::MODE_CBC:
1209                $xor = $this->decryptIV;
1210                for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1211                    $block = substr($ciphertext, $i, $block_size);
1212                    $plaintext.= $this->_decryptBlock($block) ^ $xor;
1213                    $xor = $block;
1214                }
1215                if ($this->continuousBuffer) {
1216                    $this->decryptIV = $xor;
1217                }
1218                break;
1219            case self::MODE_CTR:
1220                $xor = $this->decryptIV;
1221                if (strlen($buffer['ciphertext'])) {
1222                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1223                        $block = substr($ciphertext, $i, $block_size);
1224                        if (strlen($block) > strlen($buffer['ciphertext'])) {
1225                            $buffer['ciphertext'].= $this->_encryptBlock($xor);
1226                            $this->_increment_str($xor);
1227                        }
1228                        $key = $this->_string_shift($buffer['ciphertext'], $block_size);
1229                        $plaintext.= $block ^ $key;
1230                    }
1231                } else {
1232                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1233                        $block = substr($ciphertext, $i, $block_size);
1234                        $key = $this->_encryptBlock($xor);
1235                        $this->_increment_str($xor);
1236                        $plaintext.= $block ^ $key;
1237                    }
1238                }
1239                if ($this->continuousBuffer) {
1240                    $this->decryptIV = $xor;
1241                    if ($start = strlen($ciphertext) % $block_size) {
1242                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
1243                    }
1244                }
1245                break;
1246            case self::MODE_CFB:
1247                if ($this->continuousBuffer) {
1248                    $iv = &$this->decryptIV;
1249                    $pos = &$buffer['pos'];
1250                } else {
1251                    $iv = $this->decryptIV;
1252                    $pos = 0;
1253                }
1254                $len = strlen($ciphertext);
1255                $i = 0;
1256                if ($pos) {
1257                    $orig_pos = $pos;
1258                    $max = $block_size - $pos;
1259                    if ($len >= $max) {
1260                        $i = $max;
1261                        $len-= $max;
1262                        $pos = 0;
1263                    } else {
1264                        $i = $len;
1265                        $pos+= $len;
1266                        $len = 0;
1267                    }
1268                    // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize
1269                    $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
1270                    $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
1271                }
1272                while ($len >= $block_size) {
1273                    $iv = $this->_encryptBlock($iv);
1274                    $cb = substr($ciphertext, $i, $block_size);
1275                    $plaintext.= $iv ^ $cb;
1276                    $iv = $cb;
1277                    $len-= $block_size;
1278                    $i+= $block_size;
1279                }
1280                if ($len) {
1281                    $iv = $this->_encryptBlock($iv);
1282                    $plaintext.= $iv ^ substr($ciphertext, $i);
1283                    $iv = substr_replace($iv, substr($ciphertext, $i), 0, $len);
1284                    $pos = $len;
1285                }
1286                break;
1287            case self::MODE_CFB8:
1288                $plaintext = '';
1289                $len = strlen($ciphertext);
1290                $iv = $this->decryptIV;
1291
1292                for ($i = 0; $i < $len; ++$i) {
1293                    $plaintext .= $ciphertext[$i] ^ $this->_encryptBlock($iv);
1294                    $iv = substr($iv, 1) . $ciphertext[$i];
1295                }
1296
1297                if ($this->continuousBuffer) {
1298                    if ($len >= $block_size) {
1299                        $this->decryptIV = substr($ciphertext, -$block_size);
1300                    } else {
1301                        $this->decryptIV = substr($this->decryptIV, $len - $block_size) . substr($ciphertext, -$len);
1302                    }
1303                }
1304                break;
1305            case self::MODE_OFB:
1306                $xor = $this->decryptIV;
1307                if (strlen($buffer['xor'])) {
1308                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1309                        $block = substr($ciphertext, $i, $block_size);
1310                        if (strlen($block) > strlen($buffer['xor'])) {
1311                            $xor = $this->_encryptBlock($xor);
1312                            $buffer['xor'].= $xor;
1313                        }
1314                        $key = $this->_string_shift($buffer['xor'], $block_size);
1315                        $plaintext.= $block ^ $key;
1316                    }
1317                } else {
1318                    for ($i = 0; $i < strlen($ciphertext); $i+=$block_size) {
1319                        $xor = $this->_encryptBlock($xor);
1320                        $plaintext.= substr($ciphertext, $i, $block_size) ^ $xor;
1321                    }
1322                    $key = $xor;
1323                }
1324                if ($this->continuousBuffer) {
1325                    $this->decryptIV = $xor;
1326                    if ($start = strlen($ciphertext) % $block_size) {
1327                        $buffer['xor'] = substr($key, $start) . $buffer['xor'];
1328                    }
1329                }
1330                break;
1331            case self::MODE_STREAM:
1332                $plaintext = $this->_decryptBlock($ciphertext);
1333                break;
1334        }
1335        return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
1336    }
1337
1338    /**
1339     * OpenSSL CTR Processor
1340     *
1341     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
1342     * for CTR is the same for both encrypting and decrypting this function is re-used by both Base::encrypt()
1343     * and Base::decrypt(). Also, OpenSSL doesn't implement CTR for all of it's symmetric ciphers so this
1344     * function will emulate CTR with ECB when necessary.
1345     *
1346     * @see self::encrypt()
1347     * @see self::decrypt()
1348     * @param string $plaintext
1349     * @param string $encryptIV
1350     * @param array $buffer
1351     * @return string
1352     * @access private
1353     */
1354    function _openssl_ctr_process($plaintext, &$encryptIV, &$buffer)
1355    {
1356        $ciphertext = '';
1357
1358        $block_size = $this->block_size;
1359        $key = $this->key;
1360
1361        if ($this->openssl_emulate_ctr) {
1362            $xor = $encryptIV;
1363            if (strlen($buffer['ciphertext'])) {
1364                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
1365                    $block = substr($plaintext, $i, $block_size);
1366                    if (strlen($block) > strlen($buffer['ciphertext'])) {
1367                        $result = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
1368                        $result = !defined('OPENSSL_RAW_DATA') ? substr($result, 0, -$this->block_size) : $result;
1369                        $buffer['ciphertext'].= $result;
1370                    }
1371                    $this->_increment_str($xor);
1372                    $otp = $this->_string_shift($buffer['ciphertext'], $block_size);
1373                    $ciphertext.= $block ^ $otp;
1374                }
1375            } else {
1376                for ($i = 0; $i < strlen($plaintext); $i+=$block_size) {
1377                    $block = substr($plaintext, $i, $block_size);
1378                    $otp = @openssl_encrypt($xor, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
1379                    $otp = !defined('OPENSSL_RAW_DATA') ? substr($otp, 0, -$this->block_size) : $otp;
1380                    $this->_increment_str($xor);
1381                    $ciphertext.= $block ^ $otp;
1382                }
1383            }
1384            if ($this->continuousBuffer) {
1385                $encryptIV = $xor;
1386                if ($start = strlen($plaintext) % $block_size) {
1387                    $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
1388                }
1389            }
1390
1391            return $ciphertext;
1392        }
1393
1394        if (strlen($buffer['ciphertext'])) {
1395            $ciphertext = $plaintext ^ $this->_string_shift($buffer['ciphertext'], strlen($plaintext));
1396            $plaintext = substr($plaintext, strlen($ciphertext));
1397
1398            if (!strlen($plaintext)) {
1399                return $ciphertext;
1400            }
1401        }
1402
1403        $overflow = strlen($plaintext) % $block_size;
1404        if ($overflow) {
1405            $plaintext2 = $this->_string_pop($plaintext, $overflow); // ie. trim $plaintext to a multiple of $block_size and put rest of $plaintext in $plaintext2
1406            $encrypted = openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
1407            $temp = $this->_string_pop($encrypted, $block_size);
1408            $ciphertext.= $encrypted . ($plaintext2 ^ $temp);
1409            if ($this->continuousBuffer) {
1410                $buffer['ciphertext'] = substr($temp, $overflow);
1411                $encryptIV = $temp;
1412            }
1413        } elseif (!strlen($buffer['ciphertext'])) {
1414            $ciphertext.= openssl_encrypt($plaintext . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
1415            $temp = $this->_string_pop($ciphertext, $block_size);
1416            if ($this->continuousBuffer) {
1417                $encryptIV = $temp;
1418            }
1419        }
1420        if ($this->continuousBuffer) {
1421            if (!defined('OPENSSL_RAW_DATA')) {
1422                $encryptIV.= @openssl_encrypt('', $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
1423            }
1424            $encryptIV = openssl_decrypt($encryptIV, $this->cipher_name_openssl_ecb, $key, $this->openssl_options);
1425            if ($overflow) {
1426                $this->_increment_str($encryptIV);
1427            }
1428        }
1429
1430        return $ciphertext;
1431    }
1432
1433    /**
1434     * OpenSSL OFB Processor
1435     *
1436     * PHP's OpenSSL bindings do not operate in continuous mode so we'll wrap around it. Since the keystream
1437     * for OFB is the same for both encrypting and decrypting this function is re-used by both Base::encrypt()
1438     * and Base::decrypt().
1439     *
1440     * @see self::encrypt()
1441     * @see self::decrypt()
1442     * @param string $plaintext
1443     * @param string $encryptIV
1444     * @param array $buffer
1445     * @return string
1446     * @access private
1447     */
1448    function _openssl_ofb_process($plaintext, &$encryptIV, &$buffer)
1449    {
1450        if (strlen($buffer['xor'])) {
1451            $ciphertext = $plaintext ^ $buffer['xor'];
1452            $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
1453            $plaintext = substr($plaintext, strlen($ciphertext));
1454        } else {
1455            $ciphertext = '';
1456        }
1457
1458        $block_size = $this->block_size;
1459
1460        $len = strlen($plaintext);
1461        $key = $this->key;
1462        $overflow = $len % $block_size;
1463
1464        if (strlen($plaintext)) {
1465            if ($overflow) {
1466                $ciphertext.= openssl_encrypt(substr($plaintext, 0, -$overflow) . str_repeat("\0", $block_size), $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
1467                $xor = $this->_string_pop($ciphertext, $block_size);
1468                if ($this->continuousBuffer) {
1469                    $encryptIV = $xor;
1470                }
1471                $ciphertext.= $this->_string_shift($xor, $overflow) ^ substr($plaintext, -$overflow);
1472                if ($this->continuousBuffer) {
1473                    $buffer['xor'] = $xor;
1474                }
1475            } else {
1476                $ciphertext = openssl_encrypt($plaintext, $this->cipher_name_openssl, $key, $this->openssl_options, $encryptIV);
1477                if ($this->continuousBuffer) {
1478                    $encryptIV = substr($ciphertext, -$block_size) ^ substr($plaintext, -$block_size);
1479                }
1480            }
1481        }
1482
1483        return $ciphertext;
1484    }
1485
1486    /**
1487     * phpseclib <-> OpenSSL Mode Mapper
1488     *
1489     * May need to be overwritten by classes extending this one in some cases
1490     *
1491     * @return int
1492     * @access private
1493     */
1494    function _openssl_translate_mode()
1495    {
1496        switch ($this->mode) {
1497            case self::MODE_ECB:
1498                return 'ecb';
1499            case self::MODE_CBC:
1500                return 'cbc';
1501            case self::MODE_CTR:
1502                return 'ctr';
1503            case self::MODE_CFB:
1504                return 'cfb';
1505            case self::MODE_CFB8:
1506                return 'cfb8';
1507            case self::MODE_OFB:
1508                return 'ofb';
1509        }
1510    }
1511
1512    /**
1513     * Pad "packets".
1514     *
1515     * Block ciphers working by encrypting between their specified [$this->]block_size at a time
1516     * If you ever need to encrypt or decrypt something that isn't of the proper length, it becomes necessary to
1517     * pad the input so that it is of the proper length.
1518     *
1519     * Padding is enabled by default.  Sometimes, however, it is undesirable to pad strings.  Such is the case in SSH,
1520     * where "packets" are padded with random bytes before being encrypted.  Unpad these packets and you risk stripping
1521     * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
1522     * transmitted separately)
1523     *
1524     * @see self::disablePadding()
1525     * @access public
1526     */
1527    function enablePadding()
1528    {
1529        $this->padding = true;
1530    }
1531
1532    /**
1533     * Do not pad packets.
1534     *
1535     * @see self::enablePadding()
1536     * @access public
1537     */
1538    function disablePadding()
1539    {
1540        $this->padding = false;
1541    }
1542
1543    /**
1544     * Treat consecutive "packets" as if they are a continuous buffer.
1545     *
1546     * Say you have a 32-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
1547     * will yield different outputs:
1548     *
1549     * <code>
1550     *    echo $rijndael->encrypt(substr($plaintext,  0, 16));
1551     *    echo $rijndael->encrypt(substr($plaintext, 16, 16));
1552     * </code>
1553     * <code>
1554     *    echo $rijndael->encrypt($plaintext);
1555     * </code>
1556     *
1557     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
1558     * another, as demonstrated with the following:
1559     *
1560     * <code>
1561     *    $rijndael->encrypt(substr($plaintext, 0, 16));
1562     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
1563     * </code>
1564     * <code>
1565     *    echo $rijndael->decrypt($rijndael->encrypt(substr($plaintext, 16, 16)));
1566     * </code>
1567     *
1568     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
1569     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
1570     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
1571     *
1572     * Put another way, when the continuous buffer is enabled, the state of the \phpseclib\Crypt\*() object changes after each
1573     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
1574     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
1575     * however, they are also less intuitive and more likely to cause you problems.
1576     *
1577     * @see self::disableContinuousBuffer()
1578     * @access public
1579     * @internal Could, but not must, extend by the child Crypt_* class
1580     */
1581    function enableContinuousBuffer()
1582    {
1583        if ($this->mode == self::MODE_ECB) {
1584            return;
1585        }
1586
1587        $this->continuousBuffer = true;
1588
1589        $this->_setEngine();
1590    }
1591
1592    /**
1593     * Treat consecutive packets as if they are a discontinuous buffer.
1594     *
1595     * The default behavior.
1596     *
1597     * @see self::enableContinuousBuffer()
1598     * @access public
1599     * @internal Could, but not must, extend by the child Crypt_* class
1600     */
1601    function disableContinuousBuffer()
1602    {
1603        if ($this->mode == self::MODE_ECB) {
1604            return;
1605        }
1606        if (!$this->continuousBuffer) {
1607            return;
1608        }
1609
1610        $this->continuousBuffer = false;
1611        $this->changed = true;
1612
1613        $this->_setEngine();
1614    }
1615
1616    /**
1617     * Test for engine validity
1618     *
1619     * @see self::__construct()
1620     * @param int $engine
1621     * @access public
1622     * @return bool
1623     */
1624    function isValidEngine($engine)
1625    {
1626        switch ($engine) {
1627            case self::ENGINE_OPENSSL:
1628                if ($this->mode == self::MODE_STREAM && $this->continuousBuffer) {
1629                    return false;
1630                }
1631                $this->openssl_emulate_ctr = false;
1632                $result = $this->cipher_name_openssl &&
1633                          extension_loaded('openssl') &&
1634                          // PHP 5.3.0 - 5.3.2 did not let you set IV's
1635                          version_compare(PHP_VERSION, '5.3.3', '>=');
1636                if (!$result) {
1637                    return false;
1638                }
1639
1640                // prior to PHP 5.4.0 OPENSSL_RAW_DATA and OPENSSL_ZERO_PADDING were not defined. instead of expecting an integer
1641                // $options openssl_encrypt expected a boolean $raw_data.
1642                if (!defined('OPENSSL_RAW_DATA')) {
1643                    $this->openssl_options = true;
1644                } else {
1645                    $this->openssl_options = OPENSSL_RAW_DATA | OPENSSL_ZERO_PADDING;
1646                }
1647
1648                $methods = openssl_get_cipher_methods();
1649                if (in_array($this->cipher_name_openssl, $methods)) {
1650                    return true;
1651                }
1652                // not all of openssl's symmetric cipher's support ctr. for those
1653                // that don't we'll emulate it
1654                switch ($this->mode) {
1655                    case self::MODE_CTR:
1656                        if (in_array($this->cipher_name_openssl_ecb, $methods)) {
1657                            $this->openssl_emulate_ctr = true;
1658                            return true;
1659                        }
1660                }
1661                return false;
1662            case self::ENGINE_MCRYPT:
1663                set_error_handler(array($this, 'do_nothing'));
1664                $result = $this->cipher_name_mcrypt &&
1665                       extension_loaded('mcrypt') &&
1666                       in_array($this->cipher_name_mcrypt, mcrypt_list_algorithms());
1667                restore_error_handler();
1668                return $result;
1669            case self::ENGINE_INTERNAL:
1670                return true;
1671        }
1672
1673        return false;
1674    }
1675
1676    /**
1677     * Sets the preferred crypt engine
1678     *
1679     * Currently, $engine could be:
1680     *
1681     * - \phpseclib\Crypt\Base::ENGINE_OPENSSL  [very fast]
1682     *
1683     * - \phpseclib\Crypt\Base::ENGINE_MCRYPT   [fast]
1684     *
1685     * - \phpseclib\Crypt\Base::ENGINE_INTERNAL [slow]
1686     *
1687     * If the preferred crypt engine is not available the fastest available one will be used
1688     *
1689     * @see self::__construct()
1690     * @param int $engine
1691     * @access public
1692     */
1693    function setPreferredEngine($engine)
1694    {
1695        switch ($engine) {
1696            //case self::ENGINE_OPENSSL;
1697            case self::ENGINE_MCRYPT:
1698            case self::ENGINE_INTERNAL:
1699                $this->preferredEngine = $engine;
1700                break;
1701            default:
1702                $this->preferredEngine = self::ENGINE_OPENSSL;
1703        }
1704
1705        $this->_setEngine();
1706    }
1707
1708    /**
1709     * Returns the engine currently being utilized
1710     *
1711     * @see self::_setEngine()
1712     * @access public
1713     */
1714    function getEngine()
1715    {
1716        return $this->engine;
1717    }
1718
1719    /**
1720     * Sets the engine as appropriate
1721     *
1722     * @see self::__construct()
1723     * @access private
1724     */
1725    function _setEngine()
1726    {
1727        $this->engine = null;
1728
1729        $candidateEngines = array(
1730            $this->preferredEngine,
1731            self::ENGINE_OPENSSL,
1732            self::ENGINE_MCRYPT
1733        );
1734        foreach ($candidateEngines as $engine) {
1735            if ($this->isValidEngine($engine)) {
1736                $this->engine = $engine;
1737                break;
1738            }
1739        }
1740        if (!$this->engine) {
1741            $this->engine = self::ENGINE_INTERNAL;
1742        }
1743
1744        if ($this->engine != self::ENGINE_MCRYPT && $this->enmcrypt) {
1745            set_error_handler(array($this, 'do_nothing'));
1746            // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
1747            // (re)open them with the module named in $this->cipher_name_mcrypt
1748            mcrypt_module_close($this->enmcrypt);
1749            mcrypt_module_close($this->demcrypt);
1750            $this->enmcrypt = null;
1751            $this->demcrypt = null;
1752
1753            if ($this->ecb) {
1754                mcrypt_module_close($this->ecb);
1755                $this->ecb = null;
1756            }
1757            restore_error_handler();
1758        }
1759
1760        $this->changed = true;
1761    }
1762
1763    /**
1764     * Encrypts a block
1765     *
1766     * Note: Must be extended by the child \phpseclib\Crypt\* class
1767     *
1768     * @access private
1769     * @param string $in
1770     * @return string
1771     */
1772    abstract function _encryptBlock($in);
1773
1774    /**
1775     * Decrypts a block
1776     *
1777     * Note: Must be extended by the child \phpseclib\Crypt\* class
1778     *
1779     * @access private
1780     * @param string $in
1781     * @return string
1782     */
1783    abstract function _decryptBlock($in);
1784
1785    /**
1786     * Setup the key (expansion)
1787     *
1788     * Only used if $engine == self::ENGINE_INTERNAL
1789     *
1790     * Note: Must extend by the child \phpseclib\Crypt\* class
1791     *
1792     * @see self::_setup()
1793     * @access private
1794     */
1795    abstract function _setupKey();
1796
1797    /**
1798     * Setup the self::ENGINE_INTERNAL $engine
1799     *
1800     * (re)init, if necessary, the internal cipher $engine and flush all $buffers
1801     * Used (only) if $engine == self::ENGINE_INTERNAL
1802     *
1803     * _setup() will be called each time if $changed === true
1804     * typically this happens when using one or more of following public methods:
1805     *
1806     * - setKey()
1807     *
1808     * - setIV()
1809     *
1810     * - disableContinuousBuffer()
1811     *
1812     * - First run of encrypt() / decrypt() with no init-settings
1813     *
1814     * @see self::setKey()
1815     * @see self::setIV()
1816     * @see self::disableContinuousBuffer()
1817     * @access private
1818     * @internal _setup() is always called before en/decryption.
1819     * @internal Could, but not must, extend by the child Crypt_* class
1820     */
1821    function _setup()
1822    {
1823        $this->_clearBuffers();
1824        $this->_setupKey();
1825
1826        if ($this->use_inline_crypt) {
1827            $this->_setupInlineCrypt();
1828        }
1829    }
1830
1831    /**
1832     * Setup the self::ENGINE_MCRYPT $engine
1833     *
1834     * (re)init, if necessary, the (ext)mcrypt resources and flush all $buffers
1835     * Used (only) if $engine = self::ENGINE_MCRYPT
1836     *
1837     * _setupMcrypt() will be called each time if $changed === true
1838     * typically this happens when using one or more of following public methods:
1839     *
1840     * - setKey()
1841     *
1842     * - setIV()
1843     *
1844     * - disableContinuousBuffer()
1845     *
1846     * - First run of encrypt() / decrypt()
1847     *
1848     * @see self::setKey()
1849     * @see self::setIV()
1850     * @see self::disableContinuousBuffer()
1851     * @access private
1852     * @internal Could, but not must, extend by the child Crypt_* class
1853     */
1854    function _setupMcrypt()
1855    {
1856        $this->_clearBuffers();
1857        $this->enchanged = $this->dechanged = true;
1858
1859        if (!isset($this->enmcrypt)) {
1860            static $mcrypt_modes = array(
1861                self::MODE_CTR    => 'ctr',
1862                self::MODE_ECB    => MCRYPT_MODE_ECB,
1863                self::MODE_CBC    => MCRYPT_MODE_CBC,
1864                self::MODE_CFB    => 'ncfb',
1865                self::MODE_CFB8   => MCRYPT_MODE_CFB,
1866                self::MODE_OFB    => MCRYPT_MODE_NOFB,
1867                self::MODE_STREAM => MCRYPT_MODE_STREAM,
1868            );
1869
1870            $this->demcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1871            $this->enmcrypt = mcrypt_module_open($this->cipher_name_mcrypt, '', $mcrypt_modes[$this->mode], '');
1872
1873            // we need the $ecb mcrypt resource (only) in MODE_CFB with enableContinuousBuffer()
1874            // to workaround mcrypt's broken ncfb implementation in buffered mode
1875            // see: {@link http://phpseclib.sourceforge.net/cfb-demo.phps}
1876            if ($this->mode == self::MODE_CFB) {
1877                $this->ecb = mcrypt_module_open($this->cipher_name_mcrypt, '', MCRYPT_MODE_ECB, '');
1878            }
1879        } // else should mcrypt_generic_deinit be called?
1880
1881        if ($this->mode == self::MODE_CFB) {
1882            mcrypt_generic_init($this->ecb, $this->key, str_repeat("\0", $this->block_size));
1883        }
1884    }
1885
1886    /**
1887     * Pads a string
1888     *
1889     * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize.
1890     * $this->block_size - (strlen($text) % $this->block_size) bytes are added, each of which is equal to
1891     * chr($this->block_size - (strlen($text) % $this->block_size)
1892     *
1893     * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
1894     * and padding will, hence forth, be enabled.
1895     *
1896     * @see self::_unpad()
1897     * @param string $text
1898     * @access private
1899     * @return string
1900     */
1901    function _pad($text)
1902    {
1903        $length = strlen($text);
1904
1905        if (!$this->padding) {
1906            if ($length % $this->block_size == 0) {
1907                return $text;
1908            } else {
1909                user_error("The plaintext's length ($length) is not a multiple of the block size ({$this->block_size})");
1910                $this->padding = true;
1911            }
1912        }
1913
1914        $pad = $this->block_size - ($length % $this->block_size);
1915
1916        return str_pad($text, $length + $pad, chr($pad));
1917    }
1918
1919    /**
1920     * Unpads a string.
1921     *
1922     * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
1923     * and false will be returned.
1924     *
1925     * @see self::_pad()
1926     * @param string $text
1927     * @access private
1928     * @return string
1929     */
1930    function _unpad($text)
1931    {
1932        if (!$this->padding) {
1933            return $text;
1934        }
1935
1936        $length = ord($text[strlen($text) - 1]);
1937
1938        if (!$length || $length > $this->block_size) {
1939            return false;
1940        }
1941
1942        return substr($text, 0, -$length);
1943    }
1944
1945    /**
1946     * Clears internal buffers
1947     *
1948     * Clearing/resetting the internal buffers is done everytime
1949     * after disableContinuousBuffer() or on cipher $engine (re)init
1950     * ie after setKey() or setIV()
1951     *
1952     * @access public
1953     * @internal Could, but not must, extend by the child Crypt_* class
1954     */
1955    function _clearBuffers()
1956    {
1957        $this->enbuffer = $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);
1958
1959        // mcrypt's handling of invalid's $iv:
1960        // $this->encryptIV = $this->decryptIV = strlen($this->iv) == $this->block_size ? $this->iv : str_repeat("\0", $this->block_size);
1961        $this->encryptIV = $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, "\0");
1962
1963        if (!$this->skip_key_adjustment) {
1964            $this->key = str_pad(substr($this->key, 0, $this->key_length), $this->key_length, "\0");
1965        }
1966    }
1967
1968    /**
1969     * String Shift
1970     *
1971     * Inspired by array_shift
1972     *
1973     * @param string $string
1974     * @param int $index
1975     * @access private
1976     * @return string
1977     */
1978    function _string_shift(&$string, $index = 1)
1979    {
1980        $substr = substr($string, 0, $index);
1981        $string = substr($string, $index);
1982        return $substr;
1983    }
1984
1985    /**
1986     * String Pop
1987     *
1988     * Inspired by array_pop
1989     *
1990     * @param string $string
1991     * @param int $index
1992     * @access private
1993     * @return string
1994     */
1995    function _string_pop(&$string, $index = 1)
1996    {
1997        $substr = substr($string, -$index);
1998        $string = substr($string, 0, -$index);
1999        return $substr;
2000    }
2001
2002    /**
2003     * Increment the current string
2004     *
2005     * @see self::decrypt()
2006     * @see self::encrypt()
2007     * @param string $var
2008     * @access private
2009     */
2010    function _increment_str(&$var)
2011    {
2012        for ($i = 4; $i <= strlen($var); $i+= 4) {
2013            $temp = substr($var, -$i, 4);
2014            switch ($temp) {
2015                case "\xFF\xFF\xFF\xFF":
2016                    $var = substr_replace($var, "\x00\x00\x00\x00", -$i, 4);
2017                    break;
2018                case "\x7F\xFF\xFF\xFF":
2019                    $var = substr_replace($var, "\x80\x00\x00\x00", -$i, 4);
2020                    return;
2021                default:
2022                    $temp = unpack('Nnum', $temp);
2023                    $var = substr_replace($var, pack('N', $temp['num'] + 1), -$i, 4);
2024                    return;
2025            }
2026        }
2027
2028        $remainder = strlen($var) % 4;
2029
2030        if ($remainder == 0) {
2031            return;
2032        }
2033
2034        $temp = unpack('Nnum', str_pad(substr($var, 0, $remainder), 4, "\0", STR_PAD_LEFT));
2035        $temp = substr(pack('N', $temp['num'] + 1), -$remainder);
2036        $var = substr_replace($var, $temp, 0, $remainder);
2037    }
2038
2039    /**
2040     * Setup the performance-optimized function for de/encrypt()
2041     *
2042     * Stores the created (or existing) callback function-name
2043     * in $this->inline_crypt
2044     *
2045     * Internally for phpseclib developers:
2046     *
2047     *     _setupInlineCrypt() would be called only if:
2048     *
2049     *     - $engine == self::ENGINE_INTERNAL and
2050     *
2051     *     - $use_inline_crypt === true
2052     *
2053     *     - each time on _setup(), after(!) _setupKey()
2054     *
2055     *
2056     *     This ensures that _setupInlineCrypt() has always a
2057     *     full ready2go initializated internal cipher $engine state
2058     *     where, for example, the keys allready expanded,
2059     *     keys/block_size calculated and such.
2060     *
2061     *     It is, each time if called, the responsibility of _setupInlineCrypt():
2062     *
2063     *     - to set $this->inline_crypt to a valid and fully working callback function
2064     *       as a (faster) replacement for encrypt() / decrypt()
2065     *
2066     *     - NOT to create unlimited callback functions (for memory reasons!)
2067     *       no matter how often _setupInlineCrypt() would be called. At some
2068     *       point of amount they must be generic re-useable.
2069     *
2070     *     - the code of _setupInlineCrypt() it self,
2071     *       and the generated callback code,
2072     *       must be, in following order:
2073     *       - 100% safe
2074     *       - 100% compatible to encrypt()/decrypt()
2075     *       - using only php5+ features/lang-constructs/php-extensions if
2076     *         compatibility (down to php4) or fallback is provided
2077     *       - readable/maintainable/understandable/commented and... not-cryptic-styled-code :-)
2078     *       - >= 10% faster than encrypt()/decrypt() [which is, by the way,
2079     *         the reason for the existence of _setupInlineCrypt() :-)]
2080     *       - memory-nice
2081     *       - short (as good as possible)
2082     *
2083     * Note: - _setupInlineCrypt() is using _createInlineCryptFunction() to create the full callback function code.
2084     *       - In case of using inline crypting, _setupInlineCrypt() must extend by the child \phpseclib\Crypt\* class.
2085     *       - The following variable names are reserved:
2086     *         - $_*  (all variable names prefixed with an underscore)
2087     *         - $self (object reference to it self. Do not use $this, but $self instead)
2088     *         - $in (the content of $in has to en/decrypt by the generated code)
2089     *       - The callback function should not use the 'return' statement, but en/decrypt'ing the content of $in only
2090     *
2091     *
2092     * @see self::_setup()
2093     * @see self::_createInlineCryptFunction()
2094     * @see self::encrypt()
2095     * @see self::decrypt()
2096     * @access private
2097     * @internal If a Crypt_* class providing inline crypting it must extend _setupInlineCrypt()
2098     */
2099    function _setupInlineCrypt()
2100    {
2101        // If, for any reason, an extending \phpseclib\Crypt\Base() \phpseclib\Crypt\* class
2102        // not using inline crypting then it must be ensured that: $this->use_inline_crypt = false
2103        // ie in the class var declaration of $use_inline_crypt in general for the \phpseclib\Crypt\* class,
2104        // in the constructor at object instance-time
2105        // or, if it's runtime-specific, at runtime
2106
2107        $this->use_inline_crypt = false;
2108    }
2109
2110    /**
2111     * Creates the performance-optimized function for en/decrypt()
2112     *
2113     * Internally for phpseclib developers:
2114     *
2115     *    _createInlineCryptFunction():
2116     *
2117     *    - merge the $cipher_code [setup'ed by _setupInlineCrypt()]
2118     *      with the current [$this->]mode of operation code
2119     *
2120     *    - create the $inline function, which called by encrypt() / decrypt()
2121     *      as its replacement to speed up the en/decryption operations.
2122     *
2123     *    - return the name of the created $inline callback function
2124     *
2125     *    - used to speed up en/decryption
2126     *
2127     *
2128     *
2129     *    The main reason why can speed up things [up to 50%] this way are:
2130     *
2131     *    - using variables more effective then regular.
2132     *      (ie no use of expensive arrays but integers $k_0, $k_1 ...
2133     *      or even, for example, the pure $key[] values hardcoded)
2134     *
2135     *    - avoiding 1000's of function calls of ie _encryptBlock()
2136     *      but inlining the crypt operations.
2137     *      in the mode of operation for() loop.
2138     *
2139     *    - full loop unroll the (sometimes key-dependent) rounds
2140     *      avoiding this way ++$i counters and runtime-if's etc...
2141     *
2142     *    The basic code architectur of the generated $inline en/decrypt()
2143     *    lambda function, in pseudo php, is:
2144     *
2145     *    <code>
2146     *    +----------------------------------------------------------------------------------------------+
2147     *    | callback $inline = create_function:                                                          |
2148     *    | lambda_function_0001_crypt_ECB($action, $text)                                               |
2149     *    | {                                                                                            |
2150     *    |     INSERT PHP CODE OF:                                                                      |
2151     *    |     $cipher_code['init_crypt'];                  // general init code.                       |
2152     *    |                                                  // ie: $sbox'es declarations used for       |
2153     *    |                                                  //     encrypt and decrypt'ing.             |
2154     *    |                                                                                              |
2155     *    |     switch ($action) {                                                                       |
2156     *    |         case 'encrypt':                                                                      |
2157     *    |             INSERT PHP CODE OF:                                                              |
2158     *    |             $cipher_code['init_encrypt'];       // encrypt sepcific init code.               |
2159     *    |                                                    ie: specified $key or $box                |
2160     *    |                                                        declarations for encrypt'ing.         |
2161     *    |                                                                                              |
2162     *    |             foreach ($ciphertext) {                                                          |
2163     *    |                 $in = $block_size of $ciphertext;                                            |
2164     *    |                                                                                              |
2165     *    |                 INSERT PHP CODE OF:                                                          |
2166     *    |                 $cipher_code['encrypt_block'];  // encrypt's (string) $in, which is always:  |
2167     *    |                                                 // strlen($in) == $this->block_size          |
2168     *    |                                                 // here comes the cipher algorithm in action |
2169     *    |                                                 // for encryption.                           |
2170     *    |                                                 // $cipher_code['encrypt_block'] has to      |
2171     *    |                                                 // encrypt the content of the $in variable   |
2172     *    |                                                                                              |
2173     *    |                 $plaintext .= $in;                                                           |
2174     *    |             }                                                                                |
2175     *    |             return $plaintext;                                                               |
2176     *    |                                                                                              |
2177     *    |         case 'decrypt':                                                                      |
2178     *    |             INSERT PHP CODE OF:                                                              |
2179     *    |             $cipher_code['init_decrypt'];       // decrypt sepcific init code                |
2180     *    |                                                    ie: specified $key or $box                |
2181     *    |                                                        declarations for decrypt'ing.         |
2182     *    |             foreach ($plaintext) {                                                           |
2183     *    |                 $in = $block_size of $plaintext;                                             |
2184     *    |                                                                                              |
2185     *    |                 INSERT PHP CODE OF:                                                          |
2186     *    |                 $cipher_code['decrypt_block'];  // decrypt's (string) $in, which is always   |
2187     *    |                                                 // strlen($in) == $this->block_size          |
2188     *    |                                                 // here comes the cipher algorithm in action |
2189     *    |                                                 // for decryption.                           |
2190     *    |                                                 // $cipher_code['decrypt_block'] has to      |
2191     *    |                                                 // decrypt the content of the $in variable   |
2192     *    |                 $ciphertext .= $in;                                                          |
2193     *    |             }                                                                                |
2194     *    |             return $ciphertext;                                                              |
2195     *    |     }                                                                                        |
2196     *    | }                                                                                            |
2197     *    +----------------------------------------------------------------------------------------------+
2198     *    </code>
2199     *
2200     *    See also the \phpseclib\Crypt\*::_setupInlineCrypt()'s for
2201     *    productive inline $cipher_code's how they works.
2202     *
2203     *    Structure of:
2204     *    <code>
2205     *    $cipher_code = array(
2206     *        'init_crypt'    => (string) '', // optional
2207     *        'init_encrypt'  => (string) '', // optional
2208     *        'init_decrypt'  => (string) '', // optional
2209     *        'encrypt_block' => (string) '', // required
2210     *        'decrypt_block' => (string) ''  // required
2211     *    );
2212     *    </code>
2213     *
2214     * @see self::_setupInlineCrypt()
2215     * @see self::encrypt()
2216     * @see self::decrypt()
2217     * @param array $cipher_code
2218     * @access private
2219     * @return string (the name of the created callback function)
2220     */
2221    function _createInlineCryptFunction($cipher_code)
2222    {
2223        $block_size = $this->block_size;
2224
2225        // optional
2226        $init_crypt    = isset($cipher_code['init_crypt'])    ? $cipher_code['init_crypt']    : '';
2227        $init_encrypt  = isset($cipher_code['init_encrypt'])  ? $cipher_code['init_encrypt']  : '';
2228        $init_decrypt  = isset($cipher_code['init_decrypt'])  ? $cipher_code['init_decrypt']  : '';
2229        // required
2230        $encrypt_block = $cipher_code['encrypt_block'];
2231        $decrypt_block = $cipher_code['decrypt_block'];
2232
2233        // Generating mode of operation inline code,
2234        // merged with the $cipher_code algorithm
2235        // for encrypt- and decryption.
2236        switch ($this->mode) {
2237            case self::MODE_ECB:
2238                $encrypt = $init_encrypt . '
2239                    $_ciphertext = "";
2240                    $_plaintext_len = strlen($_text);
2241
2242                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2243                        $in = substr($_text, $_i, '.$block_size.');
2244                        '.$encrypt_block.'
2245                        $_ciphertext.= $in;
2246                    }
2247
2248                    return $_ciphertext;
2249                    ';
2250
2251                $decrypt = $init_decrypt . '
2252                    $_plaintext = "";
2253                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
2254                    $_ciphertext_len = strlen($_text);
2255
2256                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2257                        $in = substr($_text, $_i, '.$block_size.');
2258                        '.$decrypt_block.'
2259                        $_plaintext.= $in;
2260                    }
2261
2262                    return $self->_unpad($_plaintext);
2263                    ';
2264                break;
2265            case self::MODE_CTR:
2266                $encrypt = $init_encrypt . '
2267                    $_ciphertext = "";
2268                    $_plaintext_len = strlen($_text);
2269                    $_xor = $self->encryptIV;
2270                    $_buffer = &$self->enbuffer;
2271                    if (strlen($_buffer["ciphertext"])) {
2272                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2273                            $_block = substr($_text, $_i, '.$block_size.');
2274                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
2275                                $in = $_xor;
2276                                '.$encrypt_block.'
2277                                $self->_increment_str($_xor);
2278                                $_buffer["ciphertext"].= $in;
2279                            }
2280                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
2281                            $_ciphertext.= $_block ^ $_key;
2282                        }
2283                    } else {
2284                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2285                            $_block = substr($_text, $_i, '.$block_size.');
2286                            $in = $_xor;
2287                            '.$encrypt_block.'
2288                            $self->_increment_str($_xor);
2289                            $_key = $in;
2290                            $_ciphertext.= $_block ^ $_key;
2291                        }
2292                    }
2293                    if ($self->continuousBuffer) {
2294                        $self->encryptIV = $_xor;
2295                        if ($_start = $_plaintext_len % '.$block_size.') {
2296                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
2297                        }
2298                    }
2299
2300                    return $_ciphertext;
2301                ';
2302
2303                $decrypt = $init_encrypt . '
2304                    $_plaintext = "";
2305                    $_ciphertext_len = strlen($_text);
2306                    $_xor = $self->decryptIV;
2307                    $_buffer = &$self->debuffer;
2308
2309                    if (strlen($_buffer["ciphertext"])) {
2310                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2311                            $_block = substr($_text, $_i, '.$block_size.');
2312                            if (strlen($_block) > strlen($_buffer["ciphertext"])) {
2313                                $in = $_xor;
2314                                '.$encrypt_block.'
2315                                $self->_increment_str($_xor);
2316                                $_buffer["ciphertext"].= $in;
2317                            }
2318                            $_key = $self->_string_shift($_buffer["ciphertext"], '.$block_size.');
2319                            $_plaintext.= $_block ^ $_key;
2320                        }
2321                    } else {
2322                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2323                            $_block = substr($_text, $_i, '.$block_size.');
2324                            $in = $_xor;
2325                            '.$encrypt_block.'
2326                            $self->_increment_str($_xor);
2327                            $_key = $in;
2328                            $_plaintext.= $_block ^ $_key;
2329                        }
2330                    }
2331                    if ($self->continuousBuffer) {
2332                        $self->decryptIV = $_xor;
2333                        if ($_start = $_ciphertext_len % '.$block_size.') {
2334                            $_buffer["ciphertext"] = substr($_key, $_start) . $_buffer["ciphertext"];
2335                        }
2336                    }
2337
2338                    return $_plaintext;
2339                    ';
2340                break;
2341            case self::MODE_CFB:
2342                $encrypt = $init_encrypt . '
2343                    $_ciphertext = "";
2344                    $_buffer = &$self->enbuffer;
2345
2346                    if ($self->continuousBuffer) {
2347                        $_iv = &$self->encryptIV;
2348                        $_pos = &$_buffer["pos"];
2349                    } else {
2350                        $_iv = $self->encryptIV;
2351                        $_pos = 0;
2352                    }
2353                    $_len = strlen($_text);
2354                    $_i = 0;
2355                    if ($_pos) {
2356                        $_orig_pos = $_pos;
2357                        $_max = '.$block_size.' - $_pos;
2358                        if ($_len >= $_max) {
2359                            $_i = $_max;
2360                            $_len-= $_max;
2361                            $_pos = 0;
2362                        } else {
2363                            $_i = $_len;
2364                            $_pos+= $_len;
2365                            $_len = 0;
2366                        }
2367                        $_ciphertext = substr($_iv, $_orig_pos) ^ $_text;
2368                        $_iv = substr_replace($_iv, $_ciphertext, $_orig_pos, $_i);
2369                    }
2370                    while ($_len >= '.$block_size.') {
2371                        $in = $_iv;
2372                        '.$encrypt_block.';
2373                        $_iv = $in ^ substr($_text, $_i, '.$block_size.');
2374                        $_ciphertext.= $_iv;
2375                        $_len-= '.$block_size.';
2376                        $_i+= '.$block_size.';
2377                    }
2378                    if ($_len) {
2379                        $in = $_iv;
2380                        '.$encrypt_block.'
2381                        $_iv = $in;
2382                        $_block = $_iv ^ substr($_text, $_i);
2383                        $_iv = substr_replace($_iv, $_block, 0, $_len);
2384                        $_ciphertext.= $_block;
2385                        $_pos = $_len;
2386                    }
2387                    return $_ciphertext;
2388                ';
2389
2390                $decrypt = $init_encrypt . '
2391                    $_plaintext = "";
2392                    $_buffer = &$self->debuffer;
2393
2394                    if ($self->continuousBuffer) {
2395                        $_iv = &$self->decryptIV;
2396                        $_pos = &$_buffer["pos"];
2397                    } else {
2398                        $_iv = $self->decryptIV;
2399                        $_pos = 0;
2400                    }
2401                    $_len = strlen($_text);
2402                    $_i = 0;
2403                    if ($_pos) {
2404                        $_orig_pos = $_pos;
2405                        $_max = '.$block_size.' - $_pos;
2406                        if ($_len >= $_max) {
2407                            $_i = $_max;
2408                            $_len-= $_max;
2409                            $_pos = 0;
2410                        } else {
2411                            $_i = $_len;
2412                            $_pos+= $_len;
2413                            $_len = 0;
2414                        }
2415                        $_plaintext = substr($_iv, $_orig_pos) ^ $_text;
2416                        $_iv = substr_replace($_iv, substr($_text, 0, $_i), $_orig_pos, $_i);
2417                    }
2418                    while ($_len >= '.$block_size.') {
2419                        $in = $_iv;
2420                        '.$encrypt_block.'
2421                        $_iv = $in;
2422                        $cb = substr($_text, $_i, '.$block_size.');
2423                        $_plaintext.= $_iv ^ $cb;
2424                        $_iv = $cb;
2425                        $_len-= '.$block_size.';
2426                        $_i+= '.$block_size.';
2427                    }
2428                    if ($_len) {
2429                        $in = $_iv;
2430                        '.$encrypt_block.'
2431                        $_iv = $in;
2432                        $_plaintext.= $_iv ^ substr($_text, $_i);
2433                        $_iv = substr_replace($_iv, substr($_text, $_i), 0, $_len);
2434                        $_pos = $_len;
2435                    }
2436
2437                    return $_plaintext;
2438                    ';
2439                break;
2440            case self::MODE_CFB8:
2441                $encrypt = $init_encrypt . '
2442                    $_ciphertext = "";
2443                    $_len = strlen($_text);
2444                    $_iv = $self->encryptIV;
2445
2446                    for ($_i = 0; $_i < $_len; ++$_i) {
2447                        $in = $_iv;
2448                        '.$encrypt_block.'
2449                        $_ciphertext .= ($_c = $_text[$_i] ^ $in);
2450                        $_iv = substr($_iv, 1) . $_c;
2451                    }
2452
2453                    if ($self->continuousBuffer) {
2454                        if ($_len >= '.$block_size.') {
2455                            $self->encryptIV = substr($_ciphertext, -'.$block_size.');
2456                        } else {
2457                            $self->encryptIV = substr($self->encryptIV, $_len - '.$block_size.') . substr($_ciphertext, -$_len);
2458                        }
2459                    }
2460
2461                    return $_ciphertext;
2462                    ';
2463                $decrypt = $init_encrypt . '
2464                    $_plaintext = "";
2465                    $_len = strlen($_text);
2466                    $_iv = $self->decryptIV;
2467
2468                    for ($_i = 0; $_i < $_len; ++$_i) {
2469                        $in = $_iv;
2470                        '.$encrypt_block.'
2471                        $_plaintext .= $_text[$_i] ^ $in;
2472                        $_iv = substr($_iv, 1) . $_text[$_i];
2473                    }
2474
2475                    if ($self->continuousBuffer) {
2476                        if ($_len >= '.$block_size.') {
2477                            $self->decryptIV = substr($_text, -'.$block_size.');
2478                        } else {
2479                            $self->decryptIV = substr($self->decryptIV, $_len - '.$block_size.') . substr($_text, -$_len);
2480                        }
2481                    }
2482
2483                    return $_plaintext;
2484                    ';
2485                break;
2486            case self::MODE_OFB:
2487                $encrypt = $init_encrypt . '
2488                    $_ciphertext = "";
2489                    $_plaintext_len = strlen($_text);
2490                    $_xor = $self->encryptIV;
2491                    $_buffer = &$self->enbuffer;
2492
2493                    if (strlen($_buffer["xor"])) {
2494                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2495                            $_block = substr($_text, $_i, '.$block_size.');
2496                            if (strlen($_block) > strlen($_buffer["xor"])) {
2497                                $in = $_xor;
2498                                '.$encrypt_block.'
2499                                $_xor = $in;
2500                                $_buffer["xor"].= $_xor;
2501                            }
2502                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
2503                            $_ciphertext.= $_block ^ $_key;
2504                        }
2505                    } else {
2506                        for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2507                            $in = $_xor;
2508                            '.$encrypt_block.'
2509                            $_xor = $in;
2510                            $_ciphertext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
2511                        }
2512                        $_key = $_xor;
2513                    }
2514                    if ($self->continuousBuffer) {
2515                        $self->encryptIV = $_xor;
2516                        if ($_start = $_plaintext_len % '.$block_size.') {
2517                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
2518                        }
2519                    }
2520                    return $_ciphertext;
2521                    ';
2522
2523                $decrypt = $init_encrypt . '
2524                    $_plaintext = "";
2525                    $_ciphertext_len = strlen($_text);
2526                    $_xor = $self->decryptIV;
2527                    $_buffer = &$self->debuffer;
2528
2529                    if (strlen($_buffer["xor"])) {
2530                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2531                            $_block = substr($_text, $_i, '.$block_size.');
2532                            if (strlen($_block) > strlen($_buffer["xor"])) {
2533                                $in = $_xor;
2534                                '.$encrypt_block.'
2535                                $_xor = $in;
2536                                $_buffer["xor"].= $_xor;
2537                            }
2538                            $_key = $self->_string_shift($_buffer["xor"], '.$block_size.');
2539                            $_plaintext.= $_block ^ $_key;
2540                        }
2541                    } else {
2542                        for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2543                            $in = $_xor;
2544                            '.$encrypt_block.'
2545                            $_xor = $in;
2546                            $_plaintext.= substr($_text, $_i, '.$block_size.') ^ $_xor;
2547                        }
2548                        $_key = $_xor;
2549                    }
2550                    if ($self->continuousBuffer) {
2551                        $self->decryptIV = $_xor;
2552                        if ($_start = $_ciphertext_len % '.$block_size.') {
2553                             $_buffer["xor"] = substr($_key, $_start) . $_buffer["xor"];
2554                        }
2555                    }
2556                    return $_plaintext;
2557                    ';
2558                break;
2559            case self::MODE_STREAM:
2560                $encrypt = $init_encrypt . '
2561                    $_ciphertext = "";
2562                    '.$encrypt_block.'
2563                    return $_ciphertext;
2564                    ';
2565                $decrypt = $init_decrypt . '
2566                    $_plaintext = "";
2567                    '.$decrypt_block.'
2568                    return $_plaintext;
2569                    ';
2570                break;
2571            // case self::MODE_CBC:
2572            default:
2573                $encrypt = $init_encrypt . '
2574                    $_ciphertext = "";
2575                    $_plaintext_len = strlen($_text);
2576
2577                    $in = $self->encryptIV;
2578
2579                    for ($_i = 0; $_i < $_plaintext_len; $_i+= '.$block_size.') {
2580                        $in = substr($_text, $_i, '.$block_size.') ^ $in;
2581                        '.$encrypt_block.'
2582                        $_ciphertext.= $in;
2583                    }
2584
2585                    if ($self->continuousBuffer) {
2586                        $self->encryptIV = $in;
2587                    }
2588
2589                    return $_ciphertext;
2590                    ';
2591
2592                $decrypt = $init_decrypt . '
2593                    $_plaintext = "";
2594                    $_text = str_pad($_text, strlen($_text) + ('.$block_size.' - strlen($_text) % '.$block_size.') % '.$block_size.', chr(0));
2595                    $_ciphertext_len = strlen($_text);
2596
2597                    $_iv = $self->decryptIV;
2598
2599                    for ($_i = 0; $_i < $_ciphertext_len; $_i+= '.$block_size.') {
2600                        $in = $_block = substr($_text, $_i, '.$block_size.');
2601                        '.$decrypt_block.'
2602                        $_plaintext.= $in ^ $_iv;
2603                        $_iv = $_block;
2604                    }
2605
2606                    if ($self->continuousBuffer) {
2607                        $self->decryptIV = $_iv;
2608                    }
2609
2610                    return $self->_unpad($_plaintext);
2611                    ';
2612                break;
2613        }
2614
2615        // Create the $inline function and return its name as string. Ready to run!
2616        eval('$func = function ($_action, &$self, $_text) { ' . $init_crypt . 'if ($_action == "encrypt") { ' . $encrypt . ' } else { ' . $decrypt . ' } };');
2617        return $func;
2618    }
2619
2620    /**
2621     * Holds the lambda_functions table (classwide)
2622     *
2623     * Each name of the lambda function, created from
2624     * _setupInlineCrypt() && _createInlineCryptFunction()
2625     * is stored, classwide (!), here for reusing.
2626     *
2627     * The string-based index of $function is a classwide
2628     * unique value representing, at least, the $mode of
2629     * operation (or more... depends of the optimizing level)
2630     * for which $mode the lambda function was created.
2631     *
2632     * @access private
2633     * @return array &$functions
2634     */
2635    function &_getLambdaFunctions()
2636    {
2637        static $functions = array();
2638        return $functions;
2639    }
2640
2641    /**
2642     * Generates a digest from $bytes
2643     *
2644     * @see self::_setupInlineCrypt()
2645     * @access private
2646     * @param string $bytes
2647     * @return string
2648     */
2649    function _hashInlineCryptFunction($bytes)
2650    {
2651        if (!isset(self::$WHIRLPOOL_AVAILABLE)) {
2652            self::$WHIRLPOOL_AVAILABLE = extension_loaded('hash') && in_array('whirlpool', hash_algos());
2653        }
2654
2655        $result = '';
2656        $hash = $bytes;
2657
2658        switch (true) {
2659            case self::$WHIRLPOOL_AVAILABLE:
2660                foreach (str_split($bytes, 64) as $t) {
2661                    $hash = hash('whirlpool', $hash, true);
2662                    $result .= $t ^ $hash;
2663                }
2664                return $result . hash('whirlpool', $hash, true);
2665            default:
2666                $len = strlen($bytes);
2667                for ($i = 0; $i < $len; $i+=20) {
2668                    $t = substr($bytes, $i, 20);
2669                    $hash = pack('H*', sha1($hash));
2670                    $result .= $t ^ $hash;
2671                }
2672                return $result . pack('H*', sha1($hash));
2673        }
2674    }
2675
2676    /**
2677     * Convert float to int
2678     *
2679     * On ARM CPUs converting floats to ints doesn't always work
2680     *
2681     * @access private
2682     * @param string $x
2683     * @return int
2684     */
2685    function safe_intval($x)
2686    {
2687        switch (true) {
2688            case is_int($x):
2689            // PHP 5.3, per http://php.net/releases/5_3_0.php, introduced "more consistent float rounding"
2690            case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
2691                return $x;
2692        }
2693        return (fmod($x, 0x80000000) & 0x7FFFFFFF) |
2694            ((fmod(floor($x / 0x80000000), 2) & 1) << 31);
2695    }
2696
2697    /**
2698     * eval()'able string for in-line float to int
2699     *
2700     * @access private
2701     * @return string
2702     */
2703    function safe_intval_inline()
2704    {
2705        switch (true) {
2706            case defined('PHP_INT_SIZE') && PHP_INT_SIZE == 8:
2707            case (php_uname('m') & "\xDF\xDF\xDF") != 'ARM':
2708                return '%s';
2709                break;
2710            default:
2711                $safeint = '(is_int($temp = %s) ? $temp : (fmod($temp, 0x80000000) & 0x7FFFFFFF) | ';
2712                return $safeint . '((fmod(floor($temp / 0x80000000), 2) & 1) << 31))';
2713        }
2714    }
2715
2716    /**
2717     * Dummy error handler to suppress mcrypt errors
2718     *
2719     * @access private
2720     */
2721    function do_nothing()
2722    {
2723    }
2724}
2725