PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/phpseclib/Crypt/RC4.php

https://github.com/kea/phpseclib
PHP | 528 lines | 195 code | 43 blank | 290 comment | 34 complexity | 306a38657a473efe2d34cac0c463abc4 MD5 | raw file
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Pure-PHP implementation of RC4.
  5. *
  6. * Uses mcrypt, if available, and an internal implementation, otherwise.
  7. *
  8. * PHP versions 4 and 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 refered to in the SSH1 specification.
  17. *
  18. * Here's a short example of how to use this library:
  19. * <code>
  20. * <?php
  21. * include('Crypt/RC4.php');
  22. *
  23. * $rc4 = new 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. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  38. * of this software and associated documentation files (the "Software"), to deal
  39. * in the Software without restriction, including without limitation the rights
  40. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  41. * copies of the Software, and to permit persons to whom the Software is
  42. * furnished to do so, subject to the following conditions:
  43. *
  44. * The above copyright notice and this permission notice shall be included in
  45. * all copies or substantial portions of the Software.
  46. *
  47. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  48. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  49. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  50. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  51. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  52. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  53. * THE SOFTWARE.
  54. *
  55. * @category Crypt
  56. * @package Crypt_RC4
  57. * @author Jim Wigginton <terrafrost@php.net>
  58. * @copyright MMVII Jim Wigginton
  59. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  60. * @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
  61. * @link http://phpseclib.sourceforge.net
  62. */
  63. namespace phpseclib;
  64. /**
  65. * Pure-PHP implementation of RC4.
  66. *
  67. * @author Jim Wigginton <terrafrost@php.net>
  68. * @version 0.1.0
  69. * @access public
  70. * @package Crypt_RC4
  71. */
  72. class Crypt_RC4 {
  73. /**#@+
  74. * @access private
  75. * @see Crypt_RC4::Crypt_RC4()
  76. */
  77. /**
  78. * Toggles the internal implementation
  79. */
  80. const MODE_INTERNAL = 1;
  81. /**
  82. * Toggles the mcrypt implementation
  83. */
  84. const MODE_MCRYPT = 2;
  85. /**#@-*/
  86. /**#@+
  87. * @access private
  88. * @see Crypt_RC4::_crypt()
  89. */
  90. const ENCRYPT = 0;
  91. const DECRYPT = 1;
  92. /**#@-*/
  93. /**
  94. * The Key
  95. *
  96. * @see Crypt_RC4::setKey()
  97. * @var String
  98. * @access private
  99. */
  100. var $key = "\0";
  101. /**
  102. * The Key Stream for encryption
  103. *
  104. * If CRYPT_RC4_MODE == self::MODE_MCRYPT, this will be equal to the mcrypt object
  105. *
  106. * @see Crypt_RC4::setKey()
  107. * @var Array
  108. * @access private
  109. */
  110. var $encryptStream = false;
  111. /**
  112. * The Key Stream for decryption
  113. *
  114. * If CRYPT_RC4_MODE == self::MODE_MCRYPT, this will be equal to the mcrypt object
  115. *
  116. * @see Crypt_RC4::setKey()
  117. * @var Array
  118. * @access private
  119. */
  120. var $decryptStream = false;
  121. /**
  122. * The $i and $j indexes for encryption
  123. *
  124. * @see Crypt_RC4::_crypt()
  125. * @var Integer
  126. * @access private
  127. */
  128. var $encryptIndex = 0;
  129. /**
  130. * The $i and $j indexes for decryption
  131. *
  132. * @see Crypt_RC4::_crypt()
  133. * @var Integer
  134. * @access private
  135. */
  136. var $decryptIndex = 0;
  137. /**
  138. * The Encryption Algorithm
  139. *
  140. * Only used if CRYPT_RC4_MODE == self::MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
  141. *
  142. * @see Crypt_RC4::Crypt_RC4()
  143. * @var Integer
  144. * @access private
  145. */
  146. var $mode;
  147. /**
  148. * Continuous Buffer status
  149. *
  150. * @see Crypt_RC4::enableContinuousBuffer()
  151. * @var Boolean
  152. * @access private
  153. */
  154. var $continuousBuffer = false;
  155. /**
  156. * Default Constructor.
  157. *
  158. * Determines whether or not the mcrypt extension should be used.
  159. *
  160. * @param optional Integer $mode
  161. * @return Crypt_RC4
  162. * @access public
  163. */
  164. function __construct()
  165. {
  166. if ( !defined('CRYPT_RC4_MODE') ) {
  167. switch (true) {
  168. case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()):
  169. define('CRYPT_RC4_MODE', self::MODE_MCRYPT);
  170. break;
  171. default:
  172. define('CRYPT_RC4_MODE', self::MODE_INTERNAL);
  173. }
  174. }
  175. switch ( CRYPT_RC4_MODE ) {
  176. case self::MODE_MCRYPT:
  177. switch (true) {
  178. case defined('MCRYPT_ARCFOUR'):
  179. $this->mode = MCRYPT_ARCFOUR;
  180. break;
  181. case defined('MCRYPT_RC4');
  182. $this->mode = MCRYPT_RC4;
  183. }
  184. }
  185. }
  186. /**
  187. * Sets the key.
  188. *
  189. * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
  190. * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
  191. *
  192. * @access public
  193. * @param String $key
  194. */
  195. function setKey($key)
  196. {
  197. $this->key = $key;
  198. if ( CRYPT_RC4_MODE == self::MODE_MCRYPT ) {
  199. return;
  200. }
  201. $keyLength = strlen($key);
  202. $keyStream = array();
  203. for ($i = 0; $i < 256; $i++) {
  204. $keyStream[$i] = $i;
  205. }
  206. $j = 0;
  207. for ($i = 0; $i < 256; $i++) {
  208. $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
  209. $temp = $keyStream[$i];
  210. $keyStream[$i] = $keyStream[$j];
  211. $keyStream[$j] = $temp;
  212. }
  213. $this->encryptIndex = $this->decryptIndex = array(0, 0);
  214. $this->encryptStream = $this->decryptStream = $keyStream;
  215. }
  216. /**
  217. * Sets the password.
  218. *
  219. * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
  220. * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
  221. * $hash, $salt, $count, $dkLen
  222. *
  223. * @param String $password
  224. * @param optional String $method
  225. * @access public
  226. */
  227. function setPassword($password, $method = 'pbkdf2')
  228. {
  229. $key = '';
  230. switch ($method) {
  231. default: // 'pbkdf2'
  232. //not the prettiest thing ever, but solves the undefined index issue with list()
  233. $args = func_get_args();
  234. $hash = 'sha1';
  235. $salt = 'phpseclib/salt';
  236. $count = 1000;
  237. switch(count($args)){
  238. case 6:
  239. case 5:
  240. $count = $args[4];
  241. case 4:
  242. $salt = $args[3];
  243. case 3:
  244. $hash = $args[2];
  245. }
  246. //why is this hard-coded?
  247. $dkLen = 128;
  248. $i = 1;
  249. while (strlen($key) < $dkLen) {
  250. //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
  251. $hmac = new Crypt_Hash();
  252. $hmac->setHash($hash);
  253. $hmac->setKey($password);
  254. $f = $u = $hmac->hash($salt . pack('N', $i++));
  255. for ($j = 2; $j <= $count; $j++) {
  256. $u = $hmac->hash($u);
  257. $f^= $u;
  258. }
  259. $key.= $f;
  260. }
  261. }
  262. $this->setKey(substr($key, 0, $dkLen));
  263. }
  264. /**
  265. * Dummy function.
  266. *
  267. * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
  268. * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
  269. * calling setKey().
  270. *
  271. * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
  272. * the IV's are relatively easy to predict, an attack described by
  273. * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
  274. * can be used to quickly guess at the rest of the key. The following links elaborate:
  275. *
  276. * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
  277. * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
  278. *
  279. * @param String $iv
  280. * @see Crypt_RC4::setKey()
  281. * @access public
  282. */
  283. function setIV($iv)
  284. {
  285. }
  286. /**
  287. * Encrypts a message.
  288. *
  289. * @see Crypt_RC4::_crypt()
  290. * @access public
  291. * @param String $plaintext
  292. */
  293. function encrypt($plaintext)
  294. {
  295. return $this->_crypt($plaintext, self::ENCRYPT);
  296. }
  297. /**
  298. * Decrypts a message.
  299. *
  300. * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
  301. * Atleast if the continuous buffer is disabled.
  302. *
  303. * @see Crypt_RC4::_crypt()
  304. * @access public
  305. * @param String $ciphertext
  306. */
  307. function decrypt($ciphertext)
  308. {
  309. return $this->_crypt($ciphertext, self::DECRYPT);
  310. }
  311. /**
  312. * Encrypts or decrypts a message.
  313. *
  314. * @see Crypt_RC4::encrypt()
  315. * @see Crypt_RC4::decrypt()
  316. * @access private
  317. * @param String $text
  318. * @param Integer $mode
  319. */
  320. function _crypt($text, $mode)
  321. {
  322. if ( CRYPT_RC4_MODE == self::MODE_MCRYPT ) {
  323. $keyStream = $mode == self::ENCRYPT ? 'encryptStream' : 'decryptStream';
  324. if ($this->$keyStream === false) {
  325. $this->$keyStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
  326. mcrypt_generic_init($this->$keyStream, $this->key, '');
  327. } else if (!$this->continuousBuffer) {
  328. mcrypt_generic_init($this->$keyStream, $this->key, '');
  329. }
  330. $newText = mcrypt_generic($this->$keyStream, $text);
  331. if (!$this->continuousBuffer) {
  332. mcrypt_generic_deinit($this->$keyStream);
  333. }
  334. return $newText;
  335. }
  336. if ($this->encryptStream === false) {
  337. $this->setKey($this->key);
  338. }
  339. switch ($mode) {
  340. case self::ENCRYPT:
  341. $keyStream = $this->encryptStream;
  342. list($i, $j) = $this->encryptIndex;
  343. break;
  344. case self::DECRYPT:
  345. $keyStream = $this->decryptStream;
  346. list($i, $j) = $this->decryptIndex;
  347. }
  348. $newText = '';
  349. for ($k = 0; $k < strlen($text); $k++) {
  350. $i = ($i + 1) & 255;
  351. $j = ($j + $keyStream[$i]) & 255;
  352. $temp = $keyStream[$i];
  353. $keyStream[$i] = $keyStream[$j];
  354. $keyStream[$j] = $temp;
  355. $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
  356. $newText.= chr(ord($text[$k]) ^ $temp);
  357. }
  358. if ($this->continuousBuffer) {
  359. switch ($mode) {
  360. case self::ENCRYPT:
  361. $this->encryptStream = $keyStream;
  362. $this->encryptIndex = array($i, $j);
  363. break;
  364. case self::DECRYPT:
  365. $this->decryptStream = $keyStream;
  366. $this->decryptIndex = array($i, $j);
  367. }
  368. }
  369. return $newText;
  370. }
  371. /**
  372. * Treat consecutive "packets" as if they are a continuous buffer.
  373. *
  374. * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
  375. * will yield different outputs:
  376. *
  377. * <code>
  378. * echo $rc4->encrypt(substr($plaintext, 0, 8));
  379. * echo $rc4->encrypt(substr($plaintext, 8, 8));
  380. * </code>
  381. * <code>
  382. * echo $rc4->encrypt($plaintext);
  383. * </code>
  384. *
  385. * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
  386. * another, as demonstrated with the following:
  387. *
  388. * <code>
  389. * $rc4->encrypt(substr($plaintext, 0, 8));
  390. * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
  391. * </code>
  392. * <code>
  393. * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
  394. * </code>
  395. *
  396. * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
  397. * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
  398. * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
  399. *
  400. * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
  401. * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
  402. * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
  403. * however, they are also less intuitive and more likely to cause you problems.
  404. *
  405. * @see Crypt_RC4::disableContinuousBuffer()
  406. * @access public
  407. */
  408. function enableContinuousBuffer()
  409. {
  410. $this->continuousBuffer = true;
  411. }
  412. /**
  413. * Treat consecutive packets as if they are a discontinuous buffer.
  414. *
  415. * The default behavior.
  416. *
  417. * @see Crypt_RC4::enableContinuousBuffer()
  418. * @access public
  419. */
  420. function disableContinuousBuffer()
  421. {
  422. if ( CRYPT_RC4_MODE == self::MODE_INTERNAL ) {
  423. $this->encryptIndex = $this->decryptIndex = array(0, 0);
  424. $this->setKey($this->key);
  425. }
  426. $this->continuousBuffer = false;
  427. }
  428. /**
  429. * Dummy function.
  430. *
  431. * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
  432. * included is so that you can switch between a block cipher and a stream cipher transparently.
  433. *
  434. * @see Crypt_RC4::disablePadding()
  435. * @access public
  436. */
  437. function enablePadding()
  438. {
  439. }
  440. /**
  441. * Dummy function.
  442. *
  443. * @see Crypt_RC4::enablePadding()
  444. * @access public
  445. */
  446. function disablePadding()
  447. {
  448. }
  449. /**
  450. * Class destructor.
  451. *
  452. * Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
  453. * needs to be called if mcrypt is being used.
  454. *
  455. * @access public
  456. */
  457. function __destruct()
  458. {
  459. if ( CRYPT_RC4_MODE == self::MODE_MCRYPT ) {
  460. $this->_closeMCrypt();
  461. }
  462. }
  463. /**
  464. * Properly close the MCrypt objects.
  465. *
  466. * @access prviate
  467. */
  468. function _closeMCrypt()
  469. {
  470. if ( $this->encryptStream !== false ) {
  471. if ( $this->continuousBuffer ) {
  472. mcrypt_generic_deinit($this->encryptStream);
  473. }
  474. mcrypt_module_close($this->encryptStream);
  475. $this->encryptStream = false;
  476. }
  477. if ( $this->decryptStream !== false ) {
  478. if ( $this->continuousBuffer ) {
  479. mcrypt_generic_deinit($this->decryptStream);
  480. }
  481. mcrypt_module_close($this->decryptStream);
  482. $this->decryptStream = false;
  483. }
  484. }
  485. }