RC4.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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 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('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. * @link http://phpseclib.sourceforge.net
  61. */
  62. /**#@+
  63. * @access private
  64. * @see Crypt_RC4::Crypt_RC4()
  65. */
  66. /**
  67. * Toggles the internal implementation
  68. */
  69. define('CRYPT_RC4_MODE_INTERNAL', 1);
  70. /**
  71. * Toggles the mcrypt implementation
  72. */
  73. define('CRYPT_RC4_MODE_MCRYPT', 2);
  74. /**#@-*/
  75. /**#@+
  76. * @access private
  77. * @see Crypt_RC4::_crypt()
  78. */
  79. define('CRYPT_RC4_ENCRYPT', 0);
  80. define('CRYPT_RC4_DECRYPT', 1);
  81. /**#@-*/
  82. /**
  83. * Pure-PHP implementation of RC4.
  84. *
  85. * @author Jim Wigginton <terrafrost@php.net>
  86. * @version 0.1.0
  87. * @access public
  88. * @package Crypt_RC4
  89. */
  90. class Crypt_RC4 {
  91. /**
  92. * The Key
  93. *
  94. * @see Crypt_RC4::setKey()
  95. * @var String
  96. * @access private
  97. */
  98. var $key = "\0";
  99. /**
  100. * The Key Stream for encryption
  101. *
  102. * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
  103. *
  104. * @see Crypt_RC4::setKey()
  105. * @var Array
  106. * @access private
  107. */
  108. var $encryptStream = false;
  109. /**
  110. * The Key Stream for decryption
  111. *
  112. * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
  113. *
  114. * @see Crypt_RC4::setKey()
  115. * @var Array
  116. * @access private
  117. */
  118. var $decryptStream = false;
  119. /**
  120. * The $i and $j indexes for encryption
  121. *
  122. * @see Crypt_RC4::_crypt()
  123. * @var Integer
  124. * @access private
  125. */
  126. var $encryptIndex = 0;
  127. /**
  128. * The $i and $j indexes for decryption
  129. *
  130. * @see Crypt_RC4::_crypt()
  131. * @var Integer
  132. * @access private
  133. */
  134. var $decryptIndex = 0;
  135. /**
  136. * The Encryption Algorithm
  137. *
  138. * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
  139. *
  140. * @see Crypt_RC4::Crypt_RC4()
  141. * @var Integer
  142. * @access private
  143. */
  144. var $mode;
  145. /**
  146. * Continuous Buffer status
  147. *
  148. * @see Crypt_RC4::enableContinuousBuffer()
  149. * @var Boolean
  150. * @access private
  151. */
  152. var $continuousBuffer = false;
  153. /**
  154. * Default Constructor.
  155. *
  156. * Determines whether or not the mcrypt extension should be used.
  157. *
  158. * @return Crypt_RC4
  159. * @access public
  160. */
  161. function Crypt_RC4()
  162. {
  163. if ( !defined('CRYPT_RC4_MODE') ) {
  164. switch (true) {
  165. case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')) && in_array('arcfour', mcrypt_list_algorithms()):
  166. define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
  167. break;
  168. default:
  169. define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
  170. }
  171. }
  172. switch ( CRYPT_RC4_MODE ) {
  173. case CRYPT_RC4_MODE_MCRYPT:
  174. switch (true) {
  175. case defined('MCRYPT_ARCFOUR'):
  176. $this->mode = MCRYPT_ARCFOUR;
  177. break;
  178. case defined('MCRYPT_RC4');
  179. $this->mode = MCRYPT_RC4;
  180. }
  181. $this->encryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
  182. $this->decryptStream = mcrypt_module_open($this->mode, '', MCRYPT_MODE_STREAM, '');
  183. }
  184. }
  185. /**
  186. * Sets the key.
  187. *
  188. * Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
  189. * be used. If no key is explicitly set, it'll be assumed to be a single null byte.
  190. *
  191. * @access public
  192. * @param String $key
  193. */
  194. function setKey($key)
  195. {
  196. $this->key = $key;
  197. if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
  198. mcrypt_generic_init($this->encryptStream, $this->key, '');
  199. mcrypt_generic_init($this->decryptStream, $this->key, '');
  200. return;
  201. }
  202. $keyLength = strlen($key);
  203. $keyStream = array();
  204. for ($i = 0; $i < 256; $i++) {
  205. $keyStream[$i] = $i;
  206. }
  207. $j = 0;
  208. for ($i = 0; $i < 256; $i++) {
  209. $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
  210. $temp = $keyStream[$i];
  211. $keyStream[$i] = $keyStream[$j];
  212. $keyStream[$j] = $temp;
  213. }
  214. $this->encryptIndex = $this->decryptIndex = array(0, 0);
  215. $this->encryptStream = $this->decryptStream = $keyStream;
  216. }
  217. /**
  218. * Sets the password.
  219. *
  220. * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
  221. * {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
  222. * $hash, $salt, $count, $dkLen
  223. *
  224. * @param String $password
  225. * @param optional String $method
  226. * @access public
  227. */
  228. function setPassword($password, $method = 'pbkdf2')
  229. {
  230. $key = '';
  231. switch ($method) {
  232. default: // 'pbkdf2'
  233. list(, , $hash, $salt, $count) = func_get_args();
  234. if (!isset($hash)) {
  235. $hash = 'sha1';
  236. }
  237. // WPA and WPA2 use the SSID as the salt
  238. if (!isset($salt)) {
  239. $salt = 'phpseclib/salt';
  240. }
  241. // RFC2898#section-4.2 uses 1,000 iterations by default
  242. // WPA and WPA2 use 4,096.
  243. if (!isset($count)) {
  244. $count = 1000;
  245. }
  246. if (!isset($dkLen)) {
  247. $dkLen = 128;
  248. }
  249. if (!class_exists('Crypt_Hash')) {
  250. require_once('Crypt/Hash.php');
  251. }
  252. $i = 1;
  253. while (strlen($key) < $dkLen) {
  254. //$dk.= $this->_pbkdf($password, $salt, $count, $i++);
  255. $hmac = new Crypt_Hash();
  256. $hmac->setHash($hash);
  257. $hmac->setKey($password);
  258. $f = $u = $hmac->hash($salt . pack('N', $i++));
  259. for ($j = 2; $j <= $count; $j++) {
  260. $u = $hmac->hash($u);
  261. $f^= $u;
  262. }
  263. $key.= $f;
  264. }
  265. }
  266. $this->setKey(substr($key, 0, $dkLen));
  267. }
  268. /**
  269. * Dummy function.
  270. *
  271. * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
  272. * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
  273. * calling setKey().
  274. *
  275. * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
  276. * the IV's are relatively easy to predict, an attack described by
  277. * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
  278. * can be used to quickly guess at the rest of the key. The following links elaborate:
  279. *
  280. * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
  281. * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
  282. *
  283. * @param String $iv
  284. * @see Crypt_RC4::setKey()
  285. * @access public
  286. */
  287. function setIV($iv)
  288. {
  289. }
  290. /**
  291. * Encrypts a message.
  292. *
  293. * @see Crypt_RC4::_crypt()
  294. * @access public
  295. * @param String $plaintext
  296. */
  297. function encrypt($plaintext)
  298. {
  299. return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
  300. }
  301. /**
  302. * Decrypts a message.
  303. *
  304. * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
  305. * Atleast if the continuous buffer is disabled.
  306. *
  307. * @see Crypt_RC4::_crypt()
  308. * @access public
  309. * @param String $ciphertext
  310. */
  311. function decrypt($ciphertext)
  312. {
  313. return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
  314. }
  315. /**
  316. * Encrypts or decrypts a message.
  317. *
  318. * @see Crypt_RC4::encrypt()
  319. * @see Crypt_RC4::decrypt()
  320. * @access private
  321. * @param String $text
  322. * @param Integer $mode
  323. */
  324. function _crypt($text, $mode)
  325. {
  326. if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
  327. $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
  328. if (!$this->continuousBuffer) {
  329. mcrypt_generic_init($this->$keyStream, $this->key, '');
  330. }
  331. return mcrypt_generic($this->$keyStream, $text);
  332. }
  333. if ($this->encryptStream === false) {
  334. $this->setKey($this->key);
  335. }
  336. switch ($mode) {
  337. case CRYPT_RC4_ENCRYPT:
  338. $keyStream = $this->encryptStream;
  339. list($i, $j) = $this->encryptIndex;
  340. break;
  341. case CRYPT_RC4_DECRYPT:
  342. $keyStream = $this->decryptStream;
  343. list($i, $j) = $this->decryptIndex;
  344. }
  345. $newText = '';
  346. for ($k = 0; $k < strlen($text); $k++) {
  347. $i = ($i + 1) & 255;
  348. $j = ($j + $keyStream[$i]) & 255;
  349. $temp = $keyStream[$i];
  350. $keyStream[$i] = $keyStream[$j];
  351. $keyStream[$j] = $temp;
  352. $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
  353. $newText.= chr(ord($text[$k]) ^ $temp);
  354. }
  355. if ($this->continuousBuffer) {
  356. switch ($mode) {
  357. case CRYPT_RC4_ENCRYPT:
  358. $this->encryptStream = $keyStream;
  359. $this->encryptIndex = array($i, $j);
  360. break;
  361. case CRYPT_RC4_DECRYPT:
  362. $this->decryptStream = $keyStream;
  363. $this->decryptIndex = array($i, $j);
  364. }
  365. }
  366. return $newText;
  367. }
  368. /**
  369. * Treat consecutive "packets" as if they are a continuous buffer.
  370. *
  371. * Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
  372. * will yield different outputs:
  373. *
  374. * <code>
  375. * echo $rc4->encrypt(substr($plaintext, 0, 8));
  376. * echo $rc4->encrypt(substr($plaintext, 8, 8));
  377. * </code>
  378. * <code>
  379. * echo $rc4->encrypt($plaintext);
  380. * </code>
  381. *
  382. * The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
  383. * another, as demonstrated with the following:
  384. *
  385. * <code>
  386. * $rc4->encrypt(substr($plaintext, 0, 8));
  387. * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
  388. * </code>
  389. * <code>
  390. * echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
  391. * </code>
  392. *
  393. * With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
  394. * outputs. The reason is due to the fact that the initialization vector's change after every encryption /
  395. * decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
  396. *
  397. * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
  398. * encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
  399. * continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
  400. * however, they are also less intuitive and more likely to cause you problems.
  401. *
  402. * @see Crypt_RC4::disableContinuousBuffer()
  403. * @access public
  404. */
  405. function enableContinuousBuffer()
  406. {
  407. if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
  408. mcrypt_generic_init($this->encryptStream, $this->key, '');
  409. mcrypt_generic_init($this->decryptStream, $this->key, '');
  410. }
  411. $this->continuousBuffer = true;
  412. }
  413. /**
  414. * Treat consecutive packets as if they are a discontinuous buffer.
  415. *
  416. * The default behavior.
  417. *
  418. * @see Crypt_RC4::enableContinuousBuffer()
  419. * @access public
  420. */
  421. function disableContinuousBuffer()
  422. {
  423. if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
  424. $this->encryptIndex = $this->decryptIndex = array(0, 0);
  425. $this->encryptStream = $this->decryptStream = false;
  426. }
  427. $this->continuousBuffer = false;
  428. }
  429. /**
  430. * Dummy function.
  431. *
  432. * Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
  433. * included is so that you can switch between a block cipher and a stream cipher transparently.
  434. *
  435. * @see Crypt_RC4::disablePadding()
  436. * @access public
  437. */
  438. function enablePadding()
  439. {
  440. }
  441. /**
  442. * Dummy function.
  443. *
  444. * @see Crypt_RC4::enablePadding()
  445. * @access public
  446. */
  447. function disablePadding()
  448. {
  449. }
  450. }
  451. // vim: ts=4:sw=4:et:
  452. // vim6: fdl=1: