Jump to content
MakeWebGames

Let's have some fun


a_bertrand

Recommended Posts

This is not really a contest, but it's like an interactive way to study some features of PHP as well a coding in general. Instead of me giving out all directly, we shall see who can bring what, and maybe actually come to something better that I would have done myself.

For that, we start with something... rather simple... Let's "crypt" a text by replacing each letter by another one. So if I give you "Hello there" you shall transform it to something else like "Abjji kablb" so a simple replace A with X and B with C and whatever.

Your goal is to post on this thread your solution to crypt the text with this simple aglorithm. For simplicity the source text is a fixed string defined within the code.

Code must be done in PHP (any version). The cleaner and yet shorter the better. If you dare to explain you reasoning behind the code, then do it ;)

Notes:

- Using rot13 is not acceptable, as the cipher should be changed as wished. Like a "key".

- Crypt or any one way functions are not what we look after so don't look for MD5 or anything similar. We want to be able to get as easily the original text if we know the cipher.

- Keep the crypt / cipher simple. Always replace an A with the same letter... We may come to something more complex later on, but for the shake of this exercise let's keep the things simple.

Link to comment
Share on other sites

<?php
$string = 'Hello World';

$key = array();
$key['general'] = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',' ');
$key['replace'] = array('b','e','d','g','i','k','l','m','z','x','w','v','o','q','p','s','t','y','u','v','a','f','g','h','j','c',' ');

Class Cryptography	{	
public function crypt($string)		{$str = str_split($string);$nstr = '';foreach($str as $letter){$nstr .= str_ireplace($key['replace'], $key['general'], $letter);}return urlencode(base64_encode($nstr));}
public function decrypt($string)	{$str = base64_decode(urldecode($string));$str = str_split($str);$nstr = '';foreach($str as $letter){$nstr .= str_ireplace($key['general'], $key['replace'], $letter);}return $nstr;}
}
$Cryptography = new Cryptography;

$crypt = $Cryptography->crypt($string);
echo '<strong>Original Text:</strong> '.$string.'<br />';
echo '<strong>Crypted:</strong> '.$crypt.'<br />';
echo '<strong>De Crypted:</strong> '.$Cryptography->decrypt($crypt);

?>

 

Simple, took me 10 minute's. My first submission. Will most probably submit another more efficient & better way.

Link to comment
Share on other sites

Not bad, and congratulations for the first post, but can be done in a much simpler way. For example, why do a str_split, and why use urlencode / urldecode?

Also... your code doesn't actually run... you need a "global $key" set in the crypt / decrypt functions for me.

And the decrypt doesn't seems to get back the original value...

Original Text: Hello WorldCrypted: SGJ3d20ga21yd3o%3DDe Crypted: acgga gajgc

Link to comment
Share on other sites

Source:

[octarine@jas ~/tmp]$ cat test.php 
<?php

$alphabet   = 'abcdefghijklmnopqrstuvwxyz';
$key        = 'vwxyzutsrqponmlkjihgfabcde';
$plain_text = 'hello world';

/**
 * Encode a string by simple letter substitution
 **/
$cipher_text = strtr($plain_text, $alphabet, $key);

/**
 * Decode a string by simple letter substitution
 **/
$final_text = strtr($cipher_text, $key, $alphabet);

echo "plain_text  : $plain_text\n";
echo "cipher_text : $cipher_text\n";
echo "final_text  : $final_text\n";

 

Usage:

[octarine@jas ~/tmp]$ php -f test.php 
plain_text  : hello world
cipher_text : szool blioy
final_text  : hello world

 

strtr() is the key to making extremely simple (but ultimately highly insecure) substitutions like this.

Link to comment
Share on other sites

Only insecure in-as-much as the resulting crypt text is prone to very basic frequency analysis.

There is one unusual problem that does arise if the input is a multi-byte/binary string that is used to produce crypt text that is either stored without suitable escaping in a database or displayed again without correct escaping onto a web-page. Carefully selected inputs can therefore result in SQLi or XSS attack vectors, however as you all correctly escape everything this problem will of course never arise.

For storing passwords, this type of substitution is *not* recommended even on localhost setups. It is simply a character substitution function which does have its uses. Saying that, a quick look at my own rather code, I can't find anywhere I've used it so perhaps it's not that common a function.

As for what language it can be done in - all current commercial languages to my knowledge. There are a few like whitespace where it would seem unlikely though I will stand corrected if somebody cares to waste their time.

Edited by Octarine
Link to comment
Share on other sites

The frequency analysis was the next step of my little game, so don't spoil the fun please ;)

And thanks for the clarification. Even if I shall spank you a bit, by giving right away the shortest possible code I think of!

Still, the game can continue with a less compact code. str_replace and equivalent will hardly work guys... I let you find why...

