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