Passed
Branch development (e0e718)
by Nils
04:45
created

SymmetricKey   D

Complexity

Total Complexity 265

Size/Duplication

Total Lines 2525
Duplicated Lines 20.24 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 511
loc 2525
rs 4.4102
c 0
b 0
f 0
wmc 265
lcom 2
cbo 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like SymmetricKey often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use SymmetricKey, and based on these observations, apply Extract Interface, too.

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