laserlight wrote:Which encryption algorithm does it implement?
A homebrew one; generate a number, and then alternately add and subtract it from successive bytes in the string. In other words, two interleaved (but related, in the sense that if you know one you can derive the other) Caesar ciphers.
--
First things first.
function chartrans($plainText)
{
return array_map('ord', str_split($plainText));
}
Using @ to suppress error messages is a poor substitute for fixing the problem that is causing the error messages.
$a[$k] = $a[$k] + $this->p[$j];
$a[$i] = $a[$i] - $this->p[$j];
// Add fudge factor to text
//
$a[$k] = $a[$k] - $factor;
$a[$i] = $a[$i] + $factor ;
Can be simplified to
$a[$k] = $a[$k] + ($this->p[$j] - $factor);
$a[$i] = $a[$i] - ($this->p[$j] - $factor);
So the "fudge factor" is simply being subtracted from each character of the password. That can be done once before all the nested looping.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
//...deciphering is much the same
}
else
{
for($r = 0; $r < $roll; $r++)
{
for($j = 0; $j<sizeof($this->p); $j++)
{
for($i = 1; $i<sizeof($a); $i += 2)
{
$k = $i - 1;
//wrap the password around text
$a[$k] = $a[$k] - $pass[$j];
$a[$i] = $a[$i] + $pass[$j];
}
}
}
}
return $a;
}
In fact the only difference between decipherment and encipherment is that the subtraction and addition of fudged password characters is swapped.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
for($i=0; $i<count($pass); $i++)
$pass[$i] = -$pass[$i];
}
for($r = 0; $r < $roll; $r++)
{
for($j = 0; $j<sizeof($this->p); $j++)
{
for($i = 1; $i<sizeof($a); $i += 2)
{
$k = $i - 1;
//wrap the password around text
$a[$k] = $a[$k] - $pass[$j];
$a[$i] = $a[$i] + $pass[$j];
}
}
}
return $a;
}
None of those nested loops depend on each other, so they can be run in any order.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
for($i=0; $i<count($pass); $i++)
$pass[$i] = -$pass[$i];
}
for($i = 1; $i<count($a); $i += 2)
{
for($j = 0; $j<count($pass); $j++)
{
for($r = 0; $r < $roll; $r++)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - $pass[$j];
$a[$i] = $a[$i] + $pass[$j];
}
}
}
return $a;
}
The inner loop is repeatedly adding the same value. That could be done with a multiplication.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
for($i=0; $i<count($pass); $i++)
$pass[$i] = -$pass[$i];
}
for($i = 1; $i<count($a); $i += 2)
{
for($j = 0; $j<count($pass); $j++)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - $pass[$j]*$roll;
$a[$i ] = $a[$i ] + $pass[$j]*$roll;
}
}
return $a;
}
Again, with the $j loop we're just repeatedly summing the same thing multiple times.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
for($i=0; $i<count($pass); $i++)
$pass[$i] = -$pass[$i];
}
for($i = 1; $i<count($a); $i += 2)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - array_sum($pass)*$roll;
$a[$i ] = $a[$i ] + array_sum($pass)*$roll;
}
return $a;
}
And that's constant within the loop
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
if($decipher)
{
for($i=0; $i<count($pass); $i++)
$pass[$i] = -$pass[$i];
}
$fudge = array_sum($pass)*$roll;
for($i = 1; $i<count($a); $i += 2)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - $fudge;
$a[$i ] = $a[$i ] + $fudge;
}
return $a;
}
That simplifies the difference between encryption and decryption.
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$pass = $this->p;
for($i=0; $i<count($pass); $i++)
$pass[$i]-=$factor;
$pass = array_sum($pass);
if($decipher)
{
$pass = -$pass;
}
$fudge = $pass*$roll;
for($i = 1; $i<count($a); $i += 2)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - $fudge;
$a[$i ] = $a[$i ] + $fudge;
}
return $a;
}
Subtracting $factor from each element of $pass and then summing the elements of $pass is equivalent to summing the elements of $pass and repeatedly subtracting $factor from the sum:
$fudge = array_sum($pass)*$roll - $factor*count($pass);
So the password, the fudge factor, and the roll size are all combined together and the combination alternately subtracted from and added to alternate characters in the plaintext message.
/*
Crypt GreyWolf is licensed under GPL
Written by Nicholas Brightwell
Please keep this header if you wish to make changes.
*/
class crypt_gw
{
var $s;
var $p;
function crypt_gw($key)
{
$this->p = $this->chartrans($key);
}
private function encipher($a, $decipher=true, $factor=1, $roll=1)
{
$fudge = array_sum($this->p)*$roll - count($this->p)*$factor;
if($decipher)
$fudge = -$fudge;
for($i = 1; $i<sizeof($a); $i += 2)
{
//wrap the password around text
$a[$i-1] = $a[$i-1] - $fudge;
$a[$i] = $a[$i] + $fudge;
}
return $a;
}
function encrypt ($message, $factor = 1,$roll = 1)
{
$asciicode = array_map('ord', str_split($plainText));
return $this->encipher($asciicode,false, $factor,$roll);
}
function decrypt ($text, $factor = 1 ,$roll = 1)
{
$encypher = $this->encipher($text,true, $factor,$roll);
return join('', array_map('chr', $encypher));
}
}
Yes, I skipped all the hex2bin and bin2hex stuff; it's all cosmetic anyway.
What sort of value does $fudge turn out to be? For the given example, where the password is "password", the fudge factor is 122 and the number of rolls is 13, it's
array_sum(array_map('ord', str_split('password')))*13-122*strlen('password') = 10503;
Which happens to explain the sort of numbers produced by the given example (-10426, 10604, -10402, 10619, -10471, 10612, -10402, 10535, -10405, ...). However, since PHP's chr() function operates modulo 256, I only need to keep the numbers modulo 256. I am going to be spectacularly lazy about how I reduce all those numbers mod 256, and use what I just said. The last line of encipher() becomes
return array_map('ord', array_map('chr', $a));
So now the numbers are more like (70, 108, 94, 123, 25, 116, 94, 39, 91, 128, 25, 123, 97, 108, 25, 107, 104, ...) and if transport encoding is required then base64_encode(join('', array_map('chr', $cipherstream))) is more elegant.
But let's take a step back, and undo that last step (even though (70, 108, ....) will decode just as well as (-10426, 10604, ...) does - try it if you don't believe me: chr(-10426)==chr(70)='F'), and have a look at some ciphertext:
-208023 208128 -207987 208193 -207996 208197 -208064 208208 -207988 208197 -207986
208212 -207975 208128 -207985 208198 -208064 208195 -207992 208193 -207986 208199
-207995 208211 -208064 208194 -207979 208212 -208064 208169 -208064 208203 -207995
208208 -207980 208128 -207980 208200 -207995 208128 -207992 208197 -207999 208196
-207995 208210 46
I like the '46' on the end: if the message is an odd number of characters long, the last one's never touched. Looks like the message ends with a full stop.
Let's say I don't know what the message is, but I want to.
Let's say I don't know what the password was, let's say I don't even know the fudge factor or how many times it was rolled. I don't need to.
All those things get bundled into a single number which is then added to or subtracted from each character in the plaintext (except maybe the last one). As already noted only their value modulo 256 matters, so instead of two interleaved groups of numbers - one consisting of values around 200,000 and the other consisting of values around -200,000 - I can deal with one group of numbers in the range [0..255].
105 0 141 65 132 69 64 80 140 69 142 84 153 0 143 70 64 67 136 65 142 71 133 83 64 66 149 84 64 41 64 75 133 80 148 0 148 72 133 0 136 69 129 68 133 82
I very much doubt that that is the message. For a start there are NUL characters. But I haven't done any fudging yet. I have to subtract and add. How much? Let's try 1:
104 1 140 66 131 70 63 81 139 70 141 85 152 1 142 71 63 68 135 66 141 72 132 84 63 67 148 85 63 42 63 76 132 81 147 1 147 73 132 1 135 70 128 69 132 83
Still doesn't look like very much to be honest. How often am I going to have to do this? Oh, no more than 255 times, tops. I could crank them all out and see if there are any that look like a message.
Such as the 32nd iteration:
73 32 109 97 100 101 32 112 108 101 110 116 121 32 111 102 32 99 104 97 110 103 101 115 32 98 117 116 32 73 32 107 101 112 116 32 116 104 101 32 104 101 97 100 101 114
Don't forget the 46!
$ciphertext='73 32 109 97 100 101 32 112 108 101 110 116 121 32 111 102 32 99 104 97 110 103 101 115 32 98 117 116 32 73 32 107 101 112 116 32 116 104 101 32 104 101 97 100 101 114 46';
echo join('', array_map('chr', explode(' ', $ciphertext)));