* setKey('abcdefgh'); * * $size = 10 * 1024; * $plaintext = ''; * for ($i = 0; $i < $size; $i++) { * $plaintext.= 'a'; * } * * echo $rc4->decrypt($rc4->encrypt($plaintext)); * ?> * * * @author Jim Wigginton * @copyright 2007 Jim Wigginton * @license http://www.opensource.org/licenses/mit-license.html MIT License * @link http://phpseclib.sourceforge.net */ declare(strict_types=1); namespace phpseclib3\Crypt; use phpseclib3\Crypt\Common\StreamCipher; use phpseclib3\Exception\LengthException; /** * Pure-PHP implementation of RC4. * * @author Jim Wigginton */ class RC4 extends StreamCipher { /** * @see \phpseclib3\Crypt\RC4::_crypt() */ public const ENCRYPT = 0; /** * @see \phpseclib3\Crypt\RC4::_crypt() */ public const DECRYPT = 1; /** * Key Length (in bytes) * * @see \phpseclib3\Crypt\RC4::setKeyLength() * @var int */ protected $key_length = 128; // = 1024 bits /** * The Key * * @see self::setKey() * @var string */ protected $key; /** * The Key Stream for decryption and encryption * * @see self::setKey() * @var array */ private $stream; /** * Test for engine validity * * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine() * * @see \phpseclib3\Crypt\Common\SymmetricKey::__construct() */ protected function isValidEngineHelper(int $engine): bool { if ($engine == self::ENGINE_OPENSSL) { if ($this->continuousBuffer) { return false; } if (version_compare(PHP_VERSION, '5.3.7') >= 0) { $this->cipher_name_openssl = 'rc4-40'; } else { switch (strlen($this->key)) { case 5: $this->cipher_name_openssl = 'rc4-40'; break; case 8: $this->cipher_name_openssl = 'rc4-64'; break; case 16: $this->cipher_name_openssl = 'rc4'; break; default: return false; } } } return parent::isValidEngineHelper($engine); } /** * Sets the key length * * Keys can be between 1 and 256 bytes long. * * @throws LengthException if the key length is invalid */ public function setKeyLength(int $length): void { if ($length < 8 || $length > 2048) { throw new LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys between 1 and 256 bytes are supported'); } $this->key_length = $length >> 3; parent::setKeyLength($length); } /** * Sets the key length * * Keys can be between 1 and 256 bytes long. */ public function setKey(string $key): void { $length = strlen($key); if ($length < 1 || $length > 256) { throw new LengthException('Key size of ' . $length . ' bytes is not supported by RC4. Keys must be between 1 and 256 bytes long'); } parent::setKey($key); } /** * Encrypts a message. * * @return string $ciphertext * @see \phpseclib3\Crypt\Common\SymmetricKey::decrypt() * @see self::crypt() */ public function encrypt(string $plaintext): string { if ($this->engine != self::ENGINE_INTERNAL) { return parent::encrypt($plaintext); } return $this->crypt($plaintext, self::ENCRYPT); } /** * Decrypts a message. * * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)). * At least if the continuous buffer is disabled. * * @return string $plaintext * @see \phpseclib3\Crypt\Common\SymmetricKey::encrypt() * @see self::crypt() */ public function decrypt(string $ciphertext): string { if ($this->engine != self::ENGINE_INTERNAL) { return parent::decrypt($ciphertext); } return $this->crypt($ciphertext, self::DECRYPT); } /** * Encrypts a block */ protected function encryptBlock(string $in): string { // RC4 does not utilize this method return ''; } /** * Decrypts a block */ protected function decryptBlock(string $in): string { // RC4 does not utilize this method return ''; } /** * Setup the key (expansion) * * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey() */ protected function setupKey(): void { $key = $this->key; $keyLength = strlen($key); $keyStream = range(0, 255); $j = 0; for ($i = 0; $i < 256; $i++) { $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255; $temp = $keyStream[$i]; $keyStream[$i] = $keyStream[$j]; $keyStream[$j] = $temp; } $this->stream = []; $this->stream[self::DECRYPT] = $this->stream[self::ENCRYPT] = [ 0, // index $i 0, // index $j $keyStream, ]; } /** * Encrypts or decrypts a message. * * @return string $text * @see self::decrypt() * @see self::encrypt() */ private function crypt(string $text, int $mode): string { if ($this->changed) { $this->setup(); } $stream = &$this->stream[$mode]; if ($this->continuousBuffer) { $i = &$stream[0]; $j = &$stream[1]; $keyStream = &$stream[2]; } else { $i = $stream[0]; $j = $stream[1]; $keyStream = $stream[2]; } $len = strlen($text); for ($k = 0; $k < $len; ++$k) { $i = ($i + 1) & 255; $ksi = $keyStream[$i]; $j = ($j + $ksi) & 255; $ksj = $keyStream[$j]; $keyStream[$i] = $ksj; $keyStream[$j] = $ksi; $text[$k] = $text[$k] ^ chr($keyStream[($ksj + $ksi) & 255]); } return $text; } }