1<?php
2
3/**
4 * Pure-PHP implementation of RC4.
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://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
13 *  - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
14 *
15 * RC4 is also known as ARCFOUR or ARC4.  The reason is elaborated upon at Wikipedia.  This class is named RC4 and not
16 * ARCFOUR or ARC4 because RC4 is how it is referred to in the SSH1 specification.
17 *
18 * Here's a short example of how to use this library:
19 * <code>
20 * <?php
21 *    include 'vendor/autoload.php';
22 *
23 *    $rc4 = new \phpseclib\Crypt\RC4();
24 *
25 *    $rc4->setKey('abcdefgh');
26 *
27 *    $size = 10 * 1024;
28 *    $plaintext = '';
29 *    for ($i = 0; $i < $size; $i++) {
30 *        $plaintext.= 'a';
31 *    }
32 *
33 *    echo $rc4->decrypt($rc4->encrypt($plaintext));
34 * ?>
35 * </code>
36 *
37 * @category  Crypt
38 * @package   RC4
39 * @author    Jim Wigginton <terrafrost@php.net>
40 * @copyright 2007 Jim Wigginton
41 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
42 * @link      http://phpseclib.sourceforge.net
43 */
44
45namespace phpseclib\Crypt;
46
47/**
48 * Pure-PHP implementation of RC4.
49 *
50 * @package RC4
51 * @author  Jim Wigginton <terrafrost@php.net>
52 * @access  public
53 */
54class RC4 extends Base
55{
56    /**#@+
57     * @access private
58     * @see \phpseclib\Crypt\RC4::_crypt()
59    */
60    const ENCRYPT = 0;
61    const DECRYPT = 1;
62    /**#@-*/
63
64    /**
65     * Block Length of the cipher
66     *
67     * RC4 is a stream cipher
68     * so we the block_size to 0
69     *
70     * @see \phpseclib\Crypt\Base::block_size
71     * @var int
72     * @access private
73     */
74    var $block_size = 0;
75
76    /**
77     * Key Length (in bytes)
78     *
79     * @see \phpseclib\Crypt\RC4::setKeyLength()
80     * @var int
81     * @access private
82     */
83    var $key_length = 128; // = 1024 bits
84
85    /**
86     * The mcrypt specific name of the cipher
87     *
88     * @see \phpseclib\Crypt\Base::cipher_name_mcrypt
89     * @var string
90     * @access private
91     */
92    var $cipher_name_mcrypt = 'arcfour';
93
94    /**
95     * Holds whether performance-optimized $inline_crypt() can/should be used.
96     *
97     * @see \phpseclib\Crypt\Base::inline_crypt
98     * @var mixed
99     * @access private
100     */
101    var $use_inline_crypt = false; // currently not available
102
103    /**
104     * The Key
105     *
106     * @see self::setKey()
107     * @var string
108     * @access private
109     */
110    var $key;
111
112    /**
113     * The Key Stream for decryption and encryption
114     *
115     * @see self::setKey()
116     * @var array
117     * @access private
118     */
119    var $stream;
120
121    /**
122     * Default Constructor.
123     *
124     * Determines whether or not the mcrypt extension should be used.
125     *
126     * @see \phpseclib\Crypt\Base::__construct()
127     * @return \phpseclib\Crypt\RC4
128     * @access public
129     */
130    function __construct()
131    {
132        parent::__construct(Base::MODE_STREAM);
133    }
134
135    /**
136     * Test for engine validity
137     *
138     * This is mainly just a wrapper to set things up for \phpseclib\Crypt\Base::isValidEngine()
139     *
140     * @see \phpseclib\Crypt\Base::__construct()
141     * @param int $engine
142     * @access public
143     * @return bool
144     */
145    function isValidEngine($engine)
146    {
147        if ($engine == Base::ENGINE_OPENSSL) {
148            if (version_compare(PHP_VERSION, '5.3.7') >= 0) {
149                $this->cipher_name_openssl = 'rc4-40';
150            } else {
151                switch (strlen($this->key)) {
152                    case 5:
153                        $this->cipher_name_openssl = 'rc4-40';
154                        break;
155                    case 8:
156                        $this->cipher_name_openssl = 'rc4-64';
157                        break;
158                    case 16:
159                        $this->cipher_name_openssl = 'rc4';
160                        break;
161                    default:
162                        return false;
163                }
164            }
165        }
166
167        return parent::isValidEngine($engine);
168    }
169
170    /**
171     * Dummy function.
172     *
173     * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
174     * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
175     * calling setKey().
176     *
177     * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way.  Since, in that protocol,
178     * the IV's are relatively easy to predict, an attack described by
179     * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
180     * can be used to quickly guess at the rest of the key.  The following links elaborate:
181     *
182     * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
183     * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
184     *
185     * @param string $iv
186     * @see self::setKey()
187     * @access public
188     */
189    function setIV($iv)
190    {
191    }
192
193    /**
194     * Sets the key length
195     *
196     * Keys can be between 1 and 256 bytes long.
197     *
198     * @access public
199     * @param int $length
200     */
201    function setKeyLength($length)
202    {
203        if ($length < 8) {
204            $this->key_length = 1;
205        } elseif ($length > 2048) {
206            $this->key_length = 256;
207        } else {
208            $this->key_length = $length >> 3;
209        }
210
211        parent::setKeyLength($length);
212    }
213
214    /**
215     * Encrypts a message.
216     *
217     * @see \phpseclib\Crypt\Base::decrypt()
218     * @see self::_crypt()
219     * @access public
220     * @param string $plaintext
221     * @return string $ciphertext
222     */
223    function encrypt($plaintext)
224    {
225        if ($this->engine != Base::ENGINE_INTERNAL) {
226            return parent::encrypt($plaintext);
227        }
228        return $this->_crypt($plaintext, self::ENCRYPT);
229    }
230
231    /**
232     * Decrypts a message.
233     *
234     * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
235     * At least if the continuous buffer is disabled.
236     *
237     * @see \phpseclib\Crypt\Base::encrypt()
238     * @see self::_crypt()
239     * @access public
240     * @param string $ciphertext
241     * @return string $plaintext
242     */
243    function decrypt($ciphertext)
244    {
245        if ($this->engine != Base::ENGINE_INTERNAL) {
246            return parent::decrypt($ciphertext);
247        }
248        return $this->_crypt($ciphertext, self::DECRYPT);
249    }
250
251    /**
252     * Encrypts a block
253     *
254     * @access private
255     * @param string $in
256     */
257    function _encryptBlock($in)
258    {
259        // RC4 does not utilize this method
260    }
261
262    /**
263     * Decrypts a block
264     *
265     * @access private
266     * @param string $in
267     */
268    function _decryptBlock($in)
269    {
270        // RC4 does not utilize this method
271    }
272
273    /**
274     * Setup the key (expansion)
275     *
276     * @see \phpseclib\Crypt\Base::_setupKey()
277     * @access private
278     */
279    function _setupKey()
280    {
281        $key = $this->key;
282        $keyLength = strlen($key);
283        $keyStream = range(0, 255);
284        $j = 0;
285        for ($i = 0; $i < 256; $i++) {
286            $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
287            $temp = $keyStream[$i];
288            $keyStream[$i] = $keyStream[$j];
289            $keyStream[$j] = $temp;
290        }
291
292        $this->stream = array();
293        $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = array(
294            0, // index $i
295            0, // index $j
296            $keyStream
297        );
298    }
299
300    /**
301     * Encrypts or decrypts a message.
302     *
303     * @see self::encrypt()
304     * @see self::decrypt()
305     * @access private
306     * @param string $text
307     * @param int $mode
308     * @return string $text
309     */
310    function _crypt($text, $mode)
311    {
312        if ($this->changed) {
313            $this->_setup();
314            $this->changed = false;
315        }
316
317        $stream = &$this->stream[$mode];
318        if ($this->continuousBuffer) {
319            $i = &$stream[0];
320            $j = &$stream[1];
321            $keyStream = &$stream[2];
322        } else {
323            $i = $stream[0];
324            $j = $stream[1];
325            $keyStream = $stream[2];
326        }
327
328        $len = strlen($text);
329        for ($k = 0; $k < $len; ++$k) {
330            $i = ($i + 1) & 255;
331            $ksi = $keyStream[$i];
332            $j = ($j + $ksi) & 255;
333            $ksj = $keyStream[$j];
334
335            $keyStream[$i] = $ksj;
336            $keyStream[$j] = $ksi;
337            $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]);
338        }
339
340        return $text;
341    }
342}
343