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