2 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
\r
5 * Pure-PHP implementation of Triple DES.
\r
7 * Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
\r
9 * PHP versions 4 and 5
\r
11 * Here's a short example of how to use this library:
\r
14 * include('Crypt/TripleDES.php');
\r
16 * $des = new Crypt_TripleDES();
\r
18 * $des->setKey('abcdefghijklmnopqrstuvwx');
\r
20 * $size = 10 * 1024;
\r
22 * for ($i = 0; $i < $size; $i++) {
\r
26 * echo $des->decrypt($des->encrypt($plaintext));
\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
37 * The above copyright notice and this permission notice shall be included in
\r
38 * all copies or substantial portions of the Software.
\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
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
59 if (!class_exists('Crypt_DES')) {
\r
60 require_once('DES.php');
\r
64 * Encrypt / decrypt using inner chaining
\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
68 define('CRYPT_DES_MODE_3CBC', -2);
\r
71 * Encrypt / decrypt using outer chaining
\r
73 * Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
\r
75 define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
\r
78 * Pure-PHP implementation of Triple DES.
\r
80 * @author Jim Wigginton <terrafrost@php.net>
\r
83 * @package Crypt_TerraDES
\r
85 class Crypt_TripleDES extends Crypt_DES {
\r
87 * The Crypt_DES objects
\r
95 * Default Constructor.
\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
100 * @param optional Integer $mode
\r
101 * @return Crypt_TripleDES
\r
104 function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
\r
106 if ( !defined('CRYPT_DES_MODE') ) {
\r
108 case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()):
\r
109 define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
\r
112 define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
\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
123 $this->paddable = true;
\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
133 switch ( CRYPT_DES_MODE ) {
\r
134 case CRYPT_DES_MODE_MCRYPT:
\r
136 case CRYPT_DES_MODE_ECB:
\r
137 $this->paddable = true;
\r
138 $this->mode = MCRYPT_MODE_ECB;
\r
140 case CRYPT_DES_MODE_CTR:
\r
141 $this->mode = 'ctr';
\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
147 case CRYPT_DES_MODE_OFB:
\r
148 $this->mode = MCRYPT_MODE_NOFB;
\r
150 case CRYPT_DES_MODE_CBC:
\r
152 $this->paddable = true;
\r
153 $this->mode = MCRYPT_MODE_CBC;
\r
155 $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
\r
156 $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
\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
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
172 case CRYPT_DES_MODE_ECB:
\r
173 case CRYPT_DES_MODE_CBC:
\r
174 $this->paddable = true;
\r
175 $this->mode = $mode;
\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
183 $this->paddable = true;
\r
184 $this->mode = CRYPT_DES_MODE_CBC;
\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
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
199 * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
\r
201 * If the key is not explicitly set, it'll be assumed to be all zero's.
\r
204 * @param String $key
\r
206 function setKey($key)
\r
208 $length = strlen($key);
\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
215 $key = str_pad($key, 8, chr(0));
\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
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
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
241 $this->enchanged = $this->dechanged = true;
\r
245 * Sets the password.
\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
251 * @param String $password
\r
252 * @param optional String $method
\r
255 function setPassword($password, $method = 'pbkdf2')
\r
260 default: // 'pbkdf2'
\r
261 list(, , $hash, $salt, $count) = func_get_args();
\r
262 if (!isset($hash)) {
\r
265 // WPA and WPA2 use the SSID as the salt
\r
266 if (!isset($salt)) {
\r
267 $salt = 'phpseclib';
\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
275 if (!class_exists('Crypt_Hash')) {
\r
276 require_once('Crypt/Hash.php');
\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
293 $this->setKey($key);
\r
297 * Sets the initialization vector. (optional)
\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
303 * @param String $iv
\r
305 function setIV($iv)
\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
313 $this->enchanged = $this->dechanged = true;
\r
317 * Encrypts a message.
\r
320 * @param String $plaintext
\r
322 function encrypt($plaintext)
\r
324 if ($this->paddable) {
\r
325 $plaintext = $this->_pad($plaintext);
\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
332 return $ciphertext;
\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
341 $this->enchanged = false;
\r
344 if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
\r
345 $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
\r
347 $iv = &$this->encryptIV;
\r
348 $pos = &$this->enbuffer['pos'];
\r
349 $len = strlen($plaintext);
\r
355 if ($len >= $max) {
\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
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
374 $ciphertext.= mcrypt_generic($this->enmcrypt, substr($plaintext, $i, $len - $len % 8));
\r
375 $iv = substr($ciphertext, -8);
\r
376 $i = strlen($ciphertext);
\r
379 while ($len >= 8) {
\r
380 $iv = mcrypt_generic($this->ecb, $iv) ^ substr($plaintext, $i, 8);
\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
394 return $ciphertext;
\r
397 if (!$this->continuousBuffer) {
\r
398 mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
\r
401 return $ciphertext;
\r
404 if (strlen($this->key) <= 8) {
\r
405 $this->des[0]->mode = $this->mode;
\r
407 return $this->des[0]->encrypt($plaintext);
\r
410 if ($this->use_inline_crypt) {
\r
411 $inline = $this->inline_crypt;
\r
412 return $inline('encrypt', $this, $plaintext);
\r
417 $buffer = &$this->enbuffer;
\r
418 $continuousBuffer = $this->continuousBuffer;
\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
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
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
444 $ciphertext.= $block;
\r
446 if ($this->continuousBuffer) {
\r
447 $this->encryptIV = $xor;
\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
462 $key = $this->_string_shift($buffer['encrypted']);
\r
463 $ciphertext.= $block ^ $key;
\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
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
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
491 $iv = $this->encryptIV;
\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
501 $iv = $block ^ $xor;
\r
502 if ($continuousBuffer && strlen($iv) != 8) {
\r
504 'encrypted' => $iv,
\r
505 'xor' => substr($xor, strlen($iv))
\r
511 if ($this->continuousBuffer) {
\r
512 $this->encryptIV = $iv;
\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
526 $key = $this->_string_shift($buffer['xor']);
\r
527 $ciphertext.= $block ^ $key;
\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
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
546 return $ciphertext;
\r
550 * Decrypts a message.
\r
553 * @param String $ciphertext
\r
555 function decrypt($ciphertext)
\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
560 return $this->_unpad($plaintext);
\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
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
575 $this->dechanged = false;
\r
578 if ($this->mode != 'ncfb' || !$this->continuousBuffer) {
\r
579 $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
\r
581 $iv = &$this->decryptIV;
\r
582 $pos = &$this->debuffer['pos'];
\r
583 $len = strlen($ciphertext);
\r
589 if ($len >= $max) {
\r
598 $plaintext = substr($iv, $orig_pos) ^ $ciphertext;
\r
599 $iv = substr_replace($iv, substr($ciphertext, 0, $i), $orig_pos, $i);
\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
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
617 if (!$this->continuousBuffer) {
\r
618 mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
\r
621 return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
\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
630 if ($this->use_inline_crypt) {
\r
631 $inline = $this->inline_crypt;
\r
632 return $inline('decrypt', $this, $ciphertext);
\r
637 $buffer = &$this->debuffer;
\r
638 $continuousBuffer = $this->continuousBuffer;
\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
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
660 if ($this->continuousBuffer) {
\r
661 $this->decryptIV = $xor;
\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
676 $key = $this->_string_shift($buffer['ciphertext']);
\r
677 $plaintext.= $block ^ $key;
\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
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
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
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
709 $start = strlen($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
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
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
730 if ($this->continuousBuffer) {
\r
731 $this->decryptIV = $block;
\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
745 $key = $this->_string_shift($buffer['xor']);
\r
746 $plaintext.= $block ^ $key;
\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
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
765 return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
\r
769 * Treat consecutive "packets" as if they are a continuous buffer.
\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
775 * echo $des->encrypt(substr($plaintext, 0, 8));
\r
776 * echo $des->encrypt(substr($plaintext, 8, 8));
\r
779 * echo $des->encrypt($plaintext);
\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
786 * $des->encrypt(substr($plaintext, 0, 8));
\r
787 * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
\r
790 * echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
\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
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
802 * @see Crypt_TripleDES::disableContinuousBuffer()
\r
805 function enableContinuousBuffer()
\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
816 * Treat consecutive packets as if they are a discontinuous buffer.
\r
818 * The default behavior.
\r
820 * @see Crypt_TripleDES::enableContinuousBuffer()
\r
823 function disableContinuousBuffer()
\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
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
841 // vim: ts=4:sw=4:et:
\r