PageRenderTime 41ms CodeModel.GetById 11ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

/phpseclib/Crypt/TripleDES.php

https://github.com/kea/phpseclib
PHP | 1054 lines | 626 code | 78 blank | 350 comment | 125 complexity | 8f2c70ab159afc88d848e88f4a648d6e MD5 | raw file
   1<?php
   2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
   3
   4/**
   5 * Pure-PHP implementation of Triple DES.
   6 *
   7 * Uses mcrypt, if available, and an internal implementation, otherwise.  Operates in the EDE3 mode (encrypt-decrypt-encrypt).
   8 *
   9 * PHP versions 4 and 5
  10 *
  11 * Here's a short example of how to use this library:
  12 * <code>
  13 * <?php
  14 *    include('Crypt/TripleDES.php');
  15 *
  16 *    $des = new Crypt_TripleDES();
  17 *
  18 *    $des->setKey('abcdefghijklmnopqrstuvwx');
  19 *
  20 *    $size = 10 * 1024;
  21 *    $plaintext = '';
  22 *    for ($i = 0; $i < $size; $i++) {
  23 *        $plaintext.= 'a';
  24 *    }
  25 *
  26 *    echo $des->decrypt($des->encrypt($plaintext));
  27 * ?>
  28 * </code>
  29 *
  30 * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  31 * of this software and associated documentation files (the "Software"), to deal
  32 * in the Software without restriction, including without limitation the rights
  33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  34 * copies of the Software, and to permit persons to whom the Software is
  35 * furnished to do so, subject to the following conditions:
  36 *
  37 * The above copyright notice and this permission notice shall be included in
  38 * all copies or substantial portions of the Software.
  39 *
  40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  43 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  46 * THE SOFTWARE.
  47 *
  48 * @category   Crypt
  49 * @package    Crypt_TripleDES
  50 * @author     Jim Wigginton <terrafrost@php.net>
  51 * @copyright  MMVII Jim Wigginton
  52 * @license    http://www.opensource.org/licenses/mit-license.html  MIT License
  53 * @version    $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
  54 * @link       http://phpseclib.sourceforge.net
  55 */
  56
  57namespace phpseclib;
  58
  59/**
  60 * Pure-PHP implementation of Triple DES.
  61 *
  62 * @author  Jim Wigginton <terrafrost@php.net>
  63 * @version 0.1.0
  64 * @access  public
  65 * @package Crypt_TerraDES
  66 */
  67class Crypt_TripleDES {
  68    /**
  69     * Encrypt / decrypt using inner chaining
  70     *
  71     * Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (Crypt_DES::MODE_CBC3).
  72     */
  73    const MODE_3CBC = -2;
  74
  75    /**
  76     * Encrypt / decrypt using outer chaining
  77     *
  78     * Outer chaining is used by SSH-2 and when the mode is set to Crypt_DES::MODE_CBC.
  79     */
  80    const MODE_CBC3 = Crypt_DES::MODE_CBC;
  81
  82    /**
  83     * The Three Keys
  84     *
  85     * @see Crypt_TripleDES::setKey()
  86     * @var String
  87     * @access private
  88     */
  89    var $key = "\0\0\0\0\0\0\0\0";
  90
  91    /**
  92     * The Encryption Mode
  93     *
  94     * @see Crypt_TripleDES::Crypt_TripleDES()
  95     * @var Integer
  96     * @access private
  97     */
  98    var $mode = Crypt_DES::MODE_CBC;
  99
 100    /**
 101     * Continuous Buffer status
 102     *
 103     * @see Crypt_TripleDES::enableContinuousBuffer()
 104     * @var Boolean
 105     * @access private
 106     */
 107    var $continuousBuffer = false;
 108
 109    /**
 110     * Padding status
 111     *
 112     * @see Crypt_TripleDES::enablePadding()
 113     * @var Boolean
 114     * @access private
 115     */
 116    var $padding = true;
 117
 118    /**
 119     * The Initialization Vector
 120     *
 121     * @see Crypt_TripleDES::setIV()
 122     * @var String
 123     * @access private
 124     */
 125    var $iv = "\0\0\0\0\0\0\0\0";
 126
 127    /**
 128     * A "sliding" Initialization Vector
 129     *
 130     * @see Crypt_TripleDES::enableContinuousBuffer()
 131     * @var String
 132     * @access private
 133     */
 134    var $encryptIV = "\0\0\0\0\0\0\0\0";
 135
 136    /**
 137     * A "sliding" Initialization Vector
 138     *
 139     * @see Crypt_TripleDES::enableContinuousBuffer()
 140     * @var String
 141     * @access private
 142     */
 143    var $decryptIV = "\0\0\0\0\0\0\0\0";
 144
 145    /**
 146     * The Crypt_DES objects
 147     *
 148     * @var Array
 149     * @access private
 150     */
 151    var $des;
 152
 153    /**
 154     * mcrypt resource for encryption
 155     *
 156     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
 157     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
 158     *
 159     * @see Crypt_TripleDES::encrypt()
 160     * @var String
 161     * @access private
 162     */
 163    var $enmcrypt;
 164
 165    /**
 166     * mcrypt resource for decryption
 167     *
 168     * The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
 169     * Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
 170     *
 171     * @see Crypt_TripleDES::decrypt()
 172     * @var String
 173     * @access private
 174     */
 175    var $demcrypt;
 176
 177    /**
 178     * Does the enmcrypt resource need to be (re)initialized?
 179     *
 180     * @see Crypt_TripleDES::setKey()
 181     * @see Crypt_TripleDES::setIV()
 182     * @var Boolean
 183     * @access private
 184     */
 185    var $enchanged = true;
 186
 187    /**
 188     * Does the demcrypt resource need to be (re)initialized?
 189     *
 190     * @see Crypt_TripleDES::setKey()
 191     * @see Crypt_TripleDES::setIV()
 192     * @var Boolean
 193     * @access private
 194     */
 195    var $dechanged = true;
 196
 197    /**
 198     * Is the mode one that is paddable?
 199     *
 200     * @see Crypt_TripleDES::Crypt_TripleDES()
 201     * @var Boolean
 202     * @access private
 203     */
 204    var $paddable = false;
 205
 206    /**
 207     * Encryption buffer for CTR, OFB and CFB modes
 208     *
 209     * @see Crypt_TripleDES::encrypt()
 210     * @var String
 211     * @access private
 212     */
 213    var $enbuffer = '';
 214
 215    /**
 216     * Decryption buffer for CTR, OFB and CFB modes
 217     *
 218     * @see Crypt_TripleDES::decrypt()
 219     * @var String
 220     * @access private
 221     */
 222    var $debuffer = '';
 223
 224    /**
 225     * mcrypt resource for CFB mode
 226     *
 227     * @see Crypt_TripleDES::encrypt()
 228     * @see Crypt_TripleDES::decrypt()
 229     * @var String
 230     * @access private
 231     */
 232    var $ecb;
 233
 234    /**
 235     * Default Constructor.
 236     *
 237     * Determines whether or not the mcrypt extension should be used.  $mode should only, at present, be
 238     * Crypt_DES::MODE_ECB or Crypt_DES::MODE_CBC.  If not explictly set, Crypt_DES::MODE_CBC will be used.
 239     *
 240     * @param optional Integer $mode
 241     * @return Crypt_TripleDES
 242     * @access public
 243     */
 244    function __construct($mode = Crypt_DES::MODE_CBC)
 245    {
 246        if ( !defined('CRYPT_DES_MODE') ) {
 247            switch (true) {
 248                case extension_loaded('mcrypt') && in_array('tripledes', mcrypt_list_algorithms()):
 249                    define('CRYPT_DES_MODE', Crypt_DES::MODE_MCRYPT);
 250                    break;
 251                default:
 252                    define('CRYPT_DES_MODE', Crypt_DES::MODE_INTERNAL);
 253            }
 254        }
 255
 256        if ( $mode == self::MODE_3CBC ) {
 257            $this->mode = self::MODE_3CBC;
 258            $this->des = array(
 259                    new Crypt_DES(Crypt_DES::MODE_CBC),
 260                    new Crypt_DES(Crypt_DES::MODE_CBC),
 261                    new Crypt_DES(Crypt_DES::MODE_CBC)
 262                    );
 263
 264            // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
 265            $this->des[0]->disablePadding();
 266            $this->des[1]->disablePadding();
 267            $this->des[2]->disablePadding();
 268
 269            return;
 270        }
 271
 272        switch ( CRYPT_DES_MODE ) {
 273            case Crypt_DES::MODE_MCRYPT:
 274                switch ($mode) {
 275                    case Crypt_DES::MODE_ECB:
 276                        $this->paddable = true;
 277                        $this->mode = MCRYPT_MODE_ECB;
 278                        break;
 279                    case Crypt_DES::MODE_CTR:
 280                        $this->mode = 'ctr';
 281                        break;
 282                    case Crypt_DES::MODE_CFB:
 283                        $this->mode = 'ncfb';
 284                        break;
 285                    case Crypt_DES::MODE_OFB:
 286                        $this->mode = MCRYPT_MODE_NOFB;
 287                        break;
 288                    case Crypt_DES::MODE_CBC:
 289                    default:
 290                        $this->paddable = true;
 291                        $this->mode = MCRYPT_MODE_CBC;
 292                }
 293
 294                break;
 295            default:
 296                $this->des = array(
 297                        new Crypt_DES(Crypt_DES::MODE_ECB),
 298                        new Crypt_DES(Crypt_DES::MODE_ECB),
 299                        new Crypt_DES(Crypt_DES::MODE_ECB)
 300                        );
 301
 302                // we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
 303                $this->des[0]->disablePadding();
 304                $this->des[1]->disablePadding();
 305                $this->des[2]->disablePadding();
 306
 307                switch ($mode) {
 308                    case Crypt_DES::MODE_ECB:
 309                    case Crypt_DES::MODE_CBC:
 310                        $this->paddable = true;
 311                        $this->mode = $mode;
 312                        break;
 313                    case Crypt_DES::MODE_CTR:
 314                    case Crypt_DES::MODE_CFB:
 315                    case Crypt_DES::MODE_OFB:
 316                        $this->mode = $mode;
 317                        break;
 318                    default:
 319                        $this->paddable = true;
 320                        $this->mode = Crypt_DES::MODE_CBC;
 321                }
 322        }
 323    }
 324
 325    /**
 326     * Sets the key.
 327     *
 328     * Keys can be of any length.  Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
 329     * 192-bit (eg. strlen($key) == 24) keys.  This function pads and truncates $key as appropriate.
 330     *
 331     * DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
 332     *
 333     * If the key is not explicitly set, it'll be assumed to be all zero's.
 334     *
 335     * @access public
 336     * @param String $key
 337     */
 338    function setKey($key)
 339    {
 340        $length = strlen($key);
 341        if ($length > 8) {
 342            $key = str_pad(substr($key, 0, 24), 24, chr(0));
 343            // if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
 344            // http://php.net/function.mcrypt-encrypt#47973
 345            //$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
 346        } else {
 347            $key = str_pad($key, 8, chr(0));
 348        }
 349        $this->key = $key;
 350        switch (true) {
 351            case CRYPT_DES_MODE == Crypt_DES::MODE_INTERNAL:
 352            case $this->mode == self::MODE_3CBC:
 353                $this->des[0]->setKey(substr($key,  0, 8));
 354                $this->des[1]->setKey(substr($key,  8, 8));
 355                $this->des[2]->setKey(substr($key, 16, 8));
 356        }
 357        $this->enchanged = $this->dechanged = true;
 358    }
 359
 360    /**
 361     * Sets the password.
 362     *
 363     * Depending on what $method is set to, setPassword()'s (optional) parameters are as follows:
 364     *     {@link http://en.wikipedia.org/wiki/PBKDF2 pbkdf2}:
 365     *         $hash, $salt, $method
 366     *
 367     * @param String $password
 368     * @param optional String $method
 369     * @access public
 370     */
 371    function setPassword($password, $method = 'pbkdf2')
 372    {
 373        $key = '';
 374
 375        switch ($method) {
 376            default: // 'pbkdf2'
 377                //not the prettiest thing ever, but solves the undefined index issue with list()
 378                $args = func_get_args();
 379                $hash = 'sha1';
 380                $salt = 'phpseclib/salt';
 381                $count = 1000;
 382                switch(count($args)){
 383                    case 6:
 384                    case 5:
 385                        $count = $args[4];
 386                    case 4:
 387                        $salt = $args[3];
 388                    case 3:
 389                        $hash = $args[2];
 390                }
 391                $i = 1;
 392                while (strlen($key) < 24) { // $dkLen == 24
 393                    $hmac = new Crypt_Hash();
 394                    $hmac->setHash($hash);
 395                    $hmac->setKey($password);
 396                    $f = $u = $hmac->hash($salt . pack('N', $i++));
 397                    for ($j = 2; $j <= $count; $j++) {
 398                        $u = $hmac->hash($u);
 399                        $f^= $u;
 400                    }
 401                    $key.= $f;
 402                }
 403        }
 404
 405        $this->setKey($key);
 406    }
 407
 408    /**
 409     * Sets the initialization vector. (optional)
 410     *
 411     * SetIV is not required when Crypt_DES::MODE_ECB is being used.  If not explictly set, it'll be assumed
 412     * to be all zero's.
 413     *
 414     * @access public
 415     * @param String $iv
 416     */
 417    function setIV($iv)
 418    {
 419        $this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
 420        if ($this->mode == self::MODE_3CBC) {
 421            $this->des[0]->setIV($iv);
 422            $this->des[1]->setIV($iv);
 423            $this->des[2]->setIV($iv);
 424        }
 425        $this->enchanged = $this->dechanged = true;
 426    }
 427
 428    /**
 429     * Generate CTR XOR encryption key
 430     *
 431     * Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
 432     * plaintext / ciphertext in CTR mode.
 433     *
 434     * @see Crypt_TripleDES::decrypt()
 435     * @see Crypt_TripleDES::encrypt()
 436     * @access private
 437     * @param Integer $length
 438     * @param String $iv
 439     */
 440    function _generate_xor($length, &$iv)
 441    {
 442        $xor = '';
 443        $num_blocks = ($length + 7) >> 3;
 444        for ($i = 0; $i < $num_blocks; $i++) {
 445            $xor.= $iv;
 446            for ($j = 4; $j <= 8; $j+=4) {
 447                $temp = substr($iv, -$j, 4);
 448                switch ($temp) {
 449                    case "\xFF\xFF\xFF\xFF":
 450                        $iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
 451                    break;
 452                    case "\x7F\xFF\xFF\xFF":
 453                        $iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
 454                    break 2;
 455                    default:
 456                    extract(unpack('Ncount', $temp));
 457                    $iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
 458                    break 2;
 459                }
 460            }
 461        }
 462
 463        return $xor;
 464    }
 465
 466    /**
 467     * Encrypts a message.
 468     *
 469     * @access public
 470     * @param String $plaintext
 471     */
 472    function encrypt($plaintext)
 473    {
 474        if ($this->paddable) {
 475            $plaintext = $this->_pad($plaintext);
 476        }
 477
 478        // if the key is smaller then 8, do what we'd normally do
 479        if ($this->mode == self::MODE_3CBC && strlen($this->key) > 8) {
 480            $ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
 481
 482            return $ciphertext;
 483        }
 484
 485        if ( CRYPT_DES_MODE == Crypt_DES::MODE_MCRYPT ) {
 486            if ($this->enchanged) {
 487                if (!isset($this->enmcrypt)) {
 488                    $this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
 489                }
 490                mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
 491                if ($this->mode != 'ncfb') {
 492                    $this->enchanged = false;
 493                }
 494            }
 495
 496            if ($this->mode != 'ncfb') {
 497                $ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
 498            } else {
 499                if ($this->enchanged) {
 500                    $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
 501                    mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
 502                    $this->enchanged = false;
 503                }
 504
 505                if (strlen($this->enbuffer)) {
 506                    $ciphertext = $plaintext ^ substr($this->encryptIV, strlen($this->enbuffer));
 507                    $this->enbuffer.= $ciphertext;
 508                    if (strlen($this->enbuffer) == 8) {
 509                        $this->encryptIV = $this->enbuffer;
 510                        $this->enbuffer = '';
 511                        mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
 512                    }
 513                    $plaintext = substr($plaintext, strlen($ciphertext));
 514                } else {
 515                    $ciphertext = '';
 516                }
 517
 518                $last_pos = strlen($plaintext) & 0xFFFFFFF8;
 519                $ciphertext.= $last_pos ? mcrypt_generic($this->enmcrypt, substr($plaintext, 0, $last_pos)) : '';
 520
 521                if (strlen($plaintext) & 0x7) {
 522                    if (strlen($ciphertext)) {
 523                        $this->encryptIV = substr($ciphertext, -8);
 524                    }
 525                    $this->encryptIV = mcrypt_generic($this->ecb, $this->encryptIV);
 526                    $this->enbuffer = substr($plaintext, $last_pos) ^ $this->encryptIV;
 527                    $ciphertext.= $this->enbuffer;
 528                }
 529            }
 530
 531            if (!$this->continuousBuffer) {
 532                mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
 533            }
 534
 535            return $ciphertext;
 536        }
 537
 538        if (strlen($this->key) <= 8) {
 539            $this->des[0]->mode = $this->mode;
 540
 541            return $this->des[0]->encrypt($plaintext);
 542        }
 543
 544        $des = $this->des;
 545
 546        $buffer = &$this->enbuffer;
 547        $continuousBuffer = $this->continuousBuffer;
 548        $ciphertext = '';
 549        switch ($this->mode) {
 550            case Crypt_DES::MODE_ECB:
 551                for ($i = 0; $i < strlen($plaintext); $i+=8) {
 552                    $block = substr($plaintext, $i, 8);
 553                    // all of these _processBlock calls could, in theory, be put in a function - say Crypt_TripleDES::_ede_encrypt() or something.
 554                    // only problem with that: it would slow encryption and decryption down.  $this->des would have to be called every time that
 555                    // function is called, instead of once for the whole string of text that's being encrypted, which would, in turn, make
 556                    // encryption and decryption take more time, per this:
 557                    //
 558                    // http://blog.libssh2.org/index.php?/archives/21-Compiled-Variables.html
 559                    $block = $des[0]->_processBlock($block, Crypt_DES::ENCRYPT);
 560                    $block = $des[1]->_processBlock($block, Crypt_DES::DECRYPT);
 561                    $block = $des[2]->_processBlock($block, Crypt_DES::ENCRYPT);
 562                    $ciphertext.= $block;
 563                }
 564                break;
 565            case Crypt_DES::MODE_CBC:
 566                $xor = $this->encryptIV;
 567                for ($i = 0; $i < strlen($plaintext); $i+=8) {
 568                    $block = substr($plaintext, $i, 8) ^ $xor;
 569                    $block = $des[0]->_processBlock($block, Crypt_DES::ENCRYPT);
 570                    $block = $des[1]->_processBlock($block, Crypt_DES::DECRYPT);
 571                    $block = $des[2]->_processBlock($block, Crypt_DES::ENCRYPT);
 572                    $xor = $block;
 573                    $ciphertext.= $block;
 574                }
 575                if ($this->continuousBuffer) {
 576                    $this->encryptIV = $xor;
 577                }
 578                break;
 579            case Crypt_DES::MODE_CTR:
 580                $xor = $this->encryptIV;
 581                if (strlen($buffer['encrypted'])) {
 582                    for ($i = 0; $i < strlen($plaintext); $i+=8) {
 583                        $block = substr($plaintext, $i, 8);
 584                        $key = $this->_generate_xor(8, $xor);
 585                        $key = $des[0]->_processBlock($key, Crypt_DES::ENCRYPT);
 586                        $key = $des[1]->_processBlock($key, Crypt_DES::DECRYPT);
 587                        $key = $des[2]->_processBlock($key, Crypt_DES::ENCRYPT);
 588                        $buffer['encrypted'].= $key;
 589                        $key = $this->_string_shift($buffer['encrypted'], 8);
 590                        $ciphertext.= $block ^ $key;
 591                    }
 592                } else {
 593                    for ($i = 0; $i < strlen($plaintext); $i+=8) {
 594                        $block = substr($plaintext, $i, 8);
 595                        $key = $this->_generate_xor(8, $xor);
 596                        $key = $des[0]->_processBlock($key, Crypt_DES::ENCRYPT);
 597                        $key = $des[1]->_processBlock($key, Crypt_DES::DECRYPT);
 598                        $key = $des[2]->_processBlock($key, Crypt_DES::ENCRYPT);
 599                        $ciphertext.= $block ^ $key;
 600                    }
 601                }
 602                if ($this->continuousBuffer) {
 603                    $this->encryptIV = $xor;
 604                    if ($start = strlen($plaintext) & 7) {
 605                        $buffer['encrypted'] = substr($key, $start) . $buffer;
 606                    }
 607                }
 608                break;
 609            case Crypt_DES::MODE_CFB:
 610                if (!empty($buffer['xor'])) {
 611                    $ciphertext = $plaintext ^ $buffer['xor'];
 612                    $iv = $buffer['encrypted'] . $ciphertext;
 613                    $start = strlen($ciphertext);
 614                    $buffer['encrypted'].= $ciphertext;
 615                    $buffer['xor'] = substr($buffer['xor'], strlen($ciphertext));
 616                } else {
 617                    $ciphertext = '';
 618                    $iv = $this->encryptIV;
 619                    $start = 0;
 620                }
 621
 622                for ($i = $start; $i < strlen($plaintext); $i+=8) {
 623                    $block = substr($plaintext, $i, 8);
 624                    $iv = $des[0]->_processBlock($iv, Crypt_DES::ENCRYPT);
 625                    $iv = $des[1]->_processBlock($iv, Crypt_DES::DECRYPT);
 626                    $xor= $des[2]->_processBlock($iv, Crypt_DES::ENCRYPT);
 627
 628                    $iv = $block ^ $xor;
 629                    if ($continuousBuffer && strlen($iv) != 8) {
 630                        $buffer = array(
 631                                'encrypted' => $iv,
 632                                'xor' => substr($xor, strlen($iv))
 633                                );
 634                    }
 635                    $ciphertext.= $iv;
 636                }
 637
 638                if ($this->continuousBuffer) {
 639                    $this->encryptIV = $iv;
 640                }
 641                break;
 642            case Crypt_DES::MODE_OFB:
 643                $xor = $this->encryptIV;
 644                if (strlen($buffer)) {
 645                    for ($i = 0; $i < strlen($plaintext); $i+=8) {
 646                        $xor = $des[0]->_processBlock($xor, Crypt_DES::ENCRYPT);
 647                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 648                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 649                        $buffer.= $xor;
 650                        $key = $this->_string_shift($buffer, 8);
 651                        $ciphertext.= substr($plaintext, $i, 8) ^ $key;
 652                    }
 653                } else {
 654                    for ($i = 0; $i < strlen($plaintext); $i+=8) {
 655                        $xor = $des[0]->_processBlock($xor, Crypt_DES::ENCRYPT);
 656                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 657                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 658                        $ciphertext.= substr($plaintext, $i, 8) ^ $xor;
 659                    }
 660                    $key = $xor;
 661                }
 662                if ($this->continuousBuffer) {
 663                    $this->encryptIV = $xor;
 664                    if ($start = strlen($plaintext) & 7) {
 665                        $buffer = substr($key, $start) . $buffer;
 666                    }
 667                }
 668        }
 669
 670        return $ciphertext;
 671    }
 672
 673    /**
 674     * Decrypts a message.
 675     *
 676     * @access public
 677     * @param String $ciphertext
 678     */
 679    function decrypt($ciphertext)
 680    {
 681        if ($this->mode == self::MODE_3CBC && strlen($this->key) > 8) {
 682            $plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
 683
 684            return $this->_unpad($plaintext);
 685        }
 686
 687        if ($this->paddable) {
 688            // we pad with chr(0) since that's what mcrypt_generic does.  to quote from http://php.net/function.mcrypt-generic :
 689            // "The data is padded with "\0" to make sure the length of the data is n * blocksize."
 690            $ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
 691        }
 692
 693        if ( CRYPT_DES_MODE == Crypt_DES::MODE_MCRYPT ) {
 694            if ($this->dechanged) {
 695                if (!isset($this->demcrypt)) {
 696                    $this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
 697                }
 698                mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
 699                if ($this->mode != 'ncfb') {
 700                    $this->dechanged = false;
 701                }
 702            }
 703
 704            if ($this->mode != 'ncfb') {
 705                $plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
 706            } else {
 707                if ($this->dechanged) {
 708                    $this->ecb = mcrypt_module_open(MCRYPT_3DES, '', MCRYPT_MODE_ECB, '');
 709                    mcrypt_generic_init($this->ecb, $this->key, "\0\0\0\0\0\0\0\0");
 710                    $this->dechanged = false;
 711                }
 712
 713                if (strlen($this->debuffer)) {
 714                    $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($this->debuffer));
 715
 716                    $this->debuffer.= substr($ciphertext, 0, strlen($plaintext));
 717                    if (strlen($this->debuffer) == 8) {
 718                        $this->decryptIV = $this->debuffer;
 719                        $this->debuffer = '';
 720                        mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
 721                    }
 722                    $ciphertext = substr($ciphertext, strlen($plaintext));
 723                } else {
 724                    $plaintext = '';
 725                }
 726
 727                $last_pos = strlen($ciphertext) & 0xFFFFFFF8;
 728                $plaintext.= $last_pos ? mdecrypt_generic($this->demcrypt, substr($ciphertext, 0, $last_pos)) : '';
 729
 730                if (strlen($ciphertext) & 0x7) {
 731                    if (strlen($plaintext)) {
 732                        $this->decryptIV = substr($ciphertext, $last_pos - 8, 8);
 733                    }
 734                    $this->decryptIV = mcrypt_generic($this->ecb, $this->decryptIV);
 735                    $this->debuffer = substr($ciphertext, $last_pos);
 736                    $plaintext.= $this->debuffer ^ $this->decryptIV;
 737                }
 738
 739                return $plaintext;
 740            }
 741
 742            if (!$this->continuousBuffer) {
 743                mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
 744            }
 745
 746            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
 747        }
 748
 749        if (strlen($this->key) <= 8) {
 750            $this->des[0]->mode = $this->mode;
 751            $plaintext = $this->des[0]->decrypt($ciphertext);
 752            return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
 753        }
 754
 755        $des = $this->des;
 756
 757        $buffer = &$this->enbuffer;
 758        $continuousBuffer = $this->continuousBuffer;
 759        $plaintext = '';
 760        switch ($this->mode) {
 761            case Crypt_DES::MODE_ECB:
 762                for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 763                    $block = substr($ciphertext, $i, 8);
 764                    $block = $des[2]->_processBlock($block, Crypt_DES::DECRYPT);
 765                    $block = $des[1]->_processBlock($block, Crypt_DES::ENCRYPT);
 766                    $block = $des[0]->_processBlock($block, Crypt_DES::DECRYPT);
 767                    $plaintext.= $block;
 768                }
 769                break;
 770            case Crypt_DES::MODE_CBC:
 771                $xor = $this->decryptIV;
 772                for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 773                    $orig = $block = substr($ciphertext, $i, 8);
 774                    $block = $des[2]->_processBlock($block, Crypt_DES::DECRYPT);
 775                    $block = $des[1]->_processBlock($block, Crypt_DES::ENCRYPT);
 776                    $block = $des[0]->_processBlock($block, Crypt_DES::DECRYPT);
 777                    $plaintext.= $block ^ $xor;
 778                    $xor = $orig;
 779                }
 780                if ($this->continuousBuffer) {
 781                    $this->decryptIV = $xor;
 782                }
 783                break;
 784            case Crypt_DES::MODE_CTR:
 785                $xor = $this->decryptIV;
 786                if (strlen($buffer['ciphertext'])) {
 787                    for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 788                        $block = substr($ciphertext, $i, 8);
 789                        $key = $this->_generate_xor(8, $xor);
 790                        $key = $des[0]->_processBlock($key, Crypt_DES::ENCRYPT);
 791                        $key = $des[1]->_processBlock($key, Crypt_DES::DECRYPT);
 792                        $key = $des[2]->_processBlock($key, Crypt_DES::ENCRYPT);
 793                        $buffer['ciphertext'].= $key;
 794                        $key = $this->_string_shift($buffer['ciphertext'], 8);
 795                        $plaintext.= $block ^ $key;
 796                    }
 797                } else {
 798                    for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 799                        $block = substr($ciphertext, $i, 8);
 800                        $key = $this->_generate_xor(8, $xor);
 801                        $key = $des[0]->_processBlock($key, Crypt_DES::ENCRYPT);
 802                        $key = $des[1]->_processBlock($key, Crypt_DES::DECRYPT);
 803                        $key = $des[2]->_processBlock($key, Crypt_DES::ENCRYPT);
 804                        $plaintext.= $block ^ $key;
 805                    }
 806                }
 807                if ($this->continuousBuffer) {
 808                    $this->decryptIV = $xor;
 809                    if ($start = strlen($plaintext) & 7) {
 810                        $buffer['ciphertext'] = substr($key, $start) . $buffer['ciphertext'];
 811                    }
 812                }
 813                break;
 814            case Crypt_DES::MODE_CFB:
 815                if (!empty($buffer['ciphertext'])) {
 816                    $plaintext = $ciphertext ^ substr($this->decryptIV, strlen($buffer['ciphertext']));
 817                    $buffer['ciphertext'].= substr($ciphertext, 0, strlen($plaintext));
 818                    if (strlen($buffer['ciphertext']) == 8) {
 819                        $xor = $des[0]->_processBlock($buffer['ciphertext'], Crypt_DES::ENCRYPT);
 820                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 821                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 822                        $buffer['ciphertext'] = '';
 823                    }
 824                    $start = strlen($plaintext);
 825                    $block = $this->decryptIV;
 826                } else {
 827                    $plaintext = '';
 828                    $xor = $des[0]->_processBlock($this->decryptIV, Crypt_DES::ENCRYPT);
 829                    $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 830                    $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 831                    $start = 0;
 832                }
 833
 834                for ($i = $start; $i < strlen($ciphertext); $i+=8) {
 835                    $block = substr($ciphertext, $i, 8);
 836                    $plaintext.= $block ^ $xor;
 837                    if ($continuousBuffer && strlen($block) != 8) {
 838                        $buffer['ciphertext'].= $block;
 839                        $block = $xor;
 840                    } else if (strlen($block) == 8) {
 841                        $xor = $des[0]->_processBlock($block, Crypt_DES::ENCRYPT);
 842                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 843                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 844                    }
 845                }
 846                if ($this->continuousBuffer) {
 847                    $this->decryptIV = $block;
 848                }
 849                break;
 850            case Crypt_DES::MODE_OFB:
 851                $xor = $this->decryptIV;
 852                if (strlen($buffer)) {
 853                    for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 854                        $xor = $des[0]->_processBlock($xor, Crypt_DES::ENCRYPT);
 855                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 856                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 857                        $buffer.= $xor;
 858                        $key = $this->_string_shift($buffer, 8);
 859                        $plaintext.= substr($ciphertext, $i, 8) ^ $key;
 860                    }
 861                } else {
 862                    for ($i = 0; $i < strlen($ciphertext); $i+=8) {
 863                        $xor = $des[0]->_processBlock($xor, Crypt_DES::ENCRYPT);
 864                        $xor = $des[1]->_processBlock($xor, Crypt_DES::DECRYPT);
 865                        $xor = $des[2]->_processBlock($xor, Crypt_DES::ENCRYPT);
 866                        $plaintext.= substr($ciphertext, $i, 8) ^ $xor;
 867                    }
 868                    $key = $xor;
 869                }
 870                if ($this->continuousBuffer) {
 871                    $this->decryptIV = $xor;
 872                    if ($start = strlen($ciphertext) & 7) {
 873                        $buffer = substr($key, $start) . $buffer;
 874                    }
 875                }
 876        }
 877
 878        return $this->paddable ? $this->_unpad($plaintext) : $plaintext;
 879    }
 880
 881    /**
 882     * Treat consecutive "packets" as if they are a continuous buffer.
 883     *
 884     * Say you have a 16-byte plaintext $plaintext.  Using the default behavior, the two following code snippets
 885     * will yield different outputs:
 886     *
 887     * <code>
 888     *    echo $des->encrypt(substr($plaintext, 0, 8));
 889     *    echo $des->encrypt(substr($plaintext, 8, 8));
 890     * </code>
 891     * <code>
 892     *    echo $des->encrypt($plaintext);
 893     * </code>
 894     *
 895     * The solution is to enable the continuous buffer.  Although this will resolve the above discrepancy, it creates
 896     * another, as demonstrated with the following:
 897     *
 898     * <code>
 899     *    $des->encrypt(substr($plaintext, 0, 8));
 900     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
 901     * </code>
 902     * <code>
 903     *    echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
 904     * </code>
 905     *
 906     * With the continuous buffer disabled, these would yield the same output.  With it enabled, they yield different
 907     * outputs.  The reason is due to the fact that the initialization vector's change after every encryption /
 908     * decryption round when the continuous buffer is enabled.  When it's disabled, they remain constant.
 909     *
 910     * Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
 911     * encryption / decryption round, whereas otherwise, it'd remain constant.  For this reason, it's recommended that
 912     * continuous buffers not be used.  They do offer better security and are, in fact, sometimes required (SSH uses them),
 913     * however, they are also less intuitive and more likely to cause you problems.
 914     *
 915     * @see Crypt_TripleDES::disableContinuousBuffer()
 916     * @access public
 917     */
 918    function enableContinuousBuffer()
 919    {
 920        $this->continuousBuffer = true;
 921        if ($this->mode == self::MODE_3CBC) {
 922            $this->des[0]->enableContinuousBuffer();
 923            $this->des[1]->enableContinuousBuffer();
 924            $this->des[2]->enableContinuousBuffer();
 925        }
 926    }
 927
 928    /**
 929     * Treat consecutive packets as if they are a discontinuous buffer.
 930     *
 931     * The default behavior.
 932     *
 933     * @see Crypt_TripleDES::enableContinuousBuffer()
 934     * @access public
 935     */
 936    function disableContinuousBuffer()
 937    {
 938        $this->continuousBuffer = false;
 939        $this->encryptIV = $this->iv;
 940        $this->decryptIV = $this->iv;
 941
 942        if ($this->mode == self::MODE_3CBC) {
 943            $this->des[0]->disableContinuousBuffer();
 944            $this->des[1]->disableContinuousBuffer();
 945            $this->des[2]->disableContinuousBuffer();
 946        }
 947    }
 948
 949    /**
 950     * Pad "packets".
 951     *
 952     * DES works by encrypting eight bytes at a time.  If you ever need to encrypt or decrypt something that's not
 953     * a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
 954     *
 955     * Padding is enabled by default.  Sometimes, however, it is undesirable to pad strings.  Such is the case in SSH1,
 956     * where "packets" are padded with random bytes before being encrypted.  Unpad these packets and you risk stripping
 957     * away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
 958     * transmitted separately)
 959     *
 960     * @see Crypt_TripleDES::disablePadding()
 961     * @access public
 962     */
 963    function enablePadding()
 964    {
 965        $this->padding = true;
 966    }
 967
 968    /**
 969     * Do not pad packets.
 970     *
 971     * @see Crypt_TripleDES::enablePadding()
 972     * @access public
 973     */
 974    function disablePadding()
 975    {
 976        $this->padding = false;
 977    }
 978
 979    /**
 980     * Pads a string
 981     *
 982     * Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
 983     * 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
 984     *
 985     * If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
 986     * and padding will, hence forth, be enabled.
 987     *
 988     * @see Crypt_TripleDES::_unpad()
 989     * @access private
 990     */
 991    function _pad($text)
 992    {
 993        if(!$this->paddable)
 994            return $text;
 995
 996        $length = strlen($text);
 997
 998        if (!$this->padding) {
 999            if (($length & 7) == 0) {
1000                return $text;
1001            } else {
1002                user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_WARNING);
1003                $this->padding = true;
1004            }
1005        }
1006
1007        $pad = 8 - ($length & 7);
1008        return str_pad($text, $length + $pad, chr($pad));
1009    }
1010
1011    /**
1012     * Unpads a string
1013     *
1014     * If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
1015     * and false will be returned.
1016     *
1017     * @see Crypt_TripleDES::_pad()
1018     * @access private
1019     */
1020    function _unpad($text)
1021    {
1022        if (!$this->padding || !$this->paddable) {
1023            return $text;
1024        }
1025
1026        $length = ord($text[strlen($text) - 1]);
1027
1028        if (!$length || $length > 8) {
1029            return false;
1030        }
1031
1032        return substr($text, 0, -$length);
1033    }
1034
1035    /**
1036     * String Shift
1037     *
1038     * Inspired by array_shift
1039     *
1040     * @param String $string
1041     * @param optional Integer $index
1042     * @return String
1043     * @access private
1044     */
1045    function _string_shift(&$string, $index = 1)
1046    {
1047        $substr = substr($string, 0, $index);
1048        $string = substr($string, $index);
1049        return $substr;
1050    }
1051}
1052
1053// vim: ts=4:sw=4:et:
1054// vim6: fdl=1: