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 \phpseclib3\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 phpseclib3\Crypt; 46 47use phpseclib3\Crypt\Common\StreamCipher; 48 49/** 50 * Pure-PHP implementation of RC4. 51 * 52 * @package RC4 53 * @author Jim Wigginton <terrafrost@php.net> 54 * @access public 55 */ 56class RC4 extends StreamCipher 57{ 58 /** 59 * @access private 60 * @see \phpseclib3\Crypt\RC4::_crypt() 61 */ 62 const ENCRYPT = 0; 63 64 /** 65 * @access private 66 * @see \phpseclib3\Crypt\RC4::_crypt() 67 */ 68 const DECRYPT = 1; 69 70 /** 71 * Key Length (in bytes) 72 * 73 * @see \phpseclib3\Crypt\RC4::setKeyLength() 74 * @var int 75 * @access private 76 */ 77 protected $key_length = 128; // = 1024 bits 78 79 /** 80 * The mcrypt specific name of the cipher 81 * 82 * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt 83 * @var string 84 * @access private 85 */ 86 protected $cipher_name_mcrypt = 'arcfour'; 87 88 /** 89 * The Key 90 * 91 * @see self::setKey() 92 * @var string 93 * @access private 94 */ 95 protected $key; 96 97 /** 98 * The Key Stream for decryption and encryption 99 * 100 * @see self::setKey() 101 * @var array 102 * @access private 103 */ 104 private $stream; 105 106 /** 107 * Test for engine validity 108 * 109 * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() 110 * 111 * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() 112 * @param int $engine 113 * @access protected 114 * @return bool 115 */ 116 protected function isValidEngineHelper($engine) 117 { 118 if ($engine == self::ENGINE_OPENSSL) { 119 if ($this->continuousBuffer) { 120 return false; 121 } 122 if (version_compare(PHP_VERSION, '5.3.7') >= 0) { 123 $this->cipher_name_openssl = 'rc4-40'; 124 } else { 125 switch (strlen($this->key)) { 126 case 5: 127 $this->cipher_name_openssl = 'rc4-40'; 128 break; 129 case 8: 130 $this->cipher_name_openssl = 'rc4-64'; 131 break; 132 case 16: 133 $this->cipher_name_openssl = 'rc4'; 134 break; 135 default: 136 return false; 137 } 138 } 139 } 140 141 return parent::isValidEngineHelper($engine); 142 } 143 144 /** 145 * Sets the key length 146 * 147 * Keys can be between 1 and 256 bytes long. 148 * 149 * @access public 150 * @param int $length 151 * @throws \LengthException if the key length is invalid 152 */ 153 public function setKeyLength($length) 154 { 155 if ($length < 8 || $length > 2048) { 156 throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); 157 } 158 159 $this->key_length = $length >> 3; 160 161 parent::setKeyLength($length); 162 } 163 164 /** 165 * Sets the key length 166 * 167 * Keys can be between 1 and 256 bytes long. 168 * 169 * @access public 170 * @param string $key 171 */ 172 public function setKey($key) 173 { 174 $length = strlen($key); 175 if ($length < 1 || $length > 256) { 176 throw new \LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); 177 } 178 179 parent::setKey($key); 180 } 181 182 /** 183 * Encrypts a message. 184 * 185 * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() 186 * @see self::crypt() 187 * @access public 188 * @param string $plaintext 189 * @return string $ciphertext 190 */ 191 public function encrypt($plaintext) 192 { 193 if ($this->engine != self::ENGINE_INTERNAL) { 194 return parent::encrypt($plaintext); 195 } 196 return $this->crypt($plaintext, self::ENCRYPT); 197 } 198 199 /** 200 * Decrypts a message. 201 * 202 * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). 203 * At least if the continuous buffer is disabled. 204 * 205 * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() 206 * @see self::crypt() 207 * @access public 208 * @param string $ciphertext 209 * @return string $plaintext 210 */ 211 public function decrypt($ciphertext) 212 { 213 if ($this->engine != self::ENGINE_INTERNAL) { 214 return parent::decrypt($ciphertext); 215 } 216 return $this->crypt($ciphertext, self::DECRYPT); 217 } 218 219 /** 220 * Encrypts a block 221 * 222 * @access private 223 * @param string $in 224 */ 225 protected function encryptBlock($in) 226 { 227 // RC4 does not utilize this method 228 } 229 230 /** 231 * Decrypts a block 232 * 233 * @access private 234 * @param string $in 235 */ 236 protected function decryptBlock($in) 237 { 238 // RC4 does not utilize this method 239 } 240 241 /** 242 * Setup the key (expansion) 243 * 244 * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() 245 * @access private 246 */ 247 protected function setupKey() 248 { 249 $key = $this->key; 250 $keyLength = strlen($key); 251 $keyStream = range(0, 255); 252 $j = 0; 253 for ($i = 0; $i < 256; $i++) { 254 $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; 255 $temp = $keyStream[$i]; 256 $keyStream[$i] = $keyStream[$j]; 257 $keyStream[$j] = $temp; 258 } 259 260 $this->stream = []; 261 $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [ 262 0, // index $i 263 0, // index $j 264 $keyStream 265 ]; 266 } 267 268 /** 269 * Encrypts or decrypts a message. 270 * 271 * @see self::encrypt() 272 * @see self::decrypt() 273 * @access private 274 * @param string $text 275 * @param int $mode 276 * @return string $text 277 */ 278 private function crypt($text, $mode) 279 { 280 if ($this->changed) { 281 $this->setup(); 282 } 283 284 $stream = &$this->stream[$mode]; 285 if ($this->continuousBuffer) { 286 $i = &$stream[0]; 287 $j = &$stream[1]; 288 $keyStream = &$stream[2]; 289 } else { 290 $i = $stream[0]; 291 $j = $stream[1]; 292 $keyStream = $stream[2]; 293 } 294 295 $len = strlen($text); 296 for ($k = 0; $k < $len; ++$k) { 297 $i = ($i + 1) & 255; 298 $ksi = $keyStream[$i]; 299 $j = ($j + $ksi) & 255; 300 $ksj = $keyStream[$j]; 301 302 $keyStream[$i] = $ksj; 303 $keyStream[$j] = $ksi; 304 $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); 305 } 306 307 return $text; 308 } 309} 310