]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/OStatus/extlib/Crypt/TripleDES.php
is_a() with 3 params only supported in 5.3.9 anyway
[quix0rs-gnu-social.git] / plugins / OStatus / extlib / Crypt / TripleDES.php
1 <?php\r
2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */\r
3 \r
4 /**\r
5  * Pure-PHP implementation of Triple DES.\r
6  *\r
7  * Uses mcrypt, if available, and an internal implementation, otherwise.  Operates in the EDE3 mode (encrypt-decrypt-encrypt).\r
8  *\r
9  * PHP versions 4 and 5\r
10  *\r
11  * Here's a short example of how to use this library:\r
12  * <code>\r
13  * <?php\r
14  *    include('Crypt/TripleDES.php');\r
15  *\r
16  *    $des = new Crypt_TripleDES();\r
17  *\r
18  *    $des->setKey('abcdefghijklmnopqrstuvwx');\r
19  *\r
20  *    $size = 10 * 1024;\r
21  *    $plaintext = '';\r
22  *    for ($i = 0; $i < $size; $i++) {\r
23  *        $plaintext.= 'a';\r
24  *    }\r
25  *\r
26  *    echo $des->decrypt($des->encrypt($plaintext));\r
27  * ?>\r
28  * </code>\r
29  *\r
30  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy\r
31  * of this software and associated documentation files (the "Software"), to deal\r
32  * in the Software without restriction, including without limitation the rights\r
33  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
34  * copies of the Software, and to permit persons to whom the Software is\r
35  * furnished to do so, subject to the following conditions:\r
36  * \r
37  * The above copyright notice and this permission notice shall be included in\r
38  * all copies or substantial portions of the Software.\r
39  * \r
40  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
41  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
42  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
43  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
44  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
45  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
46  * THE SOFTWARE.\r
47  *\r
48  * @category   Crypt\r
49  * @package    Crypt_TripleDES\r
50  * @author     Jim Wigginton <terrafrost@php.net>\r
51  * @copyright  MMVII Jim Wigginton\r
52  * @license    http://www.opensource.org/licenses/mit-license.html  MIT License\r
53  * @link       http://phpseclib.sourceforge.net\r
54  */\r
55 \r
56 /**\r
57  * Include Crypt_DES\r
58  */\r
59 if (!class_exists('Crypt_DES')) {\r
60     require_once('DES.php');\r
61 }\r
62 \r
63 /**\r
64  * Encrypt / decrypt using inner chaining\r
65  *\r
66  * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).\r
67  */\r
68 define('CRYPT_DES_MODE_3CBC', -2);\r
69 \r
70 /**\r
71  * Encrypt / decrypt using outer chaining\r
72  *\r
73  * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.\r
74  */\r
75 define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);\r
76 \r
77 /**\r
78  * Pure-PHP implementation of Triple DES.\r
79  *\r
80  * @author  Jim Wigginton <terrafrost@php.net>\r
81  * @version 0.1.0\r
82  * @access  public\r
83  * @package Crypt_TerraDES\r
84  */\r
85 class Crypt_TripleDES extends Crypt_DES {\r
86     /**\r
87      * The Crypt_DES objects\r
88      *\r
89      * @var Array\r
90      * @access private\r
91      */\r
92     var $des;\r
93 \r
94     /**\r
95      * Default Constructor.\r
96      *\r
97      * Determines whether or not the mcrypt extension should be used.  $mode should only, at present, be\r
98      * CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC.  If not explictly set, CRYPT_DES_MODE_CBC will be used.\r
99      *\r
100      * @param optional Integer $mode\r
101      * @return Crypt_TripleDES\r
102      * @access public\r
103      */\r
104     function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)\r
105     {\r
106         if ( !defined('CRYPT_DES_MODE') ) {\r
107             switch (true) {\r
108                 case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()):\r
109                     define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);\r
110                     break;\r
111                 default:\r
112                     define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);\r
113             }\r
114         }\r
115 \r
116         if ( $mode == CRYPT_DES_MODE_3CBC ) {\r
117             $this->mode = CRYPT_DES_MODE_3CBC;\r
118             $this->des = array(\r
119                 new Crypt_DES(CRYPT_DES_MODE_CBC),\r
120                 new Crypt_DES(CRYPT_DES_MODE_CBC),\r
121                 new Crypt_DES(CRYPT_DES_MODE_CBC)\r
122             );\r
123             $this->paddable = true;\r
124 \r
125             // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects\r
126             $this->des[0]->disablePadding();\r
127             $this->des[1]->disablePadding();\r
128             $this->des[2]->disablePadding();\r
129 \r
130             return;\r
131         }\r
132 \r
133         switch ( CRYPT_DES_MODE ) {\r
134             case CRYPT_DES_MODE_MCRYPT:\r
135                 switch ($mode) {\r
136                     case CRYPT_DES_MODE_ECB:\r
137                         $this->paddable = true;\r
138                         $this->mode = MCRYPT_MODE_ECB;\r
139                         break;\r
140                     case CRYPT_DES_MODE_CTR:\r
141                         $this->mode = 'ctr';\r
142                         break;\r
143                     case CRYPT_DES_MODE_CFB:\r
144                         $this->mode = 'ncfb';\r
145                         $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');\r
146                         break;\r
147                     case CRYPT_DES_MODE_OFB:\r
148                         $this->mode = MCRYPT_MODE_NOFB;\r
149                         break;\r
150                     case CRYPT_DES_MODE_CBC:\r
151                     default:\r
152                         $this->paddable = true;\r
153                         $this->mode = MCRYPT_MODE_CBC;\r
154                 }\r
155                 $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');\r
156                 $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');\r
157 \r
158                 break;\r
159             default:\r
160                 $this->des = array(\r
161                     new Crypt_DES(CRYPT_DES_MODE_ECB),\r
162                     new Crypt_DES(CRYPT_DES_MODE_ECB),\r
163                     new Crypt_DES(CRYPT_DES_MODE_ECB)\r
164                 );\r
165  \r
166                 // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects\r
167                 $this->des[0]->disablePadding();\r
168                 $this->des[1]->disablePadding();\r
169                 $this->des[2]->disablePadding();\r
170 \r
171                 switch ($mode) {\r
172                     case CRYPT_DES_MODE_ECB:\r
173                     case CRYPT_DES_MODE_CBC:\r
174                         $this->paddable = true;\r
175                         $this->mode = $mode;\r
176                         break;\r
177                     case CRYPT_DES_MODE_CTR:\r
178                     case CRYPT_DES_MODE_CFB:\r
179                     case CRYPT_DES_MODE_OFB:\r
180                         $this->mode = $mode;\r
181                         break;\r
182                     default:\r
183                         $this->paddable = true;\r
184                         $this->mode = CRYPT_DES_MODE_CBC;\r
185                 }\r
186                 if (function_exists('create_function') && is_callable('create_function')) {\r
187                     $this->inline_crypt_setup(3);\r
188                     $this->use_inline_crypt = true;\r
189                 }\r
190         }\r
191     }\r
192 \r
193     /**\r
194      * Sets the key.\r
195      *\r
196      * Keys can be of any length.  Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or\r
197      * 192-bit (eg. strlen($key) == 24) keys.  This function pads and truncates $key as appropriate.\r
198      *\r
199      * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.\r
200      *\r
201      * If the key is not explicitly set, it'll be assumed to be all zero's.\r
202      *\r
203      * @access public\r
204      * @param String $key\r
205      */\r
206     function setKey($key)\r
207     {\r
208         $length = strlen($key);\r
209         if ($length > 8) {\r
210             $key = str_pad($key, 24, chr(0));\r
211             // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:\r
212             // http://php.net/function.mcrypt-encrypt#47973\r
213             //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);\r
214         } else {\r
215             $key = str_pad($key, 8, chr(0));\r
216         }\r
217         $this->key = $key;\r
218         switch (true) {\r
219             case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:\r
220             case $this->mode == CRYPT_DES_MODE_3CBC:\r
221                 $this->des[0]->setKey(substr($key,  0, 8));\r
222                 $this->des[1]->setKey(substr($key,  8, 8));\r
223                 $this->des[2]->setKey(substr($key, 16, 8));\r
224 \r
225                 // Merge the three DES-1-dim-key-arrays for 3DES-inline-en/decrypting  \r
226                 if ($this->use_inline_crypt && $this->mode != CRYPT_DES_MODE_3CBC) {\r
227                     $this->keys = array(\r
228                         CRYPT_DES_ENCRYPT_1DIM => array_merge(\r
229                             $this->des[0]->keys[CRYPT_DES_ENCRYPT_1DIM],\r
230                             $this->des[1]->keys[CRYPT_DES_DECRYPT_1DIM],\r
231                             $this->des[2]->keys[CRYPT_DES_ENCRYPT_1DIM]\r
232                         ),\r
233                         CRYPT_DES_DECRYPT_1DIM => array_merge(\r
234                             $this->des[2]->keys[CRYPT_DES_DECRYPT_1DIM],\r
235                             $this->des[1]->keys[CRYPT_DES_ENCRYPT_1DIM],\r
236                             $this->des[0]->keys[CRYPT_DES_DECRYPT_1DIM]\r
237                         ),\r
238                     );\r
239                 }\r
240         }\r
241         $this->enchanged = $this->dechanged = true;\r
242     }\r
243 \r
244     /**\r
245      * Sets the password.\r
246      *\r
247      * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:\r
248      *     {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:\r
249      *         $hash, $salt, $method\r
250      *\r
251      * @param String $password\r
252      * @param optional String $method\r
253      * @access public\r
254      */\r
255     function setPassword($password, $method = 'pbkdf2')\r
256     {\r
257         $key = '';\r
258 \r
259         switch ($method) {\r
260             default: // 'pbkdf2'\r
261                 list(, , $hash, $salt, $count) = func_get_args();\r
262                 if (!isset($hash)) {\r
263                     $hash = 'sha1';\r
264                 }\r
265                 // WPA and WPA2 use the SSID as the salt\r
266                 if (!isset($salt)) {\r
267                     $salt = 'phpseclib';\r
268                 }\r
269                 // RFC2898#section-4.2 uses 1,000 iterations by default\r
270                 // WPA and WPA2 use 4,096.\r
271                 if (!isset($count)) {\r
272                     $count = 1000;\r
273                 }\r
274 \r
275                 if (!class_exists('Crypt_Hash')) {\r
276                     require_once('Crypt/Hash.php');\r
277                 }\r
278 \r
279                 $i = 1;\r
280                 while (strlen($key) < 24) { // $dkLen == 24\r
281                     $hmac = new Crypt_Hash();\r
282                     $hmac->setHash($hash);\r
283                     $hmac->setKey($password);\r
284                     $f = $u = $hmac->hash($salt . pack('N', $i++));\r
285                     for ($j = 2; $j <= $count; $j++) {\r
286                         $u = $hmac->hash($u);\r
287                         $f^= $u;\r
288                     }\r
289                     $key.= $f;\r
290                 }\r
291         }\r
292 \r
293         $this->setKey($key);\r
294     }\r
295 \r
296     /**\r
297      * Sets the initialization vector. (optional)\r
298      *\r
299      * SetIV is not required when CRYPT_DES_MODE_ECB is being used.  If not explictly set, it'll be assumed\r
300      * to be all zero's.\r
301      *\r
302      * @access public\r
303      * @param String $iv\r
304      */\r
305     function setIV($iv)\r
306     {\r
307         $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));\r
308         if ($this->mode == CRYPT_DES_MODE_3CBC) {\r
309             $this->des[0]->setIV($iv);\r
310             $this->des[1]->setIV($iv);\r
311             $this->des[2]->setIV($iv);\r
312         }\r
313         $this->enchanged = $this->dechanged = true;\r
314     }\r
315 \r
316     /**\r
317      * Encrypts a message.\r
318      *\r
319      * @access public\r
320      * @param String $plaintext\r
321      */\r
322     function encrypt($plaintext)\r
323     {\r
324         if ($this->paddable) {\r
325             $plaintext = $this->_pad($plaintext);\r
326         }\r
327 \r
328         // if the key is smaller then 8, do what we'd normally do\r
329         if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {\r
330             $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));\r
331 \r
332             return $ciphertext;\r
333         }\r
334 \r
335         if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {\r
336             if ($this->enchanged) {\r
337                 mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);\r
338                 if ($this->mode == 'ncfb') {\r
339                     mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");\r
340                 }\r
341                 $this->enchanged = false;\r
342             }\r
343 \r
344             if ($this->mode != 'ncfb' || !$this->continuousBuffer) {\r
345                 $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);\r
346             } else {\r
347                 $iv = &$this->encryptIV;\r
348                 $pos = &$this->enbuffer['pos'];\r
349                 $len = strlen($plaintext);\r
350                 $ciphertext = '';\r
351                 $i = 0;\r
352                 if ($pos) {\r
353                     $orig_pos = $pos;\r
354                     $max = 8 - $pos;\r
355                     if ($len >= $max) {\r
356                         $i = $max;\r
357                         $len-= $max;\r
358                         $pos = 0;\r
359                     } else {\r
360                         $i = $len;\r
361                         $pos+= $len;\r
362                         $len = 0;\r
363                     }\r
364                     $ciphertext = substr($iv, $orig_pos) ^ $plaintext;\r
365                     $iv = substr_replace($iv, $ciphertext, $orig_pos, $i);\r
366                     $this->enbuffer['enmcrypt_init'] = true;\r
367                 }\r
368                 if ($len >= 8) {\r
369                     if ($this->enbuffer['enmcrypt_init'] === false || $len > 950) {\r
370                         if ($this->enbuffer['enmcrypt_init'] === true) {\r
371                             mcrypt_generic_init($this->enmcrypt, $this->key, $iv);\r
372                             $this->enbuffer['enmcrypt_init'] = false;\r
373                         }\r
374                         $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));\r
375                         $iv = substr($ciphertext, -8);\r
376                         $i = strlen($ciphertext);\r
377                         $len%= 8;\r
378                     } else {\r
379                         while ($len >= 8) {\r
380                             $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);\r
381                             $ciphertext.= $iv;\r
382                             $len-= 8;\r
383                             $i+= 8;\r
384                         }\r
385                     }\r
386                 } \r
387                 if ($len) {\r
388                     $iv = mcrypt_generic($this->ecb, $iv);\r
389                     $block = $iv ^ substr($plaintext, $i);\r
390                     $iv = substr_replace($iv, $block, 0, $len);\r
391                     $ciphertext.= $block;\r
392                     $pos = $len;\r
393                 }\r
394                 return $ciphertext;\r
395             }\r
396 \r
397             if (!$this->continuousBuffer) {\r
398                 mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);\r
399             }\r
400 \r
401             return $ciphertext;\r
402         }\r
403 \r
404         if (strlen($this->key) <= 8) {\r
405             $this->des[0]->mode = $this->mode;\r
406 \r
407             return $this->des[0]->encrypt($plaintext);\r
408         }\r
409 \r
410         if ($this->use_inline_crypt) {\r
411             $inline = $this->inline_crypt;\r
412             return $inline('encrypt', $this, $plaintext);\r
413         }\r
414 \r
415         $des = $this->des;\r
416 \r
417         $buffer = &$this->enbuffer;\r
418         $continuousBuffer = $this->continuousBuffer;\r
419         $ciphertext = '';\r
420         switch ($this->mode) {\r
421             case CRYPT_DES_MODE_ECB:\r
422                 for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
423                     $block = substr($plaintext, $i, 8);\r
424                     // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.\r
425                     // only problem with that: it would slow encryption and decryption down.  $this->des would have to be called every time that\r
426                     // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make \r
427                     // encryption and decryption take more time, per this:\r
428                     //\r
429                     // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html\r
430                     $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
431                     $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);\r
432                     $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
433                     $ciphertext.= $block;\r
434                 }\r
435                 break;\r
436             case CRYPT_DES_MODE_CBC:\r
437                 $xor = $this->encryptIV;\r
438                 for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
439                     $block = substr($plaintext, $i, 8) ^ $xor;\r
440                     $block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
441                     $block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);\r
442                     $block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
443                     $xor = $block;\r
444                     $ciphertext.= $block;\r
445                 }\r
446                 if ($this->continuousBuffer) {\r
447                     $this->encryptIV = $xor;\r
448                 }\r
449                 break;\r
450             case CRYPT_DES_MODE_CTR:\r
451                 $xor = $this->encryptIV;\r
452                 if (strlen($buffer['encrypted'])) {\r
453                     for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
454                         $block = substr($plaintext, $i, 8);\r
455                         if (strlen($block) > strlen($buffer['encrypted'])) {\r
456                             $key = $this->_generate_xor($xor);\r
457                             $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
458                             $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);\r
459                             $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
460                             $buffer['encrypted'].= $key;\r
461                         }\r
462                         $key = $this->_string_shift($buffer['encrypted']);\r
463                         $ciphertext.= $block ^ $key;\r
464                     }\r
465                 } else {\r
466                     for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
467                         $block = substr($plaintext, $i, 8);\r
468                         $key = $this->_generate_xor($xor);\r
469                         $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
470                         $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);\r
471                         $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
472                         $ciphertext.= $block ^ $key;\r
473                     }\r
474                 }\r
475                 if ($this->continuousBuffer) {\r
476                     $this->encryptIV = $xor;\r
477                     if ($start = strlen($plaintext) & 7) {\r
478                         $buffer['encrypted'] = substr($key, $start) . $buffer['encrypted'];\r
479                     }\r
480                 }\r
481                 break;\r
482             case CRYPT_DES_MODE_CFB:\r
483                 if (strlen($buffer['xor'])) {\r
484                     $ciphertext = $plaintext ^ $buffer['xor'];\r
485                     $iv = $buffer['encrypted'] . $ciphertext;\r
486                     $start = strlen($ciphertext);\r
487                     $buffer['encrypted'].= $ciphertext;\r
488                     $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));\r
489                 } else {\r
490                     $ciphertext = '';\r
491                     $iv = $this->encryptIV;\r
492                     $start = 0;\r
493                 }\r
494 \r
495                 for ($i = $start; $i < strlen($plaintext); $i+=8) {\r
496                     $block = substr($plaintext, $i, 8);\r
497                     $iv = $des[0]->_processBlock($iv, CRYPT_DES_ENCRYPT);\r
498                     $iv = $des[1]->_processBlock($iv, CRYPT_DES_DECRYPT);\r
499                     $xor= $des[2]->_processBlock($iv, CRYPT_DES_ENCRYPT);\r
500 \r
501                     $iv = $block ^ $xor;\r
502                     if ($continuousBuffer && strlen($iv) != 8) {\r
503                         $buffer = array(\r
504                             'encrypted' => $iv,\r
505                             'xor' => substr($xor, strlen($iv))\r
506                         );\r
507                     }\r
508                     $ciphertext.= $iv;\r
509                 }\r
510 \r
511                 if ($this->continuousBuffer) {\r
512                     $this->encryptIV = $iv;\r
513                 }\r
514                 break;\r
515             case CRYPT_DES_MODE_OFB:\r
516                 $xor = $this->encryptIV;\r
517                 if (strlen($buffer['xor'])) {\r
518                     for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
519                         $block = substr($plaintext, $i, 8);\r
520                         if (strlen($block) > strlen($buffer['xor'])) {\r
521                             $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
522                             $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
523                             $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
524                             $buffer['xor'].= $xor;\r
525                         }\r
526                         $key = $this->_string_shift($buffer['xor']);\r
527                         $ciphertext.= $block ^ $key;\r
528                     }\r
529                 } else {\r
530                     for ($i = 0; $i < strlen($plaintext); $i+=8) {\r
531                         $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
532                         $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
533                         $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
534                         $ciphertext.= substr($plaintext, $i, 8) ^ $xor;\r
535                     }\r
536                     $key = $xor;\r
537                 }\r
538                 if ($this->continuousBuffer) {\r
539                     $this->encryptIV = $xor;\r
540                     if ($start = strlen($plaintext) & 7) {\r
541                          $buffer['xor'] = substr($key, $start) . $buffer['xor'];\r
542                     }\r
543                 }\r
544         }\r
545 \r
546         return $ciphertext;\r
547     }\r
548 \r
549     /**\r
550      * Decrypts a message.\r
551      *\r
552      * @access public\r
553      * @param String $ciphertext\r
554      */\r
555     function decrypt($ciphertext)\r
556     {\r
557         if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {\r
558             $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));\r
559 \r
560             return $this->_unpad($plaintext);\r
561         }\r
562 \r
563         if ($this->paddable) {\r
564             // we pad with chr(0) since that's what mcrypt_generic does.  to quote from http://php.net/function.mcrypt-generic :\r
565             // "The data is padded with "\0" to make sure the length of the data is n * blocksize."\r
566             $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));\r
567         }\r
568 \r
569         if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {\r
570             if ($this->dechanged) {\r
571                 mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);\r
572                 if ($this->mode == 'ncfb') {\r
573                     mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");\r
574                 }\r
575                 $this->dechanged = false;\r
576             }\r
577 \r
578             if ($this->mode != 'ncfb' || !$this->continuousBuffer) {\r
579                 $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);\r
580             } else {\r
581                 $iv = &$this->decryptIV;\r
582                 $pos = &$this->debuffer['pos'];\r
583                 $len = strlen($ciphertext);\r
584                 $plaintext = '';\r
585                 $i = 0;\r
586                 if ($pos) {\r
587                     $orig_pos = $pos;\r
588                     $max = 8 - $pos;\r
589                     if ($len >= $max) {\r
590                         $i = $max;\r
591                         $len-= $max;\r
592                         $pos = 0;\r
593                     } else {\r
594                         $i = $len;\r
595                         $pos+= $len;\r
596                         $len = 0;\r
597                     }\r
598                     $plaintext = substr($iv, $orig_pos) ^ $ciphertext;\r
599                     $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);\r
600                 }\r
601                 if ($len >= 8) {\r
602                     $cb = substr($ciphertext, $i, $len - $len % 8);\r
603                     $plaintext.= mcrypt_generic($this->ecb, $iv . $cb) ^ $cb;\r
604                     $iv = substr($cb, -8);\r
605                     $len%= 8;\r
606                 }\r
607                 if ($len) {\r
608                     $iv = mcrypt_generic($this->ecb, $iv);\r
609                     $cb = substr($ciphertext, -$len);\r
610                     $plaintext.= $iv ^ $cb;\r
611                     $iv = substr_replace($iv, $cb, 0, $len);\r
612                     $pos = $len;\r
613                 }\r
614                 return $plaintext;\r
615             }\r
616 \r
617             if (!$this->continuousBuffer) {\r
618                 mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);\r
619             }\r
620 \r
621             return $this->paddable ? $this->_unpad($plaintext) : $plaintext;\r
622         }\r
623 \r
624         if (strlen($this->key) <= 8) {\r
625             $this->des[0]->mode = $this->mode;\r
626             $plaintext = $this->des[0]->decrypt($ciphertext);\r
627             return $this->paddable ? $this->_unpad($plaintext) : $plaintext;\r
628         }\r
629 \r
630         if ($this->use_inline_crypt) {\r
631             $inline = $this->inline_crypt;\r
632             return $inline('decrypt', $this, $ciphertext);\r
633         }\r
634 \r
635         $des = $this->des;\r
636 \r
637         $buffer = &$this->debuffer;\r
638         $continuousBuffer = $this->continuousBuffer;\r
639         $plaintext = '';\r
640         switch ($this->mode) {\r
641             case CRYPT_DES_MODE_ECB:\r
642                 for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
643                     $block = substr($ciphertext, $i, 8);\r
644                     $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);\r
645                     $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
646                     $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);\r
647                     $plaintext.= $block;\r
648                 }\r
649                 break;\r
650             case CRYPT_DES_MODE_CBC:\r
651                 $xor = $this->decryptIV;\r
652                 for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
653                     $orig = $block = substr($ciphertext, $i, 8);\r
654                     $block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);\r
655                     $block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
656                     $block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);\r
657                     $plaintext.= $block ^ $xor;\r
658                     $xor = $orig;\r
659                 }\r
660                 if ($this->continuousBuffer) {\r
661                     $this->decryptIV = $xor;\r
662                 }\r
663                 break;\r
664             case CRYPT_DES_MODE_CTR:\r
665                 $xor = $this->decryptIV;\r
666                 if (strlen($buffer['ciphertext'])) {\r
667                     for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
668                         $block = substr($ciphertext, $i, 8);\r
669                         if (strlen($block) > strlen($buffer['ciphertext'])) {\r
670                             $key = $this->_generate_xor($xor);\r
671                             $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
672                             $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);\r
673                             $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
674                             $buffer['ciphertext'].= $key;\r
675                         }\r
676                         $key = $this->_string_shift($buffer['ciphertext']);\r
677                         $plaintext.= $block ^ $key;\r
678                     }\r
679                 } else {\r
680                     for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
681                         $block = substr($ciphertext, $i, 8);\r
682                         $key = $this->_generate_xor($xor);\r
683                         $key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
684                         $key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);\r
685                         $key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);\r
686                         $plaintext.= $block ^ $key;\r
687                     }\r
688                 }\r
689                 if ($this->continuousBuffer) {\r
690                     $this->decryptIV = $xor;\r
691                     if ($start = strlen($plaintext) & 7) {\r
692                         $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];\r
693                     }\r
694                 }\r
695                 break;\r
696             case CRYPT_DES_MODE_CFB:\r
697                 if (strlen($buffer['ciphertext'])) {\r
698                     $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));\r
699                     $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));\r
700                     if (strlen($buffer['ciphertext']) != 8) {\r
701                         $block = $this->decryptIV;\r
702                     } else {\r
703                         $block = $buffer['ciphertext'];\r
704                         $xor = $des[0]->_processBlock($buffer['ciphertext'], CRYPT_DES_ENCRYPT);\r
705                         $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
706                         $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
707                         $buffer['ciphertext'] = '';\r
708                     }\r
709                     $start = strlen($plaintext);\r
710                 } else {\r
711                     $plaintext = '';\r
712                     $xor = $des[0]->_processBlock($this->decryptIV, CRYPT_DES_ENCRYPT);\r
713                     $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
714                     $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
715                     $start = 0;\r
716                 }\r
717 \r
718                 for ($i = $start; $i < strlen($ciphertext); $i+=8) {\r
719                     $block = substr($ciphertext, $i, 8);\r
720                     $plaintext.= $block ^ $xor;\r
721                     if ($continuousBuffer && strlen($block) != 8) {\r
722                         $buffer['ciphertext'].= $block;\r
723                         $block = $xor;\r
724                     } else if (strlen($block) == 8) {\r
725                         $xor = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);\r
726                         $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
727                         $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
728                     }\r
729                 }\r
730                 if ($this->continuousBuffer) {\r
731                     $this->decryptIV = $block;\r
732                 }\r
733                 break;\r
734             case CRYPT_DES_MODE_OFB:\r
735                 $xor = $this->decryptIV;\r
736                 if (strlen($buffer['xor'])) {\r
737                     for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
738                         $block = substr($ciphertext, $i, 8);\r
739                         if (strlen($block) > strlen($buffer['xor'])) {\r
740                             $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
741                             $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
742                             $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
743                             $buffer['xor'].= $xor;\r
744                         }\r
745                         $key = $this->_string_shift($buffer['xor']);\r
746                         $plaintext.= $block ^ $key;\r
747                     }\r
748                 } else {\r
749                     for ($i = 0; $i < strlen($ciphertext); $i+=8) {\r
750                         $xor = $des[0]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
751                         $xor = $des[1]->_processBlock($xor, CRYPT_DES_DECRYPT);\r
752                         $xor = $des[2]->_processBlock($xor, CRYPT_DES_ENCRYPT);\r
753                         $plaintext.= substr($ciphertext, $i, 8) ^ $xor;\r
754                     }\r
755                     $key = $xor;\r
756                 }\r
757                 if ($this->continuousBuffer) {\r
758                     $this->decryptIV = $xor;\r
759                     if ($start = strlen($ciphertext) & 7) {\r
760                          $buffer['xor'] = substr($key, $start) . $buffer['xor'];\r
761                     }\r
762                 }\r
763         }\r
764 \r
765         return $this->paddable ? $this->_unpad($plaintext) : $plaintext;\r
766     }\r
767 \r
768     /**\r
769      * Treat consecutive "packets" as if they are a continuous buffer.\r
770      *\r
771      * Say you have a 16-byte plaintext $plaintext.  Using the default behavior, the two following code snippets\r
772      * will yield different outputs:\r
773      *\r
774      * <code>\r
775      *    echo $des->encrypt(substr($plaintext, 0, 8));\r
776      *    echo $des->encrypt(substr($plaintext, 8, 8));\r
777      * </code>\r
778      * <code>\r
779      *    echo $des->encrypt($plaintext);\r
780      * </code>\r
781      *\r
782      * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates\r
783      * another, as demonstrated with the following:\r
784      *\r
785      * <code>\r
786      *    $des->encrypt(substr($plaintext, 0, 8));\r
787      *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));\r
788      * </code>\r
789      * <code>\r
790      *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));\r
791      * </code>\r
792      *\r
793      * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different\r
794      * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /\r
795      * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.\r
796      *\r
797      * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each\r
798      * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that\r
799      * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),\r
800      * however, they are also less intuitive and more likely to cause you problems.\r
801      *\r
802      * @see Crypt_TripleDES::disableContinuousBuffer()\r
803      * @access public\r
804      */\r
805     function enableContinuousBuffer()\r
806     {\r
807         $this->continuousBuffer = true;\r
808         if ($this->mode == CRYPT_DES_MODE_3CBC) {\r
809             $this->des[0]->enableContinuousBuffer();\r
810             $this->des[1]->enableContinuousBuffer();\r
811             $this->des[2]->enableContinuousBuffer();\r
812         }\r
813     }\r
814 \r
815     /**\r
816      * Treat consecutive packets as if they are a discontinuous buffer.\r
817      *\r
818      * The default behavior.\r
819      *\r
820      * @see Crypt_TripleDES::enableContinuousBuffer()\r
821      * @access public\r
822      */\r
823     function disableContinuousBuffer()\r
824     {\r
825         $this->continuousBuffer = false;\r
826         $this->encryptIV = $this->iv;\r
827         $this->decryptIV = $this->iv;\r
828         $this->enchanged = true;\r
829         $this->dechanged = true;\r
830         $this->enbuffer = array('encrypted' => '', 'xor' => '', 'pos' => 0, 'enmcrypt_init' => true);\r
831         $this->debuffer = array('ciphertext' => '', 'xor' => '', 'pos' => 0, 'demcrypt_init' => true);\r
832 \r
833         if ($this->mode == CRYPT_DES_MODE_3CBC) {\r
834             $this->des[0]->disableContinuousBuffer();\r
835             $this->des[1]->disableContinuousBuffer();\r
836             $this->des[2]->disableContinuousBuffer();\r
837         }\r
838     }\r
839 }\r
840 \r
841 // vim: ts=4:sw=4:et:\r
842 // vim6: fdl=1:\r