Subversion Repositories oidplus

Rev

Rev 846 | Rev 1042 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

  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.  
  45. namespace phpseclib3\Crypt;
  46.  
  47. use 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.  */
  56. class 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.