]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/extlib/Crypt/AES.php
is_a() with 3 params only supported in 5.3.9 anyway
[quix0rs-gnu-social.git] / plugins / OStatus / extlib / Crypt / AES.php
1 <?php\r
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */\r
3 \r
4 /**\r
5  * Pure-PHP implementation of AES.\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  * If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from\r
12  * {@link Crypt_AES::setKey() setKey()}.  ie. if the key is 128-bits, the key length will be 128-bits.  If it's 136-bits\r
13  * it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}\r
14  * is called, again, at which point, it'll be recalculated.\r
15  *\r
16  * Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't\r
17  * make a whole lot of sense.  {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance.  Calling that function,\r
18  * however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).\r
19  *\r
20  * Here's a short example of how to use this library:\r
21  * <code>\r
22  * <?php\r
23  *    include('Crypt/AES.php');\r
24  *\r
25  *    $aes = new Crypt_AES();\r
26  *\r
27  *    $aes->setKey('abcdefghijklmnop');\r
28  *\r
29  *    $size = 10 * 1024;\r
30  *    $plaintext = '';\r
31  *    for ($i = 0; $i < $size; $i++) {\r
32  *        $plaintext.= 'a';\r
33  *    }\r
34  *\r
35  *    echo $aes->decrypt($aes->encrypt($plaintext));\r
36  * ?>\r
37  * </code>\r
38  *\r
39  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy\r
40  * of this software and associated documentation files (the "Software"), to deal\r
41  * in the Software without restriction, including without limitation the rights\r
42  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
43  * copies of the Software, and to permit persons to whom the Software is\r
44  * furnished to do so, subject to the following conditions:\r
45  * \r
46  * The above copyright notice and this permission notice shall be included in\r
47  * all copies or substantial portions of the Software.\r
48  * \r
49  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
50  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
51  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
52  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
53  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
54  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
55  * THE SOFTWARE.\r
56  *\r
57  * @category   Crypt\r
58  * @package    Crypt_AES\r
59  * @author     Jim Wigginton <terrafrost@php.net>\r
60  * @copyright  MMVIII Jim Wigginton\r
61  * @license    http://www.opensource.org/licenses/mit-license.html  MIT License\r
62  * @link       http://phpseclib.sourceforge.net\r
63  */\r
64 \r
65 /**\r
66  * Include Crypt_Rijndael\r
67  */\r
68 if (!class_exists('Crypt_Rijndael')) {\r
69     require_once 'Rijndael.php';\r
70 }\r
71 \r
72 /**#@+\r
73  * @access public\r
74  * @see Crypt_AES::encrypt()\r
75  * @see Crypt_AES::decrypt()\r
76  */\r
77 /**\r
78  * Encrypt / decrypt using the Counter mode.\r
79  *\r
80  * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.\r
81  *\r
82  * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29\r
83  */\r
84 define('CRYPT_AES_MODE_CTR', -1);\r
85 /**\r
86  * Encrypt / decrypt using the Electronic Code Book mode.\r
87  *\r
88  * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29\r
89  */\r
90 define('CRYPT_AES_MODE_ECB', 1);\r
91 /**\r
92  * Encrypt / decrypt using the Code Book Chaining mode.\r
93  *\r
94  * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29\r
95  */\r
96 define('CRYPT_AES_MODE_CBC', 2);\r
97 /**\r
98  * Encrypt / decrypt using the Cipher Feedback mode.\r
99  *\r
100  * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29\r
101  */\r
102 define('CRYPT_AES_MODE_CFB', 3);\r
103 /**\r
104  * Encrypt / decrypt using the Cipher Feedback mode.\r
105  *\r
106  * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29\r
107  */\r
108 define('CRYPT_AES_MODE_OFB', 4);\r
109 /**#@-*/\r
110 \r
111 /**#@+\r
112  * @access private\r
113  * @see Crypt_AES::Crypt_AES()\r
114  */\r
115 /**\r
116  * Toggles the internal implementation\r
117  */\r
118 define('CRYPT_AES_MODE_INTERNAL', 1);\r
119 /**\r
120  * Toggles the mcrypt implementation\r
121  */\r
122 define('CRYPT_AES_MODE_MCRYPT', 2);\r
123 /**#@-*/\r
124 \r
125 /**\r
126  * Pure-PHP implementation of AES.\r
127  *\r
128  * @author  Jim Wigginton <terrafrost@php.net>\r
129  * @version 0.1.0\r
130  * @access  public\r
131  * @package Crypt_AES\r
132  */\r
133 class Crypt_AES extends Crypt_Rijndael {\r
134     /**\r
135      * mcrypt resource for encryption\r
136      *\r
137      * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.\r
138      * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.\r
139      *\r
140      * @see Crypt_AES::encrypt()\r
141      * @var String\r
142      * @access private\r
143      */\r
144     var $enmcrypt;\r
145 \r
146     /**\r
147      * mcrypt resource for decryption\r
148      *\r
149      * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.\r
150      * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.\r
151      *\r
152      * @see Crypt_AES::decrypt()\r
153      * @var String\r
154      * @access private\r
155      */\r
156     var $demcrypt;\r
157 \r
158     /**\r
159      * mcrypt resource for CFB mode\r
160      *\r
161      * @see Crypt_AES::encrypt()\r
162      * @see Crypt_AES::decrypt()\r
163      * @var String\r
164      * @access private\r
165      */\r
166     var $ecb;\r
167 \r
168     /**\r
169      * Default Constructor.\r
170      *\r
171      * Determines whether or not the mcrypt extension should be used.  $mode should only, at present, be\r
172      * CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC.  If not explictly set, CRYPT_AES_MODE_CBC will be used.\r
173      *\r
174      * @param optional Integer $mode\r
175      * @return Crypt_AES\r
176      * @access public\r
177      */\r
178     function Crypt_AES($mode = CRYPT_AES_MODE_CBC)\r
179     {\r
180         if ( !defined('CRYPT_AES_MODE') ) {\r
181             switch (true) {\r
182                 case extension_loaded('mcrypt') && in_array('rijndael-128', mcrypt_list_algorithms()):\r
183                     define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);\r
184                     break;\r
185                 default:\r
186                     define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);\r
187             }\r
188         }\r
189 \r
190         switch ( CRYPT_AES_MODE ) {\r
191             case CRYPT_AES_MODE_MCRYPT:\r
192                 switch ($mode) {\r
193                     case CRYPT_AES_MODE_ECB:\r
194                         $this->paddable = true;\r
195                         $this->mode = MCRYPT_MODE_ECB;\r
196                         break;\r
197                     case CRYPT_AES_MODE_CTR:\r
198                         // ctr doesn't have a constant associated with it even though it appears to be fairly widely\r
199                         // supported.  in lieu of knowing just how widely supported it is, i've, for now, opted not to\r
200                         // include a compatibility layer.  the layer has been implemented but, for now, is commented out.\r
201                         $this->mode = 'ctr';\r
202                         //$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;\r
203                         break;\r
204                     case CRYPT_AES_MODE_CFB:\r
205                         $this->mode = 'ncfb';\r
206                         break;\r
207                     case CRYPT_AES_MODE_OFB:\r
208                         $this->mode = MCRYPT_MODE_NOFB;\r
209                         break;\r
210                     case CRYPT_AES_MODE_CBC:\r
211                     default:\r
212                         $this->paddable = true;\r
213                         $this->mode = MCRYPT_MODE_CBC;\r
214                 }\r
215 \r
216                 break;\r
217             default:\r
218                 switch ($mode) {\r
219                     case CRYPT_AES_MODE_ECB:\r
220                         $this->paddable = true;\r
221                         $this->mode = CRYPT_RIJNDAEL_MODE_ECB;\r
222                         break;\r
223                     case CRYPT_AES_MODE_CTR:\r
224                         $this->mode = CRYPT_RIJNDAEL_MODE_CTR;\r
225                         break;\r
226                     case CRYPT_AES_MODE_CFB:\r
227                         $this->mode = CRYPT_RIJNDAEL_MODE_CFB;\r
228                         break;\r
229                     case CRYPT_AES_MODE_OFB:\r
230                         $this->mode = CRYPT_RIJNDAEL_MODE_OFB;\r
231                         break;\r
232                     case CRYPT_AES_MODE_CBC:\r
233                     default:\r
234                         $this->paddable = true;\r
235                         $this->mode = CRYPT_RIJNDAEL_MODE_CBC;\r
236                 }\r
237         }\r
238 \r
239         if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {\r
240             parent::Crypt_Rijndael($this->mode);\r
241         }\r
242 \r
243     }\r
244 \r
245     /**\r
246      * Dummy function\r
247      *\r
248      * Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.\r
249      *\r
250      * @access public\r
251      * @param Integer $length\r
252      */\r
253     function setBlockLength($length)\r
254     {\r
255         return;\r
256     }\r
257 \r
258     /**\r
259      * Sets the initialization vector. (optional)\r
260      *\r
261      * SetIV is not required when CRYPT_RIJNDAEL_MODE_ECB is being used.  If not explictly set, it'll be assumed\r
262      * to be all zero's.\r
263      *\r
264      * @access public\r
265      * @param String $iv\r
266      */\r
267     function setIV($iv)\r
268     {\r
269         parent::setIV($iv);\r
270         if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {\r
271             $this->changed = true;\r
272         }\r
273     }\r
274 \r
275     /**\r
276      * Encrypts a message.\r
277      *\r
278      * $plaintext will be padded with up to 16 additional bytes.  Other AES implementations may or may not pad in the\r
279      * same manner.  Other common approaches to padding and the reasons why it's necessary are discussed in the following\r
280      * URL:\r
281      *\r
282      * {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}\r
283      *\r
284      * An alternative to padding is to, separately, send the length of the file.  This is what SSH, in fact, does.\r
285      * strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that\r
286      * length.\r
287      *\r
288      * @see Crypt_AES::decrypt()\r
289      * @access public\r
290      * @param String $plaintext\r
291      */\r
292     function encrypt($plaintext)\r
293     {\r
294         if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {\r
295             $this->_mcryptSetup();\r
296 \r
297             // re: http://phpseclib.sourceforge.net/cfb-demo.phps\r
298             // using mcrypt's default handing of CFB the above would output two different things.  using phpseclib's\r
299             // rewritten CFB implementation the above outputs the same thing twice.\r
300             if ($this->mode == 'ncfb' && $this->continuousBuffer) {\r
301                 $iv = &$this->encryptIV;\r
302                 $pos = &$this->enbuffer['pos'];\r
303                 $len = strlen($plaintext);\r
304                 $ciphertext = '';\r
305                 $i = 0;\r
306                 if ($pos) {\r
307                     $orig_pos = $pos;\r
308                     $max = 16 - $pos;\r
309                     if ($len >= $max) {\r
310                         $i = $max;\r
311                         $len-= $max;\r
312                         $pos = 0;\r
313                     } else {\r
314                         $i = $len;\r
315                         $pos+= $len;\r
316                         $len = 0;\r
317                     }\r
318                     $ciphertext = substr($iv, $orig_pos) ^ $plaintext;\r
319                     $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);\r
320                     $this->enbuffer['enmcrypt_init'] = true;\r
321                 }\r
322                 if ($len >= 16) {\r
323                     if ($this->enbuffer['enmcrypt_init'] === false || $len > 280) {\r
324                         if ($this->enbuffer['enmcrypt_init'] === true) {\r
325                             mcrypt_generic_init($this->enmcrypt, $this->key, $iv);\r
326                             $this->enbuffer['enmcrypt_init'] = false;\r
327                         }\r
328                         $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 16));\r
329                         $iv = substr($ciphertext, -16);\r
330                         $len%= 16;\r
331                     } else {\r
332                         while ($len >= 16) {\r
333                             $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 16);\r
334                             $ciphertext.= $iv;\r
335                             $len-= 16;\r
336                             $i+= 16;\r
337                         }\r
338                     }\r
339                 }\r
340 \r
341                 if ($len) {\r
342                     $iv = mcrypt_generic($this->ecb, $iv);\r
343                     $block = $iv ^ substr($plaintext, -$len);\r
344                     $iv = substr_replace($iv, $block, 0, $len);\r
345                     $ciphertext.= $block;\r
346                     $pos = $len;\r
347                 }\r
348 \r
349                 return $ciphertext;\r
350             }\r
351 \r
352             if ($this->paddable) {\r
353                 $plaintext = $this->_pad($plaintext);\r
354             }\r
355 \r
356             $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);\r
357 \r
358             if (!$this->continuousBuffer) {\r
359                 mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);\r
360             }\r
361 \r
362             return $ciphertext;\r
363         }\r
364 \r
365         return parent::encrypt($plaintext);\r
366     }\r
367 \r
368     /**\r
369      * Decrypts a message.\r
370      *\r
371      * If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.\r
372      *\r
373      * @see Crypt_AES::encrypt()\r
374      * @access public\r
375      * @param String $ciphertext\r
376      */\r
377     function decrypt($ciphertext)\r
378     {\r
379         if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {\r
380             $this->_mcryptSetup();\r
381 \r
382             if ($this->mode == 'ncfb' && $this->continuousBuffer) {\r
383                 $iv = &$this->decryptIV;\r
384                 $pos = &$this->debuffer['pos'];\r
385                 $len = strlen($ciphertext);\r
386                 $plaintext = '';\r
387                 $i = 0;\r
388                 if ($pos) {\r
389                     $orig_pos = $pos;\r
390                     $max = 16 - $pos;\r
391                     if ($len >= $max) {\r
392                         $i = $max;\r
393                         $len-= $max;\r
394                         $pos = 0;\r
395                     } else {\r
396                         $i = $len;\r
397                         $pos+= $len;\r
398                         $len = 0;\r
399                     }\r
400                     // ie. $i = min($max, $len), $len-= $i, $pos+= $i, $pos%= $blocksize\r
401                     $plaintext = substr($iv, $orig_pos) ^ $ciphertext;\r
402                     $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);\r
403                 }\r
404                 if ($len >= 16) {\r
405                     $cb = substr($ciphertext, $i, $len - $len % 16);\r
406                     $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;\r
407                     $iv = substr($cb, -16);\r
408                     $len%= 16;\r
409                 }\r
410                 if ($len) {\r
411                     $iv = mcrypt_generic($this->ecb, $iv);\r
412                     $plaintext.= $iv ^ substr($ciphertext, -$len);\r
413                     $iv = substr_replace($iv, substr($ciphertext, -$len), 0, $len);\r
414                     $pos = $len;\r
415                 }\r
416 \r
417                 return $plaintext;\r
418             }\r
419 \r
420             if ($this->paddable) {\r
421                 // we pad with chr(0) since that's what mcrypt_generic does.  to quote from http://php.net/function.mcrypt-generic :\r
422                 // "The data is padded with "\0" to make sure the length of the data is n * blocksize."\r
423                 $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));\r
424             }\r
425 \r
426             $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);\r
427 \r
428             if (!$this->continuousBuffer) {\r
429                 mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);\r
430             }\r
431 \r
432             return $this->paddable ? $this->_unpad($plaintext) : $plaintext;\r
433         }\r
434 \r
435         return parent::decrypt($ciphertext);\r
436     }\r
437 \r
438     /**\r
439      * Setup mcrypt\r
440      *\r
441      * Validates all the variables.\r
442      *\r
443      * @access private\r
444      */\r
445     function _mcryptSetup()\r
446     {\r
447         if (!$this->changed) {\r
448             return;\r
449         }\r
450 \r
451         if (!$this->explicit_key_length) {\r
452             // this just copied from Crypt_Rijndael::_setup()\r
453             $length = strlen($this->key) >> 2;\r
454             if ($length > 8) {\r
455                 $length = 8;\r
456             } else if ($length < 4) {\r
457                 $length = 4;\r
458             }\r
459             $this->Nk = $length;\r
460             $this->key_size = $length << 2;\r
461         }\r
462 \r
463         switch ($this->Nk) {\r
464             case 4: // 128\r
465                 $this->key_size = 16;\r
466                 break;\r
467             case 5: // 160\r
468             case 6: // 192\r
469                 $this->key_size = 24;\r
470                 break;\r
471             case 7: // 224\r
472             case 8: // 256\r
473                 $this->key_size = 32;\r
474         }\r
475 \r
476         $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, chr(0));\r
477         $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));\r
478 \r
479         if (!isset($this->enmcrypt)) {\r
480             $mode = $this->mode;\r
481             //$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;\r
482 \r
483             $this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');\r
484             $this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');\r
485 \r
486             if ($mode == 'ncfb') {\r
487                 $this->ecb = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_ECB, '');\r
488             }\r
489 \r
490         } // else should mcrypt_generic_deinit be called?\r
491 \r
492         mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);\r
493         mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);\r
494 \r
495         if ($this->mode == 'ncfb') {\r
496             mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");\r
497         }\r
498 \r
499         $this->changed = false;\r
500     }\r
501 \r
502     /**\r
503      * Treat consecutive "packets" as if they are a continuous buffer.\r
504      *\r
505      * The default behavior.\r
506      *\r
507      * @see Crypt_Rijndael::disableContinuousBuffer()\r
508      * @access public\r
509      */\r
510     function enableContinuousBuffer()\r
511     {\r
512         parent::enableContinuousBuffer();\r
513 \r
514         if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {\r
515             $this->enbuffer['enmcrypt_init'] = true;\r
516             $this->debuffer['demcrypt_init'] = true;\r
517         }\r
518     }\r
519 \r
520     /**\r
521      * Treat consecutive packets as if they are a discontinuous buffer.\r
522      *\r
523      * The default behavior.\r
524      *\r
525      * @see Crypt_Rijndael::enableContinuousBuffer()\r
526      * @access public\r
527      */\r
528     function disableContinuousBuffer()\r
529     {\r
530         parent::disableContinuousBuffer();\r
531 \r
532         if (CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT) {\r
533             mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);\r
534             mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);\r
535         }\r
536     }\r
537 }\r
538 \r
539 // vim: ts=4:sw=4:et:\r
540 // vim6: fdl=1:\r