1<?php
2
3/**
4 * Pure-PHP implementation of RC2.
5 *
6 * Uses mcrypt, if available, and an internal implementation, otherwise.
7 *
8 * PHP version 5
9 *
10 * Useful resources are as follows:
11 *
12 *  - {@link http://tools.ietf.org/html/rfc2268}
13 *
14 * Here's a short example of how to use this library:
15 * <code>
16 * <?php
17 *    include 'vendor/autoload.php';
18 *
19 *    $rc2 = new \phpseclib3\Crypt\RC2('ctr');
20 *
21 *    $rc2->setKey('abcdefgh');
22 *
23 *    $plaintext = str_repeat('a', 1024);
24 *
25 *    echo $rc2->decrypt($rc2->encrypt($plaintext));
26 * ?>
27 * </code>
28 *
29 * @category Crypt
30 * @package  RC2
31 * @author   Patrick Monnerat <pm@datasphere.ch>
32 * @license  http://www.opensource.org/licenses/mit-license.html  MIT License
33 * @link     http://phpseclib.sourceforge.net
34 */
35
36namespace phpseclib3\Crypt;
37
38use phpseclib3\Crypt\Common\BlockCipher;
39use phpseclib3\Exception\BadModeException;
40
41/**
42 * Pure-PHP implementation of RC2.
43 *
44 * @package RC2
45 * @access  public
46 */
47class RC2 extends BlockCipher
48{
49    /**
50     * Block Length of the cipher
51     *
52     * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
53     * @var int
54     * @access private
55     */
56    protected $block_size = 8;
57
58    /**
59     * The Key
60     *
61     * @see \phpseclib3\Crypt\Common\SymmetricKey::key
62     * @see self::setKey()
63     * @var string
64     * @access private
65     */
66    protected $key;
67
68    /**
69     * The Original (unpadded) Key
70     *
71     * @see \phpseclib3\Crypt\Common\SymmetricKey::key
72     * @see self::setKey()
73     * @see self::encrypt()
74     * @see self::decrypt()
75     * @var string
76     * @access private
77     */
78    private $orig_key;
79
80    /**
81     * Don't truncate / null pad key
82     *
83     * @see \phpseclib3\Crypt\Common\SymmetricKey::clearBuffers()
84     * @var bool
85     * @access private
86     */
87    private $skip_key_adjustment = true;
88
89    /**
90     * Key Length (in bytes)
91     *
92     * @see \phpseclib3\Crypt\RC2::setKeyLength()
93     * @var int
94     * @access private
95     */
96    protected $key_length = 16; // = 128 bits
97
98    /**
99     * The mcrypt specific name of the cipher
100     *
101     * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
102     * @var string
103     * @access private
104     */
105    protected $cipher_name_mcrypt = 'rc2';
106
107    /**
108     * Optimizing value while CFB-encrypting
109     *
110     * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
111     * @var int
112     * @access private
113     */
114    protected $cfb_init_len = 500;
115
116    /**
117     * The key length in bits.
118     *
119     * {@internal Should be in range [1..1024].}
120     *
121     * {@internal Changing this value after setting the key has no effect.}
122     *
123     * @see self::setKeyLength()
124     * @see self::setKey()
125     * @var int
126     * @access private
127     */
128    private $default_key_length = 1024;
129
130    /**
131     * The key length in bits.
132     *
133     * {@internal Should be in range [1..1024].}
134     *
135     * @see self::isValidEnine()
136     * @see self::setKey()
137     * @var int
138     * @access private
139     */
140    private $current_key_length;
141
142    /**
143     * The Key Schedule
144     *
145     * @see self::setupKey()
146     * @var array
147     * @access private
148     */
149    private $keys;
150
151    /**
152     * Key expansion randomization table.
153     * Twice the same 256-value sequence to save a modulus in key expansion.
154     *
155     * @see self::setKey()
156     * @var array
157     * @access private
158     */
159    private static $pitable = [
160        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
161        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
162        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
163        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
164        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
165        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
166        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
167        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
168        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
169        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
170        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
171        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
172        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
173        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
174        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
175        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
176        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
177        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
178        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
179        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
180        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
181        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
182        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
183        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
184        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
185        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
186        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
187        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
188        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
189        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
190        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
191        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD,
192        0xD9, 0x78, 0xF9, 0xC4, 0x19, 0xDD, 0xB5, 0xED,
193        0x28, 0xE9, 0xFD, 0x79, 0x4A, 0xA0, 0xD8, 0x9D,
194        0xC6, 0x7E, 0x37, 0x83, 0x2B, 0x76, 0x53, 0x8E,
195        0x62, 0x4C, 0x64, 0x88, 0x44, 0x8B, 0xFB, 0xA2,
196        0x17, 0x9A, 0x59, 0xF5, 0x87, 0xB3, 0x4F, 0x13,
197        0x61, 0x45, 0x6D, 0x8D, 0x09, 0x81, 0x7D, 0x32,
198        0xBD, 0x8F, 0x40, 0xEB, 0x86, 0xB7, 0x7B, 0x0B,
199        0xF0, 0x95, 0x21, 0x22, 0x5C, 0x6B, 0x4E, 0x82,
200        0x54, 0xD6, 0x65, 0x93, 0xCE, 0x60, 0xB2, 0x1C,
201        0x73, 0x56, 0xC0, 0x14, 0xA7, 0x8C, 0xF1, 0xDC,
202        0x12, 0x75, 0xCA, 0x1F, 0x3B, 0xBE, 0xE4, 0xD1,
203        0x42, 0x3D, 0xD4, 0x30, 0xA3, 0x3C, 0xB6, 0x26,
204        0x6F, 0xBF, 0x0E, 0xDA, 0x46, 0x69, 0x07, 0x57,
205        0x27, 0xF2, 0x1D, 0x9B, 0xBC, 0x94, 0x43, 0x03,
206        0xF8, 0x11, 0xC7, 0xF6, 0x90, 0xEF, 0x3E, 0xE7,
207        0x06, 0xC3, 0xD5, 0x2F, 0xC8, 0x66, 0x1E, 0xD7,
208        0x08, 0xE8, 0xEA, 0xDE, 0x80, 0x52, 0xEE, 0xF7,
209        0x84, 0xAA, 0x72, 0xAC, 0x35, 0x4D, 0x6A, 0x2A,
210        0x96, 0x1A, 0xD2, 0x71, 0x5A, 0x15, 0x49, 0x74,
211        0x4B, 0x9F, 0xD0, 0x5E, 0x04, 0x18, 0xA4, 0xEC,
212        0xC2, 0xE0, 0x41, 0x6E, 0x0F, 0x51, 0xCB, 0xCC,
213        0x24, 0x91, 0xAF, 0x50, 0xA1, 0xF4, 0x70, 0x39,
214        0x99, 0x7C, 0x3A, 0x85, 0x23, 0xB8, 0xB4, 0x7A,
215        0xFC, 0x02, 0x36, 0x5B, 0x25, 0x55, 0x97, 0x31,
216        0x2D, 0x5D, 0xFA, 0x98, 0xE3, 0x8A, 0x92, 0xAE,
217        0x05, 0xDF, 0x29, 0x10, 0x67, 0x6C, 0xBA, 0xC9,
218        0xD3, 0x00, 0xE6, 0xCF, 0xE1, 0x9E, 0xA8, 0x2C,
219        0x63, 0x16, 0x01, 0x3F, 0x58, 0xE2, 0x89, 0xA9,
220        0x0D, 0x38, 0x34, 0x1B, 0xAB, 0x33, 0xFF, 0xB0,
221        0xBB, 0x48, 0x0C, 0x5F, 0xB9, 0xB1, 0xCD, 0x2E,
222        0xC5, 0xF3, 0xDB, 0x47, 0xE5, 0xA5, 0x9C, 0x77,
223        0x0A, 0xA6, 0x20, 0x68, 0xFE, 0x7F, 0xC1, 0xAD
224    ];
225
226    /**
227     * Inverse key expansion randomization table.
228     *
229     * @see self::setKey()
230     * @var array
231     * @access private
232     */
233    private static $invpitable = [
234        0xD1, 0xDA, 0xB9, 0x6F, 0x9C, 0xC8, 0x78, 0x66,
235        0x80, 0x2C, 0xF8, 0x37, 0xEA, 0xE0, 0x62, 0xA4,
236        0xCB, 0x71, 0x50, 0x27, 0x4B, 0x95, 0xD9, 0x20,
237        0x9D, 0x04, 0x91, 0xE3, 0x47, 0x6A, 0x7E, 0x53,
238        0xFA, 0x3A, 0x3B, 0xB4, 0xA8, 0xBC, 0x5F, 0x68,
239        0x08, 0xCA, 0x8F, 0x14, 0xD7, 0xC0, 0xEF, 0x7B,
240        0x5B, 0xBF, 0x2F, 0xE5, 0xE2, 0x8C, 0xBA, 0x12,
241        0xE1, 0xAF, 0xB2, 0x54, 0x5D, 0x59, 0x76, 0xDB,
242        0x32, 0xA2, 0x58, 0x6E, 0x1C, 0x29, 0x64, 0xF3,
243        0xE9, 0x96, 0x0C, 0x98, 0x19, 0x8D, 0x3E, 0x26,
244        0xAB, 0xA5, 0x85, 0x16, 0x40, 0xBD, 0x49, 0x67,
245        0xDC, 0x22, 0x94, 0xBB, 0x3C, 0xC1, 0x9B, 0xEB,
246        0x45, 0x28, 0x18, 0xD8, 0x1A, 0x42, 0x7D, 0xCC,
247        0xFB, 0x65, 0x8E, 0x3D, 0xCD, 0x2A, 0xA3, 0x60,
248        0xAE, 0x93, 0x8A, 0x48, 0x97, 0x51, 0x15, 0xF7,
249        0x01, 0x0B, 0xB7, 0x36, 0xB1, 0x2E, 0x11, 0xFD,
250        0x84, 0x2D, 0x3F, 0x13, 0x88, 0xB3, 0x34, 0x24,
251        0x1B, 0xDE, 0xC5, 0x1D, 0x4D, 0x2B, 0x17, 0x31,
252        0x74, 0xA9, 0xC6, 0x43, 0x6D, 0x39, 0x90, 0xBE,
253        0xC3, 0xB0, 0x21, 0x6B, 0xF6, 0x0F, 0xD5, 0x99,
254        0x0D, 0xAC, 0x1F, 0x5C, 0x9E, 0xF5, 0xF9, 0x4C,
255        0xD6, 0xDF, 0x89, 0xE4, 0x8B, 0xFF, 0xC7, 0xAA,
256        0xE7, 0xED, 0x46, 0x25, 0xB6, 0x06, 0x5E, 0x35,
257        0xB5, 0xEC, 0xCE, 0xE8, 0x6C, 0x30, 0x55, 0x61,
258        0x4A, 0xFE, 0xA0, 0x79, 0x03, 0xF0, 0x10, 0x72,
259        0x7C, 0xCF, 0x52, 0xA6, 0xA7, 0xEE, 0x44, 0xD3,
260        0x9A, 0x57, 0x92, 0xD0, 0x5A, 0x7A, 0x41, 0x7F,
261        0x0E, 0x00, 0x63, 0xF2, 0x4F, 0x05, 0x83, 0xC9,
262        0xA1, 0xD4, 0xDD, 0xC4, 0x56, 0xF4, 0xD2, 0x77,
263        0x81, 0x09, 0x82, 0x33, 0x9F, 0x07, 0x86, 0x75,
264        0x38, 0x4E, 0x69, 0xF1, 0xAD, 0x23, 0x73, 0x87,
265        0x70, 0x02, 0xC2, 0x1E, 0xB8, 0x0A, 0xFC, 0xE6
266    ];
267
268    /**
269     * Default Constructor.
270     *
271     * @param string $mode
272     * @access public
273     * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
274     */
275    public function __construct($mode)
276    {
277        parent::__construct($mode);
278
279        if ($this->mode == self::MODE_STREAM) {
280            throw new BadModeException('Block ciphers cannot be ran in stream mode');
281        }
282    }
283
284    /**
285     * Test for engine validity
286     *
287     * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
288     *
289     * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct()
290     * @param int $engine
291     * @access protected
292     * @return bool
293     */
294    protected function isValidEngineHelper($engine)
295    {
296        switch ($engine) {
297            case self::ENGINE_OPENSSL:
298                if ($this->current_key_length != 128 || strlen($this->orig_key) < 16) {
299                    return false;
300                }
301                $this->cipher_name_openssl_ecb = 'rc2-ecb';
302                $this->cipher_name_openssl = 'rc2-' . $this->openssl_translate_mode();
303        }
304
305        return parent::isValidEngineHelper($engine);
306    }
307
308    /**
309     * Sets the key length.
310     *
311     * Valid key lengths are 8 to 1024.
312     * Calling this function after setting the key has no effect until the next
313     *  \phpseclib3\Crypt\RC2::setKey() call.
314     *
315     * @access public
316     * @param int $length in bits
317     * @throws \LengthException if the key length isn't supported
318     */
319    public function setKeyLength($length)
320    {
321        if ($length < 8 || $length > 1024) {
322            throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
323        }
324
325        $this->default_key_length = $this->current_key_length = $length;
326        $this->explicit_key_length = $length >> 3;
327    }
328
329    /**
330     * Returns the current key length
331     *
332     * @access public
333     * @return int
334     */
335    public function getKeyLength()
336    {
337        return $this->current_key_length;
338    }
339
340    /**
341     * Sets the key.
342     *
343     * Keys can be of any length. RC2, itself, uses 8 to 1024 bit keys (eg.
344     * strlen($key) <= 128), however, we only use the first 128 bytes if $key
345     * has more then 128 bytes in it, and set $key to a single null byte if
346     * it is empty.
347     *
348     * @see \phpseclib3\Crypt\Common\SymmetricKey::setKey()
349     * @access public
350     * @param string $key
351     * @param int|boolean $t1 optional Effective key length in bits.
352     * @throws \LengthException if the key length isn't supported
353     */
354    public function setKey($key, $t1 = false)
355    {
356        $this->orig_key = $key;
357
358        if ($t1 === false) {
359            $t1 = $this->default_key_length;
360        }
361
362        if ($t1 < 1 || $t1 > 1024) {
363            throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 1024 bits, inclusive, are supported');
364        }
365
366        $this->current_key_length = $t1;
367        if (strlen($key) < 1 || strlen($key) > 128) {
368            throw new \LengthException('Key of size ' . strlen($key) . ' not supported by this algorithm. Only keys of sizes between 8 and 1024 bits, inclusive, are supported');
369        }
370
371        $t = strlen($key);
372
373        // The mcrypt RC2 implementation only supports effective key length
374        // of 1024 bits. It is however possible to handle effective key
375        // lengths in range 1..1024 by expanding the key and applying
376        // inverse pitable mapping to the first byte before submitting it
377        // to mcrypt.
378
379        // Key expansion.
380        $l = array_values(unpack('C*', $key));
381        $t8 = ($t1 + 7) >> 3;
382        $tm = 0xFF >> (8 * $t8 - $t1);
383
384        // Expand key.
385        $pitable = self::$pitable;
386        for ($i = $t; $i < 128; $i++) {
387            $l[$i] = $pitable[$l[$i - 1] + $l[$i - $t]];
388        }
389        $i = 128 - $t8;
390        $l[$i] = $pitable[$l[$i] & $tm];
391        while ($i--) {
392            $l[$i] = $pitable[$l[$i + 1] ^ $l[$i + $t8]];
393        }
394
395        // Prepare the key for mcrypt.
396        $l[0] = self::$invpitable[$l[0]];
397        array_unshift($l, 'C*');
398
399        $this->key = pack(...$l);
400        $this->key_length = strlen($this->key);
401        $this->changed = $this->nonIVChanged = true;
402        $this->setEngine();
403    }
404
405    /**
406     * Encrypts a message.
407     *
408     * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::encrypt, with some additional OpenSSL handling code
409     *
410     * @see self::decrypt()
411     * @access public
412     * @param string $plaintext
413     * @return string $ciphertext
414     */
415    public function encrypt($plaintext)
416    {
417        if ($this->engine == self::ENGINE_OPENSSL) {
418            $temp = $this->key;
419            $this->key = $this->orig_key;
420            $result = parent::encrypt($plaintext);
421            $this->key = $temp;
422            return $result;
423        }
424
425        return parent::encrypt($plaintext);
426    }
427
428    /**
429     * Decrypts a message.
430     *
431     * Mostly a wrapper for \phpseclib3\Crypt\Common\SymmetricKey::decrypt, with some additional OpenSSL handling code
432     *
433     * @see self::encrypt()
434     * @access public
435     * @param string $ciphertext
436     * @return string $plaintext
437     */
438    public function decrypt($ciphertext)
439    {
440        if ($this->engine == self::ENGINE_OPENSSL) {
441            $temp = $this->key;
442            $this->key = $this->orig_key;
443            $result = parent::decrypt($ciphertext);
444            $this->key = $temp;
445            return $result;
446        }
447
448        return parent::decrypt($ciphertext);
449    }
450
451    /**
452     * Encrypts a block
453     *
454     * @see \phpseclib3\Crypt\Common\SymmetricKey::encryptBlock()
455     * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt()
456     * @access private
457     * @param string $in
458     * @return string
459     */
460    protected function encryptBlock($in)
461    {
462        list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
463        $keys = $this->keys;
464        $limit = 20;
465        $actions = [$limit => 44, 44 => 64];
466        $j = 0;
467
468        for (;;) {
469            // Mixing round.
470            $r0 = (($r0 + $keys[$j++] + ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
471            $r0 |= $r0 >> 16;
472            $r1 = (($r1 + $keys[$j++] + ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
473            $r1 |= $r1 >> 16;
474            $r2 = (($r2 + $keys[$j++] + ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
475            $r2 |= $r2 >> 16;
476            $r3 = (($r3 + $keys[$j++] + ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
477            $r3 |= $r3 >> 16;
478
479            if ($j === $limit) {
480                if ($limit === 64) {
481                    break;
482                }
483
484                // Mashing round.
485                $r0 += $keys[$r3 & 0x3F];
486                $r1 += $keys[$r0 & 0x3F];
487                $r2 += $keys[$r1 & 0x3F];
488                $r3 += $keys[$r2 & 0x3F];
489                $limit = $actions[$limit];
490            }
491        }
492
493        return pack('vvvv', $r0, $r1, $r2, $r3);
494    }
495
496    /**
497     * Decrypts a block
498     *
499     * @see \phpseclib3\Crypt\Common\SymmetricKey::decryptBlock()
500     * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt()
501     * @access private
502     * @param string $in
503     * @return string
504     */
505    protected function decryptBlock($in)
506    {
507        list($r0, $r1, $r2, $r3) = array_values(unpack('v*', $in));
508        $keys = $this->keys;
509        $limit = 44;
510        $actions = [$limit => 20, 20 => 0];
511        $j = 64;
512
513        for (;;) {
514            // R-mixing round.
515            $r3 = ($r3 | ($r3 << 16)) >> 5;
516            $r3 = ($r3 - $keys[--$j] - ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
517            $r2 = ($r2 | ($r2 << 16)) >> 3;
518            $r2 = ($r2 - $keys[--$j] - ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
519            $r1 = ($r1 | ($r1 << 16)) >> 2;
520            $r1 = ($r1 - $keys[--$j] - ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
521            $r0 = ($r0 | ($r0 << 16)) >> 1;
522            $r0 = ($r0 - $keys[--$j] - ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;
523
524            if ($j === $limit) {
525                if ($limit === 0) {
526                    break;
527                }
528
529                // R-mashing round.
530                $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
531                $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
532                $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
533                $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;
534                $limit = $actions[$limit];
535            }
536        }
537
538        return pack('vvvv', $r0, $r1, $r2, $r3);
539    }
540
541    /**
542     * Creates the key schedule
543     *
544     * @see \phpseclib3\Crypt\Common\SymmetricKey::setupKey()
545     * @access private
546     */
547    protected function setupKey()
548    {
549        if (!isset($this->key)) {
550            $this->setKey('');
551        }
552
553        // Key has already been expanded in \phpseclib3\Crypt\RC2::setKey():
554        // Only the first value must be altered.
555        $l = unpack('Ca/Cb/v*', $this->key);
556        array_unshift($l, self::$pitable[$l['a']] | ($l['b'] << 8));
557        unset($l['a']);
558        unset($l['b']);
559        $this->keys = $l;
560    }
561
562    /**
563     * Setup the performance-optimized function for de/encrypt()
564     *
565     * @see \phpseclib3\Crypt\Common\SymmetricKey::setupInlineCrypt()
566     * @access private
567     */
568    protected function setupInlineCrypt()
569    {
570        // Init code for both, encrypt and decrypt.
571        $init_crypt = '$keys = $this->keys;';
572
573        $keys = $this->keys;
574
575        // $in is the current 8 bytes block which has to be en/decrypt
576        $encrypt_block = $decrypt_block = '
577            $in = unpack("v4", $in);
578            $r0 = $in[1];
579            $r1 = $in[2];
580            $r2 = $in[3];
581            $r3 = $in[4];
582        ';
583
584        // Create code for encryption.
585        $limit = 20;
586        $actions = [$limit => 44, 44 => 64];
587        $j = 0;
588
589        for (;;) {
590            // Mixing round.
591            $encrypt_block .= '
592                $r0 = (($r0 + ' . $keys[$j++] . ' +
593                       ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF) << 1;
594                $r0 |= $r0 >> 16;
595                $r1 = (($r1 + ' . $keys[$j++] . ' +
596                       ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF) << 2;
597                $r1 |= $r1 >> 16;
598                $r2 = (($r2 + ' . $keys[$j++] . ' +
599                       ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF) << 3;
600                $r2 |= $r2 >> 16;
601                $r3 = (($r3 + ' . $keys[$j++] . ' +
602                       ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF) << 5;
603                $r3 |= $r3 >> 16;';
604
605            if ($j === $limit) {
606                if ($limit === 64) {
607                    break;
608                }
609
610                // Mashing round.
611                $encrypt_block .= '
612                    $r0 += $keys[$r3 & 0x3F];
613                    $r1 += $keys[$r0 & 0x3F];
614                    $r2 += $keys[$r1 & 0x3F];
615                    $r3 += $keys[$r2 & 0x3F];';
616                $limit = $actions[$limit];
617            }
618        }
619
620        $encrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
621
622        // Create code for decryption.
623        $limit = 44;
624        $actions = [$limit => 20, 20 => 0];
625        $j = 64;
626
627        for (;;) {
628            // R-mixing round.
629            $decrypt_block .= '
630                $r3 = ($r3 | ($r3 << 16)) >> 5;
631                $r3 = ($r3 - ' . $keys[--$j] . ' -
632                       ((($r0 ^ $r1) & $r2) ^ $r0)) & 0xFFFF;
633                $r2 = ($r2 | ($r2 << 16)) >> 3;
634                $r2 = ($r2 - ' . $keys[--$j] . ' -
635                       ((($r3 ^ $r0) & $r1) ^ $r3)) & 0xFFFF;
636                $r1 = ($r1 | ($r1 << 16)) >> 2;
637                $r1 = ($r1 - ' . $keys[--$j] . ' -
638                       ((($r2 ^ $r3) & $r0) ^ $r2)) & 0xFFFF;
639                $r0 = ($r0 | ($r0 << 16)) >> 1;
640                $r0 = ($r0 - ' . $keys[--$j] . ' -
641                       ((($r1 ^ $r2) & $r3) ^ $r1)) & 0xFFFF;';
642
643            if ($j === $limit) {
644                if ($limit === 0) {
645                    break;
646                }
647
648                // R-mashing round.
649                $decrypt_block .= '
650                    $r3 = ($r3 - $keys[$r2 & 0x3F]) & 0xFFFF;
651                    $r2 = ($r2 - $keys[$r1 & 0x3F]) & 0xFFFF;
652                    $r1 = ($r1 - $keys[$r0 & 0x3F]) & 0xFFFF;
653                    $r0 = ($r0 - $keys[$r3 & 0x3F]) & 0xFFFF;';
654                $limit = $actions[$limit];
655            }
656        }
657
658        $decrypt_block .= '$in = pack("v4", $r0, $r1, $r2, $r3);';
659
660        // Creates the inline-crypt function
661        $this->inline_crypt = $this->createInlineCryptFunction(
662            [
663               'init_crypt'    => $init_crypt,
664               'encrypt_block' => $encrypt_block,
665               'decrypt_block' => $decrypt_block
666            ]
667        );
668    }
669}
670