Reputation: 3514
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
Reputation: 108840
Even when this bug is fixed, your code is utterly insecure. I see three flaws at a glance:
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.
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.
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
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