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