Link to comment
Share on other sites

Tested,works, simple, yet more complicated;

 

<?php
function my_encode($string) {
$letters = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY);
foreach($letters as $letter) {
$intletter = ord($letter);
if($intletter != 32) { $intletter +=1; }
$newletter = chr($intletter);
$return .= $newletter;
}
return $return;
}
function my_decode($string) {
$letters = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY);
foreach($letters as $letter) {
$intletter = ord($letter);
if($intletter != 32) { $intletter -=1; }
$newletter = chr($intletter);
$return .= $newletter;
}
return $return;
}

$string = 'Hello world';
$encoded = my_encode($string);
$decoded = my_decode($encoded);

echo 'Plain text: '. $string;
echo '<br />Cyphered text: '. $encoded;
echo '<br />De-Cyphered text: '. $decoded;

 

Strange, there are br's in their, but they don't seem too show

Link to comment
Share on other sites

An interesting gotcha that has to the best of my knowledge caught out at least two MWG regulars today...

Source

[octarine@jas ~/tmp]$ cat test.php 
<?php

$translate = array
(
   'a' => 'a', 'b' => 'f', 'c' => 'k', 'd' => 'o', 'e' => 's', 'f' => 'w',
   'g' => 'b', 'h' => 'g', 'i' => 'l', 'j' => 'p', 'k' => 't', 'l' => 'x',
   'm' => 'c', 'n' => 'h', 'o' => 'm', 'p' => 'q', 'q' => 'u', 'r' => 'y',
   's' => 'd', 't' => 'i', 'u' => 'n', 'v' => 'r', 'w' => 'v', 'x' => 'z',
   'y' => 'e', 'z' => 'i',
);

// split into two parts and cache
$search = array_keys($translate);
$replace = array_values($translate);

$plain_text = 'hello world';
$crypt_text = str_replace($search, $replace, $plain_text);
$final_text = str_replace($replace, $search, $crypt_text);

echo "plain text : $plain_text \n";
echo "crypt text : $crypt_text \n";
echo "final text : $final_text \n";

Usage

[octarine@jas ~/tmp]$ php -f test.php
plain text : hello world 
crypt text : gdiim vmeim 
final text : ustto woyto 

Huh? The code looks okay, until you actually read the docs (RTFM for all you luddites) - which clearly states

Because str_replace() replaces left to right, it might replace a previously inserted value when doing multiple replacements.

Which shows that it pays to read the manual in detail for each and every function you encounter.

Using a for loop however would work, although in reality there is nothing to beat strtr() simply as it is written in C and thus operates at full machine speed. strtr() itself could be optimized a little, but the gain in speed is negligible and not worth considering.

Edited by Octarine
Link to comment
Share on other sites

I made a little script which opens the door for the next step of our little game:

 

<html>
<head>
<title>Simple Crypt / Decrypt tool</title>
</head>
<body>
<?php

$alphabet = "abcdefghijklmnopqrstuvwxyz";

// Generates a cipher key based on a partial key (like a word)
function BuildKey ($pass)
{
   global $alphabet;

   $pass = str_replace(" ", "", $pass);

   $key = "";
   for ($i = 0; $i < strlen($pass); $i ++)
       if (strpos($key, $pass[$i]) === false)
           $key .= $pass[$i];
   for ($i = 0; $i < strlen($alphabet); $i ++)
       if (strpos($key, $alphabet[$i]) === false)
           $key .= $alphabet[$i];
   return $key;
}

// Generates a random cipher key
function GenKey ()
{
   global $alphabet;

   $key = "";

   while (strlen($key) < strlen($alphabet))
   {
       $c = $alphabet[rand(0, strlen($alphabet) - 1)];
       if (strpos($key, $c) === false)
           $key .= $c;
   }

   return $key;
}

function CryptText ($key, $text)
{
   $result = "";
   for ($i = 0; $i < strlen($text); $i ++)
   {
       $c = ord($text[$i]);
       // A - Z
       if ($c >= 65 && $c <= 90)
           $result .= strtoupper($key[$c - 65]);
       else if ($c >= 97 && $c <= 122)
           $result .= $key[$c - 97];
       else
           $result .= $text[$i];
   }
   return $result;
}

function DecryptText ($key, $text)
{
   $result="";
   for ($i = 0; $i < strlen($text); $i ++)
   {
       $c = ord($text[$i]);
       $p = strpos($key, $text[$i]);
       // A - Z
       if ($c >= 65 && $c <= 90)
           $result .= chr(65 + $p);
       else if ($c >= 97 && $c <= 122)
           $result .= chr(97 + $p);
       else
           $result .= $text[$i];
   }

   return $result;
}

