2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
5 * Pure-PHP implementation of RC4.
7 * Uses mcrypt, if available, and an internal implementation, otherwise.
11 * Useful resources are as follows:
13 * - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
14 * - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
16 * RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
17 * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
19 * Here's a short example of how to use this library:
22 * include('Crypt/RC4.php');
24 * $rc4 = new Crypt_RC4();
26 * $rc4->setKey('abcdefgh');
30 * for ($i = 0; $i < $size; $i++) {
34 * echo $rc4->decrypt($rc4->encrypt($plaintext));
38 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
39 * of this software and associated documentation files (the "Software"), to deal
40 * in the Software without restriction, including without limitation the rights
41 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
42 * copies of the Software, and to permit persons to whom the Software is
43 * furnished to do so, subject to the following conditions:
45 * The above copyright notice and this permission notice shall be included in
46 * all copies or substantial portions of the Software.
48 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
49 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
50 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
51 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
52 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
53 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
58 * @author Jim Wigginton <terrafrost@php.net>
59 * @copyright MMVII Jim Wigginton
60 * @license http://www.opensource.org/licenses/mit-license.html MIT License
61 * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
62 * @link http://phpseclib.sourceforge.net
67 * @see Crypt_RC4::Crypt_RC4()
70 * Toggles the internal implementation
72 define('CRYPT_RC4_MODE_INTERNAL', 1);
74 * Toggles the mcrypt implementation
76 define('CRYPT_RC4_MODE_MCRYPT', 2);
81 * @see Crypt_RC4::_crypt()
83 define('CRYPT_RC4_ENCRYPT', 0);
84 define('CRYPT_RC4_DECRYPT', 1);
88 * Pure-PHP implementation of RC4.
90 * @author Jim Wigginton <terrafrost@php.net>
99 * @see Crypt_RC4::setKey()
106 * The Key Stream for encryption
108 * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
110 * @see Crypt_RC4::setKey()
114 var $encryptStream = false;
117 * The Key Stream for decryption
119 * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
121 * @see Crypt_RC4::setKey()
125 var $decryptStream = false;
128 * The $i and $j indexes for encryption
130 * @see Crypt_RC4::_crypt()
134 var $encryptIndex = 0;
137 * The $i and $j indexes for decryption
139 * @see Crypt_RC4::_crypt()
143 var $decryptIndex = 0;
146 * The Encryption Algorithm
148 * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
150 * @see Crypt_RC4::Crypt_RC4()
157 * Continuous Buffer status
159 * @see Crypt_RC4::enableContinuousBuffer()
163 var $continuousBuffer = false;
166 * Default Constructor.
168 * Determines whether or not the mcrypt extension should be used.
170 * @param optional Integer $mode
176 if ( !defined('CRYPT_RC4_MODE') ) {
178 case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') ||
defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()):
179 define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT
);
182 define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL
);
186 switch ( CRYPT_RC4_MODE
) {
187 case CRYPT_RC4_MODE_MCRYPT
:
189 case defined('MCRYPT_ARCFOUR'):
190 $this->mode
= MCRYPT_ARCFOUR
;
192 case defined('MCRYPT_RC4');
193 $this->mode
= MCRYPT_RC4
;
201 * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
202 * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
207 function setKey($key)
211 if ( CRYPT_RC4_MODE
== CRYPT_RC4_MODE_MCRYPT
) {
215 $keyLength = strlen($key);
216 $keyStream = array();
217 for ($i = 0; $i < 256; $i++
) {
221 for ($i = 0; $i < 256; $i++
) {
222 $j = ($j +
$keyStream[$i] +
ord($key[$i %
$keyLength])) & 255;
223 $temp = $keyStream[$i];
224 $keyStream[$i] = $keyStream[$j];
225 $keyStream[$j] = $temp;
228 $this->encryptIndex
= $this->decryptIndex
= array(0, 0);
229 $this->encryptStream
= $this->decryptStream
= $keyStream;
235 * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
236 * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
237 * $hash, $salt, $count, $dkLen
239 * @param String $password
240 * @param optional String $method
243 function setPassword($password, $method = 'pbkdf2')
249 list(, , $hash, $salt, $count) = func_get_args();
253 // WPA and WPA use the SSID as the salt
255 $salt = 'phpseclib/salt';
257 // RFC2898#section-4.2 uses 1,000 iterations by default
258 // WPA and WPA2 use 4,096.
259 if (!isset($count)) {
262 if (!isset($dkLen)) {
266 if (!class_exists('Crypt_Hash')) {
267 require_once('Crypt/Hash.php');
271 while (strlen($key) < $dkLen) {
272 //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
273 $hmac = new Crypt_Hash();
274 $hmac->setHash($hash);
275 $hmac->setKey($password);
276 $f = $u = $hmac->hash($salt . pack('N', $i++
));
277 for ($j = 2; $j <= $count; $j++
) {
278 $u = $hmac->hash($u);
285 $this->setKey(substr($key, 0, $dkLen));
291 * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
292 * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
295 * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
296 * the IV's are relatively easy to predict, an attack described by
297 * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
298 * can be used to quickly guess at the rest of the key. The following links elaborate:
300 * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
301 * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
304 * @see Crypt_RC4::setKey()
312 * Encrypts a message.
314 * @see Crypt_RC4::_crypt()
316 * @param String $plaintext
318 function encrypt($plaintext)
320 return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT
);
324 * Decrypts a message.
326 * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
327 * Atleast if the continuous buffer is disabled.
329 * @see Crypt_RC4::_crypt()
331 * @param String $ciphertext
333 function decrypt($ciphertext)
335 return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT
);
339 * Encrypts or decrypts a message.
341 * @see Crypt_RC4::encrypt()
342 * @see Crypt_RC4::decrypt()
344 * @param String $text
345 * @param Integer $mode
347 function _crypt($text, $mode)
349 if ( CRYPT_RC4_MODE
== CRYPT_RC4_MODE_MCRYPT
) {
350 $keyStream = $mode == CRYPT_RC4_ENCRYPT ?
'encryptStream' : 'decryptStream';
352 if ($this->$keyStream === false) {
353 $this->$keyStream = mcrypt_module_open($this->mode
, '', MCRYPT_MODE_STREAM
, '');
354 mcrypt_generic_init($this->$keyStream, $this->key
, '');
355 } else if (!$this->continuousBuffer
) {
356 mcrypt_generic_init($this->$keyStream, $this->key
, '');
358 $newText = mcrypt_generic($this->$keyStream, $text);
359 if (!$this->continuousBuffer
) {
360 mcrypt_generic_deinit($this->$keyStream);
366 if ($this->encryptStream
=== false) {
367 $this->setKey($this->key
);
371 case CRYPT_RC4_ENCRYPT
:
372 $keyStream = $this->encryptStream
;
373 list($i, $j) = $this->encryptIndex
;
375 case CRYPT_RC4_DECRYPT
:
376 $keyStream = $this->decryptStream
;
377 list($i, $j) = $this->decryptIndex
;
381 for ($k = 0; $k < strlen($text); $k++
) {
383 $j = ($j +
$keyStream[$i]) & 255;
384 $temp = $keyStream[$i];
385 $keyStream[$i] = $keyStream[$j];
386 $keyStream[$j] = $temp;
387 $temp = $keyStream[($keyStream[$i] +
$keyStream[$j]) & 255];
388 $newText.= chr(ord($text[$k]) ^
$temp);
391 if ($this->continuousBuffer
) {
393 case CRYPT_RC4_ENCRYPT
:
394 $this->encryptStream
= $keyStream;
395 $this->encryptIndex
= array($i, $j);
397 case CRYPT_RC4_DECRYPT
:
398 $this->decryptStream
= $keyStream;
399 $this->decryptIndex
= array($i, $j);
407 * Treat consecutive "packets" as if they are a continuous buffer.
409 * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
410 * will yield different outputs:
413 * echo $rc4->encrypt(substr($plaintext, 0, 8));
414 * echo $rc4->encrypt(substr($plaintext, 8, 8));
417 * echo $rc4->encrypt($plaintext);
420 * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
421 * another, as demonstrated with the following:
424 * $rc4->encrypt(substr($plaintext, 0, 8));
425 * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
428 * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
431 * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
432 * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
433 * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
435 * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
436 * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
437 * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
438 * however, they are also less intuitive and more likely to cause you problems.
440 * @see Crypt_RC4::disableContinuousBuffer()
443 function enableContinuousBuffer()
445 $this->continuousBuffer
= true;
449 * Treat consecutive packets as if they are a discontinuous buffer.
451 * The default behavior.
453 * @see Crypt_RC4::enableContinuousBuffer()
456 function disableContinuousBuffer()
458 if ( CRYPT_RC4_MODE
== CRYPT_RC4_MODE_INTERNAL
) {
459 $this->encryptIndex
= $this->decryptIndex
= array(0, 0);
460 $this->setKey($this->key
);
463 $this->continuousBuffer
= false;
469 * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
470 * included is so that you can switch between a block cipher and a stream cipher transparently.
472 * @see Crypt_RC4::disablePadding()
475 function enablePadding()
482 * @see Crypt_RC4::enablePadding()
485 function disablePadding()
492 * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
493 * needs to be called if mcrypt is being used.
497 function __destruct()
499 if ( CRYPT_RC4_MODE
== CRYPT_RC4_MODE_MCRYPT
) {
500 $this->_closeMCrypt();
505 * Properly close the MCrypt objects.
509 function _closeMCrypt()
511 if ( $this->encryptStream
!== false ) {
512 if ( $this->continuousBuffer
) {
513 mcrypt_generic_deinit($this->encryptStream
);
516 mcrypt_module_close($this->encryptStream
);
518 $this->encryptStream
= false;
521 if ( $this->decryptStream
!== false ) {
522 if ( $this->continuousBuffer
) {
523 mcrypt_generic_deinit($this->decryptStream
);
526 mcrypt_module_close($this->decryptStream
);
528 $this->decryptStream
= false;