RoboTamer
RoboTamer

Reputation: 3514

PHP char encoding issue but only with char T, at least that is my guess

I published this class at different places but now found a bug. It has become very popular, so I was wondering if someone can help me figure this out. I been on it for hours now, and what I found is that 99% of the time the problem is with lower and upper case letter T. I have tried different encoding, and the t's change to a different char. but it's always just the t's seams like it.

Class

<?php
/**
 * An open source PHP library collection
 *
 * @category     RoboTamer
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://www.RoboTamer.com/license.html
 * @link         http://www.RoboTamer.com
 **/

/**
 * RTCrypt
 *
 * RTCrypt allows for encryption and decryption on the fly using
 * a simple but effective method.
 *
 * RTCrypt does not require mcrypt, mhash or any other PHP extension,
 * it uses only PHP.
 *
 * @category     RoboTamer
 * @package      RTCrypt
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2011, Dennis T Kaplan
 * @license      http://www.RoboTamer.com/license.html
 * @link         http://www.RoboTamer.com
 **/
class RTCrypt
{
    const streight = '012345679ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    private $scramble1 = NULL;
    private $scramble2 = NULL;

    public function __construct()
    {
        $this->setScramble1();
    }

    public function __destruct(){}

    public function getStreight()
    {
        return self::streight;
    }

    public function getScramble1()
    {
        return implode($this->scramble1);
    }

    public function getScramble2()
    {
        return implode($this->scramble2);
    }

    /**
     * Set the characters you like to replace
     *
     * @access  private
     * @param   string $str
     */
    private function setScramble1()
    {
        $this->scramble1 = str_split(self::streight);
    }

    /**
     * This is your private key.
     * You can generate a random private key based on scramble1 via
     * the randomizeString($scramble1) function.
     *
     * @access  public
     * @param   string $str
     * @return  bool TRUE
     */
    public function setScramble2($str=NULL)
    {
        if($str===NULL){
            trigger_error('No key, use genKey($str)', E_USER_ERROR );
            die;
        }
        $this->scramble2 = str_split($str);
        return TRUE;
    }

    /**
     * This will encrypt your data
     *
     * @access  public
     * @param   string $str
     * @return  string encrypt data
     */
    public function encrypt($str)
    {
        if($this->scramble2 === NULL) $this->setScramble2();
        $str = base64_encode($str);
        $len = strlen($str);
        $newstr='';
        for($i=0; $i < $len;$i++){
            $r = substr($str, -1);
            $str = substr($str, 0, -1);
            $an = array_search($r,$this->scramble1);
            if($an > 0){
                $newstr .= $this->scramble2[$an];
            }else{
                $newstr .= $r;
            }
        }
        return $newstr;
    }

    /**
     * This will decrypt a Crypted string back to the original data
     *
     * @access  public
     * @param   string $str
     * @return  string
     */
    public function decrypt($str)
    {
        if($this->scramble2 === NULL) $this->setScramble2();
        $len = strlen($str);
        $newstr='';
        for($i=0; $i < $len;$i++){
            $r = substr($str, -1);
            $str = substr($str, 0, -1);
            $an = array_search($r,$this->scramble2);
            if($an > 0){
                $newstr .= $this->scramble1[$an];
            }else{
                $newstr .= $r;
            }
        }
        $str = base64_decode($newstr);
        return $str;
    }

    /**
     * Generates your private key.
     * You would use it to set scramble2
     * Keep it save!
     *
     * @access  public
     * @return  string
     */
    public function genKey()
    {
        $array = str_split(self::streight);
        shuffle($array);
        return implode($array);
    }
}
?>

Test script:

define('BR',"\n");
Locale::setDefault('en-US');

include 'RTCrypt.php';

$Crypter = new RTCrypt();
$key = $Crypter->genKey();
echo BR.'Your Key: '.BR.$Crypter->getStreight().BR.$key.BR.BR;
$Crypter->setScramble2($key);

$str = '/**
 * encode, decode and also serialize when nessesery.
 * Works with anything that php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://RoboTamer.com/license.html
 * @link         http://RoboTamer.com
 * @todo         combine this with RTCrypt to one class
 */';
echo 'String before RTCrypt: '.BR.$str.BR.BR;

//echo BR.base64_encode($str).BR.BR;

$str = $Crypter->encrypt($str);
echo 'RTCrpted: '.$str.BR.BR;
echo 'String after RTCrypt: '.BR.$Crypter->decrypt($str);
echo BR.BR;
?>

String before RTCrypt:

/**
 * encode, decode and also serialize when nessesery.
 * Works with anything that php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @author       Dennis T Kaplan
 * @copyright    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      http://RoboTamer.com/license.html
 * @link         http://RoboTamer.com
 * @todo         combine this with RTCrypt to one class
 */

String after RTCrypt:

/**
 * encode, decode and also serialize when nessesery.
 * Works wich anyching chac php can serialize.
 * string, array, etc.
 *
 * @category     RoboTaMeR
 * @package      Strsafe
 * @auchor       Dennis T Kaplan
 * @copyrighc    Copyright (c) 2008 - 2011, Dennis T Kaplan
 * @license      hctp://RoboTamer.com/license.html
 * @link         hctp://RoboTamer.com
 * @codo         combine chis with RTCrypt co one class
 */

Upvotes: 0

Views: 130

Answers (2)

CodesInChaos
CodesInChaos

Reputation: 108840

Even when this bug is fixed, your code is utterly insecure. I see three flaws at a glance:

  1. It's a substitution cypher. An ancient method that's very weak. Character frequency counting is a simple method to beat it. Your use of Base64 complicates the attack somewhat, but not much. => Broken by design.

  2. Several characters in the Base64 output will always be mapped to themselves. Namely +, /, 8(Looks like you simply forgot this one in your string) and =. The identity mapping of = is acceptable, since it's only the end-of-string marker, but the other three are security flaws.

  3. You're using shuffle to create the private key. shuffle isn't designed for cryptographic purposes, and thus probably uses a weak random number generator.
    I wouldn't be surprised if it mapped to the c RNG which only has a 32 bit seed. That would mean that there are only 4 billion different keys, which is trivial to brute-force.

Upvotes: 1

Tom van der Woerdt
Tom van der Woerdt

Reputation: 29985

The only thing I can think of:

if($an > 0){

There can be a key on index 0. You should check for FALSE, by using:

if ($an !== FALSE)

Upvotes: 3

Related Questions