// Clean the "magic" quotes if any as they are just counter productive.
if (get_magic_quotes_gpc() != 0)
{
   foreach ($_GET as $key => $value)
       $_GET[$key] = stripcslashes($value);

   foreach ($_POST as $key => $value)
   {
       if(!is_array($value))
           $_POST[$key] = stripcslashes($value);
   }
}

if (isset($_POST["action"]) && $_POST["action"] == "Crypt")
{
   if (trim($_POST["key"]) == "")
       $_POST["key"] = GenKey();

   $_POST["key"] = BuildKey($_POST["key"]);

   $_POST["text"] = CryptText($_POST["key"], $_POST["text"]);
}

if(isset($_POST["action"]) && $_POST["action"] == "De-Crypt")
{
   $_POST["key"] = BuildKey($_POST["key"]);
   $_POST["text"] = DecryptText($_POST["key"], $_POST["text"]);
}

if (count($_POST) > 0)
{
   $_POST["key"] = BuildKey($_POST["key"]);
}

if (! isset($_POST["text"]))
   $_POST["text"] = "IYH wpodi svjsu jd iyh lovv pd iyh oaahtxvr-cjjt jm Tonot Icgbijd'a
moaypjdoxvh asyjjv yon tocuhn iyh yjgc mjc npatpaapjd.

Wcjgka jm chaivhaa, odbpjga kgkpva aijjn oxjgi iyh okocithdi, jc
lhch woiyhchn oi iyh lpdnjla, loisypdw iyh copd iyoi yon xhhd
movvpdw pd sjkpjga ayjlhca apdsh tjcdpdw. Ovv lhch howhc ij wj, rhi
djdh nochn xcofh iyh aijct.

Gdnhc iyh aijdh ocsylor jm iyh hdicodsh ij iyh oaahtxvr-yovv, o
wcjgk jm mjgc topnhda aijjn syoiipdw, okoci mcjt iyh chai, loisypdw
iyh copd, odn ptkoiphdi mjc pia shaaoipjd.

\"P udjl tr moiyhc lpvv hpiyhc ahdn tr xcjiyhc, jc sjth mjc th
yptahvm,\" aopn Yhvhd Vh Wcodnh, \"aj P dhhn dji mhoc iyh copd.\" Iyhd,
igcdpdw ij iyh ajmi-hrhn Ehlhaa lyj aijjn xr yhc apnh, ayh onnhn,
\"Lyhd iyh soccpowh sjtha, Vhoy, rjg sod iouh o ahoi lpiy th. P'vv
ahh iyoi rjg och aomhvr nhkjapihn oi yjth.\"

\"Iyodu rjg, Yhvhd, xgi pi ljd'i ygci th ij lovu. Djiypdw ygcia
th--Vhoy Tjcnhsop iyh nhakpahn.\" Iyhd, ofhcipdw yhc mosh, iyh rjgdw
wpcv woqhn oxaicosihnvr pdij iyh aichhi, odn xhwod ygttpdw pd o vjl
ijdh.

Ij iyhah ljcna jm iyh rjgdw Ehlhaa iyhch loa dj chkvr. O shciopd
ajci jm htkyoapa pd yhc giihcodsh ahhthn ij mjcxpn odr pdzgpcr, odn
apvhdsh odr ljcn jm shdagch iyoi tpwyi ocpah ij iyh vpka jm yhc
sjtkodpjda.

\"Yjl thod jm th, dji ij jmmhc o ahoi pd iyh soccpowh ij Vpqqph
Yhocilhvv, ijj,\" iyjgwyi Yhvhd omihc o tjthdi'a chmvhsipjd; \"xgi P
nochn dji, jd ossjgdi jm tr xcjiyhc, lyj yoa aj chkhoihnvr gcwhn th
ij touh hzgova jdvr jm iyh cpsy. Yh vpiivh udjla yjl P vjfh Vpqqph
Yhocilhvv, odn lyhiyhc ayh xh cpsy jc kjjc P udjl dji, dhpiyhc nj P
soch.\"

\"P aor, wpcva,\" oi vhdwiy xcjuh iyh apvhdsh, oa iyh mjgciy thtxhc jm
iyh wcjgk, Xhciyo Vhfr, o Ehlhaa ijj, akjuh jgi, \"iypdu yjl aigkpn P
ot. Totto yoa kcjtpahn th o atovv iho-kocir ij-tjccjl dpwyi, odn
iypa lchisyhn copd yon lhvv-dpwy sogahn th ij mjcwhi pi; xgi, iyodu
mjcigdh, pi'a wpfpdw lor o vpiivh, odn torxh lh ayovv ovv whi yjth
omihc olypvh. P't nhakhcoihvr ygdwcr! Jm sjgcah, rjg lpvv ovv
kcjtpah th ij sjth, odn P ayovv hbkhsi rjg.\" Iyhd, igcdpdw ij Yhvhd,
ayh aopn, \"Ljd'i rjg?\"";

