]> git.mxchange.org Git - friendica.git/blob - phpsec/Crypt/RC4.php
Needed for salmon which seems to have a fixation with forcing you to encode/decode...
[friendica.git] / phpsec / Crypt / RC4.php
1 <?php\r
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */\r
3 \r
4 /**\r
5  * Pure-PHP implementation of RC4.\r
6  *\r
7  * Uses mcrypt, if available, and an internal implementation, otherwise.\r
8  *\r
9  * PHP versions 4 and 5\r
10  *\r
11  * Useful resources are as follows:\r
12  *\r
13  *  - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}\r
14  *  - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}\r
15  *\r
16  * RC4 is also known as ARCFOUR or ARC4.  The reason is elaborated upon at Wikipedia.  This class is named RC4 and not\r
17  * ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.\r
18  *\r
19  * Here's a short example of how to use this library:\r
20  * <code>\r
21  * <?php\r
22  *    include('Crypt/RC4.php');\r
23  *\r
24  *    $rc4 = new Crypt_RC4();\r
25  *\r
26  *    $rc4->setKey('abcdefgh');\r
27  *\r
28  *    $size = 10 * 1024;\r
29  *    $plaintext = '';\r
30  *    for ($i = 0; $i < $size; $i++) {\r
31  *        $plaintext.= 'a';\r
32  *    }\r
33  *\r
34  *    echo $rc4->decrypt($rc4->encrypt($plaintext));\r
35  * ?>\r
36  * </code>\r
37  *\r
38  * LICENSE: This library is free software; you can redistribute it and/or\r
39  * modify it under the terms of the GNU Lesser General Public\r
40  * License as published by the Free Software Foundation; either\r
41  * version 2.1 of the License, or (at your option) any later version.\r
42  *\r
43  * This library is distributed in the hope that it will be useful,\r
44  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
45  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
46  * Lesser General Public License for more details.\r
47  *\r
48  * You should have received a copy of the GNU Lesser General Public\r
49  * License along with this library; if not, write to the Free Software\r
50  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,\r
51  * MA  02111-1307  USA\r
52  *\r
53  * @category   Crypt\r
54  * @package    Crypt_RC4\r
55  * @author     Jim Wigginton <terrafrost@php.net>\r
56  * @copyright  MMVII Jim Wigginton\r
57  * @license    http://www.gnu.org/licenses/lgpl.txt\r
58  * @version    $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $\r
59  * @link       http://phpseclib.sourceforge.net\r
60  */\r
61 \r
62 /**#@+\r
63  * @access private\r
64  * @see Crypt_RC4::Crypt_RC4()\r
65  */\r
66 /**\r
67  * Toggles the internal implementation\r
68  */\r
69 define('CRYPT_RC4_MODE_INTERNAL', 1);\r
70 /**\r
71  * Toggles the mcrypt implementation\r
72  */\r
73 define('CRYPT_RC4_MODE_MCRYPT', 2);\r
74 /**#@-*/\r
75 \r
76 /**#@+\r
77  * @access private\r
78  * @see Crypt_RC4::_crypt()\r
79  */\r
80 define('CRYPT_RC4_ENCRYPT', 0);\r
81 define('CRYPT_RC4_DECRYPT', 1);\r
82 /**#@-*/\r
83 \r
84 /**\r
85  * Pure-PHP implementation of RC4.\r
86  *\r
87  * @author  Jim Wigginton <terrafrost@php.net>\r
88  * @version 0.1.0\r
89  * @access  public\r
90  * @package Crypt_RC4\r
91  */\r
92 class Crypt_RC4 {\r
93     /**\r
94      * The Key\r
95      *\r
96      * @see Crypt_RC4::setKey()\r
97      * @var String\r
98      * @access private\r
99      */\r
100     var $key = "\0";\r
101 \r
102     /**\r
103      * The Key Stream for encryption\r
104      *\r
105      * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object\r
106      *\r
107      * @see Crypt_RC4::setKey()\r
108      * @var Array\r
109      * @access private\r
110      */\r
111     var $encryptStream = false;\r
112 \r
113     /**\r
114      * The Key Stream for decryption\r
115      *\r
116      * If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object\r
117      *\r
118      * @see Crypt_RC4::setKey()\r
119      * @var Array\r
120      * @access private\r
121      */\r
122     var $decryptStream = false;\r
123 \r
124     /**\r
125      * The $i and $j indexes for encryption\r
126      *\r
127      * @see Crypt_RC4::_crypt()\r
128      * @var Integer\r
129      * @access private\r
130      */\r
131     var $encryptIndex = 0;\r
132 \r
133     /**\r
134      * The $i and $j indexes for decryption\r
135      *\r
136      * @see Crypt_RC4::_crypt()\r
137      * @var Integer\r
138      * @access private\r
139      */\r
140     var $decryptIndex = 0;\r
141 \r
142     /**\r
143      * MCrypt parameters\r
144      *\r
145      * @see Crypt_RC4::setMCrypt()\r
146      * @var Array\r
147      * @access private\r
148      */\r
149     var $mcrypt = array('', '');\r
150 \r
151     /**\r
152      * The Encryption Algorithm\r
153      *\r
154      * Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT.  Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.\r
155      *\r
156      * @see Crypt_RC4::Crypt_RC4()\r
157      * @var Integer\r
158      * @access private\r
159      */\r
160     var $mode;\r
161 \r
162     /**\r
163      * Default Constructor.\r
164      *\r
165      * Determines whether or not the mcrypt extension should be used.\r
166      *\r
167      * @param optional Integer $mode\r
168      * @return Crypt_RC4\r
169      * @access public\r
170      */\r
171     function Crypt_RC4()\r
172     {\r
173         if ( !defined('CRYPT_RC4_MODE') ) {\r
174             switch (true) {\r
175                 case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')):\r
176                     // i'd check to see if rc4 was supported, by doing in_array('arcfour', mcrypt_list_algorithms('')),\r
177                     // but since that can be changed after the object has been created, there doesn't seem to be\r
178                     // a lot of point...\r
179                     define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);\r
180                     break;\r
181                 default:\r
182                     define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);\r
183             }\r
184         }\r
185 \r
186         switch ( CRYPT_RC4_MODE ) {\r
187             case CRYPT_RC4_MODE_MCRYPT:\r
188                 switch (true) {\r
189                     case defined('MCRYPT_ARCFOUR'):\r
190                         $this->mode = MCRYPT_ARCFOUR;\r
191                         break;\r
192                     case defined('MCRYPT_RC4');\r
193                         $this->mode = MCRYPT_RC4;\r
194                 }\r
195         }\r
196     }\r
197 \r
198     /**\r
199      * Sets the key.\r
200      *\r
201      * Keys can be between 1 and 256 bytes long.  If they are longer then 256 bytes, the first 256 bytes will\r
202      * be used.  If no key is explicitly set, it'll be assumed to be a single null byte.\r
203      *\r
204      * @access public\r
205      * @param String $key\r
206      */\r
207     function setKey($key)\r
208     {\r
209         $this->key = $key;\r
210 \r
211         if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {\r
212             return;\r
213         }\r
214 \r
215         $keyLength = strlen($key);\r
216         $keyStream = array();\r
217         for ($i = 0; $i < 256; $i++) {\r
218             $keyStream[$i] = $i;\r
219         }\r
220         $j = 0;\r
221         for ($i = 0; $i < 256; $i++) {\r
222             $j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;\r
223             $temp = $keyStream[$i];\r
224             $keyStream[$i] = $keyStream[$j];\r
225             $keyStream[$j] = $temp;\r
226         }\r
227 \r
228         $this->encryptIndex = $this->decryptIndex = array(0, 0);\r
229         $this->encryptStream = $this->decryptStream = $keyStream;\r
230     }\r
231 \r
232     /**\r
233      * Dummy function.\r
234      *\r
235      * Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].\r
236      * If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before\r
237      * calling setKey().\r
238      *\r
239      * [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way.  Since, in that protocol,\r
240      * the IV's are relatively easy to predict, an attack described by\r
241      * {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}\r
242      * can be used to quickly guess at the rest of the key.  The following links elaborate:\r
243      *\r
244      * {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}\r
245      * {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}\r
246      *\r
247      * @param String $iv\r
248      * @see Crypt_RC4::setKey()\r
249      * @access public\r
250      */\r
251     function setIV($iv)\r
252     {\r
253     }\r
254 \r
255     /**\r
256      * Sets MCrypt parameters. (optional)\r
257      *\r
258      * If MCrypt is being used, empty strings will be used, unless otherwise specified.\r
259      *\r
260      * @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open\r
261      * @access public\r
262      * @param optional Integer $algorithm_directory\r
263      * @param optional Integer $mode_directory\r
264      */\r
265     function setMCrypt($algorithm_directory = '', $mode_directory = '')\r
266     {\r
267         if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {\r
268             $this->mcrypt = array($algorithm_directory, $mode_directory);\r
269             $this->_closeMCrypt();\r
270         }\r
271     }\r
272 \r
273     /**\r
274      * Encrypts a message.\r
275      *\r
276      * @see Crypt_RC4::_crypt()\r
277      * @access public\r
278      * @param String $plaintext\r
279      */\r
280     function encrypt($plaintext)\r
281     {\r
282         return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);\r
283     }\r
284 \r
285     /**\r
286      * Decrypts a message.\r
287      *\r
288      * $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).\r
289      * Atleast if the continuous buffer is disabled.\r
290      *\r
291      * @see Crypt_RC4::_crypt()\r
292      * @access public\r
293      * @param String $ciphertext\r
294      */\r
295     function decrypt($ciphertext)\r
296     {\r
297         return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);\r
298     }\r
299 \r
300     /**\r
301      * Encrypts or decrypts a message.\r
302      *\r
303      * @see Crypt_RC4::encrypt()\r
304      * @see Crypt_RC4::decrypt()\r
305      * @access private\r
306      * @param String $text\r
307      * @param Integer $mode\r
308      */\r
309     function _crypt($text, $mode)\r
310     {\r
311         if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {\r
312             $keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';\r
313 \r
314             if ($this->$keyStream === false) {\r
315                 $this->$keyStream = mcrypt_module_open($this->mode, $this->mcrypt[0], MCRYPT_MODE_STREAM, $this->mcrypt[1]);\r
316                 mcrypt_generic_init($this->$keyStream, $this->key, '');\r
317             } else if (!$this->continuousBuffer) {\r
318                 mcrypt_generic_init($this->$keyStream, $this->key, '');\r
319             }\r
320             $newText = mcrypt_generic($this->$keyStream, $text);\r
321             if (!$this->continuousBuffer) {\r
322                 mcrypt_generic_deinit($this->$keyStream);\r
323             }\r
324 \r
325             return $newText;\r
326         }\r
327 \r
328         if ($this->encryptStream === false) {\r
329             $this->setKey($this->key);\r
330         }\r
331 \r
332         switch ($mode) {\r
333             case CRYPT_RC4_ENCRYPT:\r
334                 $keyStream = $this->encryptStream;\r
335                 list($i, $j) = $this->encryptIndex;\r
336                 break;\r
337             case CRYPT_RC4_DECRYPT:\r
338                 $keyStream = $this->decryptStream;\r
339                 list($i, $j) = $this->decryptIndex;\r
340         }\r
341 \r
342         $newText = '';\r
343         for ($k = 0; $k < strlen($text); $k++) {\r
344             $i = ($i + 1) & 255;\r
345             $j = ($j + $keyStream[$i]) & 255;\r
346             $temp = $keyStream[$i];\r
347             $keyStream[$i] = $keyStream[$j];\r
348             $keyStream[$j] = $temp;\r
349             $temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];\r
350             $newText.= chr(ord($text[$k]) ^ $temp);\r
351         }\r
352 \r
353         if ($this->continuousBuffer) {\r
354             switch ($mode) {\r
355                 case CRYPT_RC4_ENCRYPT:\r
356                     $this->encryptStream = $keyStream;\r
357                     $this->encryptIndex = array($i, $j);\r
358                     break;\r
359                 case CRYPT_RC4_DECRYPT:\r
360                     $this->decryptStream = $keyStream;\r
361                     $this->decryptIndex = array($i, $j);\r
362             }\r
363         }\r
364 \r
365         return $newText;\r
366     }\r
367 \r
368     /**\r
369      * Treat consecutive "packets" as if they are a continuous buffer.\r
370      *\r
371      * Say you have a 16-byte plaintext $plaintext.  Using the default behavior, the two following code snippets\r
372      * will yield different outputs:\r
373      *\r
374      * <code>\r
375      *    echo $rc4->encrypt(substr($plaintext, 0, 8));\r
376      *    echo $rc4->encrypt(substr($plaintext, 8, 8));\r
377      * </code>\r
378      * <code>\r
379      *    echo $rc4->encrypt($plaintext);\r
380      * </code>\r
381      *\r
382      * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates\r
383      * another, as demonstrated with the following:\r
384      *\r
385      * <code>\r
386      *    $rc4->encrypt(substr($plaintext, 0, 8));\r
387      *    echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));\r
388      * </code>\r
389      * <code>\r
390      *    echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));\r
391      * </code>\r
392      *\r
393      * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different\r
394      * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /\r
395      * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.\r
396      *\r
397      * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each\r
398      * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that\r
399      * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),\r
400      * however, they are also less intuitive and more likely to cause you problems.\r
401      *\r
402      * @see Crypt_RC4::disableContinuousBuffer()\r
403      * @access public\r
404      */\r
405     function enableContinuousBuffer()\r
406     {\r
407         $this->continuousBuffer = true;\r
408     }\r
409 \r
410     /**\r
411      * Treat consecutive packets as if they are a discontinuous buffer.\r
412      *\r
413      * The default behavior.\r
414      *\r
415      * @see Crypt_RC4::enableContinuousBuffer()\r
416      * @access public\r
417      */\r
418     function disableContinuousBuffer()\r
419     {\r
420         if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {\r
421             $this->encryptIndex = $this->decryptIndex = array(0, 0);\r
422             $this->setKey($this->key);\r
423         }\r
424 \r
425         $this->continuousBuffer = false;\r
426     }\r
427 \r
428     /**\r
429      * Dummy function.\r
430      *\r
431      * Since RC4 is a stream cipher and not a block cipher, no padding is necessary.  The only reason this function is\r
432      * included is so that you can switch between a block cipher and a stream cipher transparently.\r
433      *\r
434      * @see Crypt_RC4::disablePadding()\r
435      * @access public\r
436      */\r
437     function enablePadding()\r
438     {\r
439     }\r
440 \r
441     /**\r
442      * Dummy function.\r
443      *\r
444      * @see Crypt_RC4::enablePadding()\r
445      * @access public\r
446      */\r
447     function disablePadding()\r
448     {\r
449     }\r
450 \r
451     /**\r
452      * Class destructor.\r
453      *\r
454      * Will be called, automatically, if you're using PHP5.  If you're using PHP4, call it yourself.  Only really\r
455      * needs to be called if mcrypt is being used.\r
456      *\r
457      * @access public\r
458      */\r
459     function __destruct()\r
460     {\r
461         if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {\r
462             $this->_closeMCrypt();\r
463         }\r
464     }\r
465 \r
466     /**\r
467      * Properly close the MCrypt objects.\r
468      *\r
469      * @access prviate\r
470      */\r
471     function _closeMCrypt()\r
472     {\r
473         if ( $this->encryptStream !== false ) {\r
474             if ( $this->continuousBuffer ) {\r
475                 mcrypt_generic_deinit($this->encryptStream);\r
476             }\r
477 \r
478             mcrypt_module_close($this->encryptStream);\r
479 \r
480             $this->encryptStream = false;\r
481         }\r
482 \r
483         if ( $this->decryptStream !== false ) {\r
484             if ( $this->continuousBuffer ) {\r
485                 mcrypt_generic_deinit($this->decryptStream);\r
486             }\r
487 \r
488             mcrypt_module_close($this->decryptStream);\r
489 \r
490             $this->decryptStream = false;\r
491         }\r
492     }\r
493 }