Raw code | Youtube demonstration
Note: this is NOT SECURE ENCRYPTION. It is fun to watch and play with, but it is NOT SECURE.
#!/usr/bin/env php
<?php
$doSubstitution = true;
function resetSpeed() {
global $speed, $speedslowfactor, $speedmin;
$speed = 400;
$speedslowfactor = 1.1;
$speedmin = 10;
}
resetSpeed();
$encrypt = true;
echo "Encrypt (enter E) or decrypt (enter D)? ";
$encryptInput = stream_get_line(STDIN, 1024, PHP_EOL);
if ($encryptInput == 'd' || $encryptInput == 'D') {
$encrypt = false;
}
echo "Input password, end with enter. Press Ctrl+D to abort.\n";
$password = stream_get_line(STDIN, 1024, PHP_EOL);
if (!$password) {
die("Ctrl+D - exiting\n");
}
echo "Input message, end with enter. Press Ctrl+D to abort.\n";
$message = stream_get_line(STDIN, 1024, PHP_EOL);
if (!$message) {
die("Ctrl+D - exiting\n");
}
$closestSquare = ceil(sqrt(strlen($message)));
$padding = '';
for ($i = 0; $i < pow($closestSquare, 2) - strlen($message) - 1; $i++) {
$padding .= chr(rand(32, 126));
}
if (strlen($padding) > 0) {
$padding = ' ' . $padding;
}
$message .= $padding;
if ($encrypt) {
echo "Starting encryption. Our square is:\n";
displaySquare($message, $closestSquare);
echo "Expanding password... ";
$password = keyExpansion($password);
echo "New password: $password\n";
if ($doSubstitution) {
echo "Applying password to each character...\n";
for ($i = 0; $i < strlen($message); $i++) {
$passindex = $i % strlen($password);
$passchar = $password[$passindex];
$newchar = chr((((ord($message[$i]) - 32) + (ord($passchar) - 32) + $i) % 95) + 32);
echo "Char $i (" . $message[$i] . ', #' . (ord($message[$i]) - 32) . ") plus password char $passindex ($passchar, #" . (ord($passchar) - 32) . ") plus position $i results in $newchar (#" . (ord($newchar) - 32) . ")\n";
$message[$i] = $newchar;
displaySquare($message, $closestSquare);
}
resetSpeed();
echo "Done applying character substitution. Now let's shift our square around!\n";
}
else {
echo "Skipping substitution as requested...\n";
}
$sq = strToSq($message, $closestSquare);
for ($i = 0; $i < strlen($password) - 1; $i += 2) {
$passchars = $password[$i] . $password[$i + 1];
$passvalue = (ord($password[$i]) - 32) * 96 + ord($password[$i + 1]) - 32;
$dir = $passvalue & bindec('000000000001');
$horizshift = $passvalue & bindec('000000000010') >> 1;
$row = $passvalue & bindec('000001111100') >> 2;
$amount = $passvalue & bindec('111110000000') >> 7;
$amount += $i;
$row %= $closestSquare;
$dir = $dir > 0 ? 1 : -1;
$horizshift = $horizshift > 0;
$amount %= $closestSquare;
$amount *= $dir;
$rowcol = $horizshift ? 'row' : 'column';
echo "Password chars $i and " . ($i + 1) . " ($passchars) make #" . ($passvalue - 32) . " which give us $rowcol $row.\nWe will shift $amount times.\n";
if ($horizshift) {
$original = $sq[$row];
for ($j = 0; $j < $closestSquare; $j++) {
$index = ($j + $amount) % $closestSquare;
if ($index < 0) {
$index += $closestSquare;
}
$sq[$row][$j] = $original[$index];
}
}
else {
$original = [];
for ($j = 0; $j < $closestSquare; $j++) {
$original[] = $sq[$j][$row];
}
for ($j = 0; $j < $closestSquare; $j++) {
$index = ($j + $amount) % $closestSquare;
if ($index < 0) {
$index += $closestSquare;
}
$sq[$j][$row] = $original[$index];
}
}
displaySquare2($sq);
}
echo "All done!\n";
echo "Result: ";
for ($x = 0; $x < $closestSquare; $x++) {
for ($y = 0; $y < $closestSquare; $y++) {
echo $sq[$y][$x];
}
}
echo "\n";
}
else {
echo "Starting decryption. Our square is:\n";
$sq = strToSqRev($message, $closestSquare);
displaySquare2($sq);
$originalPassword = $password; // We need this later for substitution
$password = keyExpansion($password);
echo "Expanding password... new password: $password\n";
$actionqueue = [];
for ($i = 0; $i < strlen($password) - 1; $i += 2) {
$passchars = $password[$i] . $password[$i + 1];
$passvalue = (ord($password[$i]) - 32) * 96 + ord($password[$i + 1]) - 32;
$dir = $passvalue & bindec('000000000001');
$horizshift = $passvalue & bindec('000000000010') >> 1;
$row = $passvalue & bindec('000001111100') >> 2;
$amount = $passvalue & bindec('111110000000') >> 7;
$amount += $i;
$row %= $closestSquare;
$dir = $dir > 0 ? 1 : -1;
$amount %= $closestSquare;
$amount *= $dir;
$actionqueue[] = [-$amount, $horizshift, $row, $dir, $passchars, $passvalue];
}
$actionqueue = array_reverse($actionqueue);
$i = 0;
foreach ($actionqueue as $action) {
$amount = $action[0];
$horizshift = $action[1];
$row = $action[2];
$dir = $action[3];
$passchars = $action[4];
$passvalue = $action[5];
$horizshift = $horizshift > 0 ? true : false;
$rowcol = $horizshift ? 'row' : 'column';
echo "Password chars $i and " . ($i + 1) . " ($passchars) make #" . ($passvalue - 32) . " which give us $rowcol $row.\nWe will shift $amount times.\n";
$i++;
if ($dir > 0) {
$original = $sq[$row];
for ($j = 0; $j < $closestSquare; $j++) {
$index = ($j + $amount) % $closestSquare;
if ($index < 0) {
$index += $closestSquare;
}
$sq[$row][$j] = $original[$index];
}
}
else {
$original = [];
for ($j = 0; $j < $closestSquare; $j++) {
$original[] = $sq[$j][$row];
}
for ($j = 0; $j < $closestSquare; $j++) {
$index = ($j + $amount) % $closestSquare;
if ($index < 0) {
$index += $closestSquare;
}
$sq[$j][$row] = $original[$index];
}
}
displaySquare2($sq);
}
$message = '';
foreach ($sq as $y) {
$message .= implode('', $y);
}
if ($doSubstitution) {
resetSpeed();
echo "Done shifting things around. Now reversing character substitution...\n";
// Apply substitution
for ($i = 0; $i < strlen($message); $i++) {
$passindex = $i % strlen($password);
$passchar = $password[$passindex];
$newchar = chr(mod(((ord($message[$i]) - 32) - (ord($passchar) - 32) - $i), 95) + 32);
echo "Char $i (" . $message[$i] . ', #' . (ord($message[$i]) - 32) . ") minus password char $passindex ($passchar, #" . (ord($passchar) - 32) . ") minus position $i results in $newchar (#" . (ord($newchar) - 32) . ")\n";
$message[$i] = $newchar;
displaySquare($message, $closestSquare);
}
}
else {
echo "Skipping substitution as requested\n";
}
// Done!
echo "Done! Result: $message\n";
echo "(Note that the last few characters are probably garbage.)\n";
}
function mod($n, $m) {
$n = $n % $m;
if ($n > 0)
return $n;
return ($n + abs($m)) % $m;
}
// Same as strToSq() except this reverts the x and y axis
function strToSqRev($str, $size) {
$sq = [];
for ($x = 0; $x < $size; $x++) {
$tmpstr = substr($str, $x * $size, $size);
for ($y = 0; $y < strlen($tmpstr); $y++) {
if (!isset($sq[$y])) {
$sq[$y] = [];
}
$sq[$y][$x] = $tmpstr[$y];
}
}
return $sq;
}
function strToSq($str, $size) {
$sq = [];
for ($y = 0; $y < $size; $y++) {
$tmpstr = substr($str, $y * $size, $size);
$sq[$y] = [];
for ($x = 0; $x < strlen($tmpstr); $x++) {
$sq[$y][$x] = $tmpstr[$x];
}
}
return $sq;
}
function displaySquare($message, $size) {
global $speed, $speedslowfactor, $speedmin;
$outstr = '';
for ($y = 0; $y < $size; $y++) {
$str = substr($message, $y * $size, $size);
for ($i = 0; $i < strlen($str); $i++) {
$outstr .= $str[$i] . ' ';
}
$outstr .= "\n";
}
echo $outstr . "\n";
usleep($speed * 1000);
$speed = max($speedmin, $speed / $speedslowfactor);
}
function displaySquare2($sq) {
global $speed, $speedslowfactor, $speedmin;
$outstr = '';
foreach ($sq as $y) {
foreach ($y as $x) {
$outstr .= $x . ' ';
}
$outstr .= "\n";
}
echo $outstr . "\n";
usleep($speed * 1000);
$speed = max($speedmin, $speed / $speedslowfactor);
}
function keyExpansion($password) {
$password .= $password;
$originalPassword = $password;
$i = ord($password[0]) + 1;
while (strlen($originalPassword) > 1) {
$password .= str_repeat($originalPassword . chr(($i % 95) + 32), strlen($originalPassword));
$originalPassword = substr($originalPassword, 1);
$i++;
}
$a = 43690;
for ($i = 0; $i < strlen($password) - 1; $i++) {
$password[$i] = chr((((ord($password[$i]) ^ $a) + $i) % 95) + 32);
if ($a == 43690) {
$a = 21845;
}
else {
$a = 43690;
}
}
return $password;
}