echo "<form method='post'>\n";
echo "<table width='100%'>\n";
echo "<tr><td width='1%'>Key:</td><td><input type='text' name='key' value='" . (isset($_POST["key"]) ? htmlentities($_POST["key"]) : "") . "' style='width: 100%;'></td></tr>\n";
echo "<tr valign='top'><td>Text:</td><td><textarea name='text' rows='10' style='width: 100%'>" . (isset($_POST['text']) ? htmlentities($_POST['text']) : "") . "</textarea></td></tr>\n";
echo "</table>\n";
echo "<br><br>";
echo "<input type='submit' value='Crypt' name='action'>\n";
echo "<input type='submit' value='De-Crypt' name='action'>\n";
?>
</body>
</html>

 

This tool should be able to create automatically cipher keys if you don't set one, or make a repeatable one if you put just one word and not a complete key. Would allow to use only a "key word" to crypt / decrypt texts and this was used long time ago (if I remember right it's called the Cesare cipher).

I didn't used the strtr function on purpose to show how you could make it without (it's not the fastest way, but shows the idea behind I hope).

As you can see, there is also some... encrypted text (if you start the PHP page it will appear in the text area). The text is from an old book, not from me. So the text step of the little fun is to try to decrypt it, without me giving you the key. (Octarine I know you can do it in no time, so don't spoil the fun and PM me when you do it ;) )

To help you with this job, I prepared you a text file. The first 26 lines of it contains a letter and separated by a coma (,) a number, this let you find the freqency of the letters in part of the book where I extracted the text. After the first 26 lines, you will find a list of words, which should help you build like a dictionary. The file (zipped) can be downloaded here: [ATTACH]600[/ATTACH]

I will share my code to reach the goal later on (both in C# and in PHP if you are interested).

Again, please post your ideas, questions, discuss it and even propose your solution here. Again Octarine, please refeign to post at day 0 the full solution otherwise you will kill the game ;) if you want send it to me as PM. Or wait a bit and share later on.

This "game" has a couple of scope in my mind, share a few code tech / functions that some of you may not know. Issues that you may not be aware (like the one octarine told that you can't crypt via str_replace and such), and learn how cryptography is not always secure as well as have some fun together with what we all love: coding.

dict.zip

Link to comment
Share on other sites

Beside Octarine which solved the thing (manually at least, as he didn't sent me a code for it), it seems I lost the audience. Too complex? No clues where to start?

Ok I will give some hints then:

- Sort the frequency table and use the 2 most used letters. You may then check the frequency of letters in the crypted document and guess that the 2 most used letters correspond to the 2 most used letters in our original frequency table. That shall solve already 2 letters over the possible 26 to search (I don't remember if the fragment I gave do have all the 26 letters could be that some are not even present).

- Then, you could check with the help of the dictionary to find unique possible words with the known letters, for example, boo? where you know b and o, may lead to a very limited number of words... Maybe just one? If this is the case you find a new letter and you can start the process again with all the letters you know.

By doing that you will end up solving the whole encrypted document. Of course this works ONLY for texts you know the language, and which are big enough to be cracked via frequency and have words you can extract.

With all this knowledge you should be able to make the code now ;)

I will leave you a couple more days then give you my answer if nobody make it.

Link to comment
Share on other sites

I've tried a number of different ways of doing this using the following frequency tables

Table #1 -- From the first 26 lines (letter:frequency) of dict.txt
Table #2 -- From the remainder of the words in dict.txt
Table #3 -- A combination of tables 1 & 2
Table #4 -- A well known letter frequency based on the analysis of 10,000 English texts.
Table #5 -- My own table based on the above four, then altered by guessing

 

The results

Table #1 - tne ysoit cdacv ai tne fodd si tne ohhelbdg-raal aw louol trmjtai'h
Table #2 - eta moice hlnhw nc eta uill oc eta irrasvlf-dnns ny sipis edgxenc'r
Table #3 - tne ysoit cdacv ai tne fodd si tne ohhelbdg-raal aw louol trmjtai'h (Same as #1)
Table #4 - tie fhost wdawv as tie modd hs tie orrelbdy-naal ag louol tncqtas'r
Table #5 - (I won't publish yet, but it yields the correct plain text)

 

So using the provided dictionary is in itself not sufficient to crack the text, however with a little manual adjustment of your frequency table you can solve it by looking for common bigrams, trigrams and making the occasional education guess and/or leap of faith.

It ~may~ be possible to brute force a letter frequency, but the effort and time involved far exceeds that of simply manually adjusting the letter frequency by hand; think of it like solving an anagram or a crossword puzzle.

Link to comment
Share on other sites

  • 2 weeks later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...