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