123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- #!/usr/bin/php
- <?php
- /*
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- const SNAME='decrypt';
- const SVERS='0.2';
- mb_internal_encoding('UTF-8');
- $conf=[
- 'phrase'=>null,
- 'encrypt'=>false,
- 'method'=>0,
- 'write'=>false,
- 'norm'=>false,
- 'progress'=>0,
- 'substabfp'=>'substable.txt',
- 'solsfp'=>'solutions.txt',
- 'dictfps'=>[],
- 'debug'=>false
- ];
- $help=
- 'SYNOPSIS
- '.SNAME.' -d <dictionary file> <phrase>
- DESCRIPTION
- This is '.SNAME.' v'.SVERS.', a CLI PHP script that can help decrypting
- phrases encrypted with any algorithm producing an encrypted phrase where each
- symbol always replaces the same, different symbol from the clear text one.
- COMMAND LINE OPTIONS
- -d, --dictfp <dictionary file>
- Load a dictionary file. It has to be a simple text file with one word on each
- line. You have to specify at least one, and you can specify more than one
- prepending «-d» or «--dictfp» to each file path.
- Currently '.SNAME.' comes with two xz compressed dictionary files:
- «english.txt.xz» and «italiano.txt.xz». To use any of them, you have to
- decompress it with the xz program. They both are dumps from the corresponding
- aspell dictionaries, with each lemma declined in all its forms. You can
- get dumps for other languages with something like this:
- aspell -d <lang code> dump master | aspell -l <lang code> expand 4
- (You may need to further process the above command output with tr, grep, sed
- and the likes, in order to have a functional dictionary file).
- -n, --normalize
- “Normalize” each dictionar(y|ies) entry replacing every accented or otherwise
- “signed” character with its “plain” form, and removing possible duplicated
- entries. For example, this will substitute every “è” character with “e”.
- -e, --encrypt
- Encrypt the given phrase. Useful for testing purposes. By default, or when
- «-m» or «--method» is set to «0», it encrypts the phrase by simply switching
- every source character with its subsequent character in the UTF-8 table. With
- «-m» or «--method» set to «1», it encrypts the phrase using a random
- substition table.
- -m, --method <0|1>
- Choose a method for encryption when «-e» or «--encrypt» is specified (see
- previous option description). It defaults to “'.$conf['method'].'”.
- -p, --progress <0|1|2>
- With «0», show no progress during script execution. With «1», show bare
- minimum progress. With «2», show detailed progress (this consumes more CPU
- and RAM). Defaults to «'.$conf['progress'].'».
- -w, --write
- Write any substition table '.SNAME.' may find into a «'.$conf['substabfp'].'»
- file and any solution '.SNAME.' may find into a «'.$conf['solsfp'].'» file.
- Both files will be written in current working directory. Note that possible
- existing files with the same name will be overwritten.
- -h, --help
- Show this help text and exit.
- EXAMPLES
- decrypt "ipx nvdi xppe xpvme b xppedivdl divdl?" -d english.txt -p 1 -w
- With the bundled «english.txt» dictionary, this will produce 68 possible
- solutions, with «how much wood would a woodchuck chuck?» being one of them,
- showing bare minimum progress during the execution, writing substitution
- tables into «'.$conf['substabfp'].'» and solutions into «'.$conf['solsfp'].'» within
- current working directory.
- DISCLAIMER AND LICENSE
- This program comes with ABSOLUTELY NO WARRANTY; for details see the source.
- This is free software, and you are welcome to redistribute it under certain
- conditions; see <http://www.gnu.org/licenses/> for details.';
- // https://www.codiceedizioni.it/lccc-la-cura-cripto-contest/
- // 4E E1V VXVTHM VP SRR 0AY DA23I5VO L 2W4T WL 9ONA1Z K5 HIDUFJ 88LC NZD4 S50B.
- // this is almost certainly not encrypted by just replacing each source symbol
- // occurrence with the same different symbol
- // sempre caro mi fu quest'ermo colle
- // tfnqsf dbsp nj gv rvftu'fsnp dpmmf
- // e il naufragar m'è dolce in questo mare
- // f jm obvgsbhbs n'é epmdf jo rvftup nbsf
- // 6 v0 7òôõíò3òí x'à êú0ø6 v7 ûô6hwú xòí6 (random substitution table)
- // how much wood would a woodchuck chuck?
- // ipx nvdi xppe xpvme b xppedivdl divdl?
- for ($i=1; $i<$argc; $i++) {
- if ($argv[$i]=='-h' || $argv[$i]=='--help') {
- echo "{$help}\n";
- exit(0);
- } elseif ($argv[$i]=='--makereadme') {
- file_put_contents(__DIR__.'/README.md',"```text\n{$help}\n```\n");
- exit(0);
- } elseif ($argv[$i]=='-e' || $argv[$i]=='--encrypt') {
- $conf['encrypt']=true;
- } elseif ($argv[$i]=='-d' || $argv[$i]=='--dictfp') {
- if ($i<$argc-1) {
- $i++;
- $conf['dictfps'][]=$argv[$i];
- } else {
- edie("«{$argv[$i]}» requires an argument (use «-h» or «--help» for help); shutting down.\n",1);
- }
- } elseif ($argv[$i]=='-m' || $argv[$i]=='--method') {
- if ($i<$argc-1) {
- if (in_array($argv[$i+1],['0','1']))
- $conf['method']=$argv[$i+1]+0;
- else
- edie("«{$argv[$i+1]}» is not a valid argument for «{$argv[$i]}» (use «-h» or «--help» for help); shutting down.\n",1);
- $i++;
- } else {
- edie("«{$argv[$i]}» requires an argument (use «-h» or «--help» for help); shutting down.\n",1);
- }
- } elseif ($argv[$i]=='-w' || $argv[$i]=='--write') {
- $conf['write']=true;
- } elseif ($argv[$i]=='-n' || $argv[$i]=='--norm') {
- $conf['norm']=true;
- } elseif ($argv[$i]=='-p' || $argv[$i]=='--progress') {
- if ($i<$argc-1) {
- if (in_array($argv[$i+1],['0','1','2']))
- $conf['progress']=$argv[$i+1]+0;
- else
- edie("«{$argv[$i+1]}» is not a valid argument for «{$argv[$i]}» (use «-h» or «--help» for help); shutting down.\n",1);
- $i++;
- } else {
- edie("«{$argv[$i]}» requires an argument (use «-h» or «--help» for help); shutting down.\n",1);
- }
- } elseif (is_null($conf['phrase'])) {
- $conf['phrase']=trim($argv[$i]);
- } else {
- edie("a phrase has already been specified and «{$argv[$i]}» is not a known option (use «-h» or «--help» for help); shutting down.\n",1);
- }
- }
- //print_r($conf); pause();
- if (is_null($conf['phrase']))
- edie("you have not specified a phrase (use «-h» or «--help» for help); shutting down.\n",1);
- elseif ($conf['phrase']=='')
- edie("you can’t specify an empty string as phrase (use «-h» or «--help» for help); shutting down.\n",1);
- if ($conf['encrypt']) {
- $ichars='aàáâãäåbcdeèéêëfghiìíîïjklmnoòóôõöøpqrstuùúûüvwxyz0123456789';
- $ichars=mb_str_split($ichars,1,'UTF-8');
- $phrase=mb_strtolower($conf['phrase'],'UTF-8');
- $phrase=mb_str_split($phrase,1,'UTF-8');
- if ($conf['method']==1) {
- $buff=$ichars;
- shuffle($buff);
- $len=count($ichars);
- for ($i=0; $i<$len; $i++)
- $ochars[$ichars[$i]]=$buff[$i];
- $len=count($phrase);
- $buff='';
- for ($i=0; $i<$len; $i++) {
- $c=$phrase[$i];
- if (array_key_exists($c,$ochars))
- $buff.=$ochars[$c];
- else
- $buff.=$c;
- }
- } elseif ($conf['method']==0) {
- $buff='';
- $len=count($phrase);
- for ($i=0; $i<$len; $i++) {
- $c=$phrase[$i];
- if (!in_array($c,$ichars))
- $buff.=$c;
- else
- $buff.=mb_chr(mb_ord($c,'UTF-8')+1,'UTF-8');
- }
- }
- echo "{$buff}\n";
- exit(0);
- }
- if (count($conf['dictfps'])==0)
- edie("you have specified no dictionaries (use «-h» or «--help» for help); shutting down.\n",1);
- $norma=[
- 'a'=>['à','á','â','ã','ä','å'],
- 'e'=>['è','é','ê','ë'],
- 'i'=>['ì','í','î','ï'],
- 'o'=>['ò','ó','ô','õ','ö','ø'],
- 'u'=>['ù','ú','û','ü']
- ];
- $dict=[];
- foreach ($conf['dictfps'] as $fp) {
- $buff=@file($fp,FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES);
- if ($buff===false)
- edie("could not open dictionary file «{$fp}» (use «-h» or «--help» for help); shutting down.\n",1);
- foreach ($buff as $k=>$v)
- $conf['norm'] ? $dict[]=norm(mb_strtolower($v,'UTF-8')) : $dict[]=mb_strtolower($v,'UTF-8');
- }
- $dict=array_unique($dict);
- //shuffle($dict);
- foreach ($dict as $k=>$v) {
- $buff=mb_str_split($v,1,'UTF-8');
- $len=count($buff);
- if ($conf['method']==1)
- for ($i=0; $i<$len; $i++)
- $buff[$i]=mb_chr(mb_ord($buff[$i],'UTF-8')+$i,'UTF-8');
- $dict[$k]=['len'=>$len,'splitword'=>$buff,'word'=>$v];
- }
- if (count($dict)==0)
- edie("the dictionar(y|ies) you have specified contain(s) no words; shutting down.\n",1);
- //print_r($dict); pause();
- //foreach ($dict as $k=>$v) echo "{$v['word']}\n"; pause();
- $phrase=preg_replace('#\s+#u',' ',$conf['phrase']);
- $phrase=mb_strtolower($phrase,'UTF-8');
- $cphrase=$phrase;
- $phrase=preg_split('#\W+#u',$phrase,0,PREG_SPLIT_NO_EMPTY);
- //$phrase=explode(' ',$phrase);
- //print_r($phrase); pause();
- $buff=[];
- foreach ($phrase as $k=>$v)
- $buff[$k]=mb_strlen($v,'UTF-8');
- arsort($buff,SORT_NUMERIC);
- foreach ($phrase as $k=>$v) {
- $split=mb_str_split($v,1,'UTF-8');
- $len=count($split);
- $buff[$k]=['len'=>$len,'splitword'=>$split,'word'=>$v,'sols'=>[]];
- }
- $phrase=$buff;
- //print_r($phrase); pause();
- $c=count($phrase);
- $d=0;
- $cns=0;
- $substa=[[]];
- foreach ($phrase as $pindex=>$pword) {
- $d++;
- $ib=$pindex+1;
- eprog(1,"Working on phrase word {$ib}: «{$pword['word']}» ({$d}/{$c})\n");
- $newsubsta=ckdict($dict,$pword,$pindex,$substa);
- /*foreach ($newsubsta as $k=>$v)
- echo strsubsta($v)."\n";*/
- $cns=count($newsubsta);
- eprog(1,"Finished working on phrase word {$ib}: «{$pword['word']}» ({$d}/{$c}); found {$cns} compatible keys.\n");
- //pause();
- if ($cns==0) break;
- $substa=$newsubsta;
- unset($newsubsta);
- }
- if (count($substa)>0) {
- if ($conf['write'])
- if (false===file_put_contents($conf['substabfp'],print_r($substa,true)))
- eerr("Warning: could not open «{$conf['substabfp']}» for writing.\n");
- //ksort($phrase,SORT_NUMERIC);
- $i=0;
- if ($conf['write']) {
- $f=@fopen($conf['solsfp'],'w');
- if ($f===false)
- eerr("Warning: could not open «{$conf['solsfp']}» for writing.\n");
- }
- foreach ($substa as $subst) {
- $i++;
- $oline=$i.': '.getsubst($subst,$cphrase)."\n";
- echo $oline;
- if ($conf['write'] && $f!==false) fwrite($f,$oline);
- }
- if ($conf['write'] && $f!==false) fclose($f);
- if ($cns==0) echo "Warning: no complete solutions found.\n";
- } else {
- echo "No solutions found.\n";
- }
- exit(0);
- function ckdict(&$dict,$pword,$pindex,$substa) {
- global $phrase, $conf;
- $newsubsta=[];
- foreach ($dict as $dword) {
- if ($dword['len']==$pword['len']) {
- //echo "«{$pword['word']}» has same length as «{$dword['word']}»\n";
- foreach ($substa as $subst) {
- $newsubst=$subst;
- //echo '-> '.strsubsta($newsubst)."\n";
- $ok=true;
- for ($i=0; $i<$pword['len']; $i++) {
- $pc=$pword['splitword'][$i];
- $dc=$dword['splitword'][$i];
- //$dc=mb_chr(mb_ord($dword['splitword'][$i],'UTF-8')+$i,'UTF-8');
- $x=array_search($dc,$newsubst,true);
- if (($x!==false && $x!=$pc) || (array_key_exists($pc,$newsubst) && $newsubst[$pc]!=$dc)) {
- $ok=false;
- break;
- }
- $newsubst[$pc]=$dc;
- }
- if ($ok) {
- if ($conf['progress']>1 && !in_array($dword['word'],$phrase[$pindex]['sols'])) {
- $phrase[$pindex]['sols'][]=$dword['word'];
- echo "«{$pword['word']}» could be «{$dword['word']}»\n";
- }
- $newsubsta[]=$newsubst;
- }
- }
- }
- }
- return $newsubsta;
- }
- function eprog($lev,$str) {
- global $conf;
- if ($lev<=$conf['progress'])
- echo $str;
- }
- function eerr($str) {
- fwrite(STDERR,$str);
- }
- function pause() {
- echo "Press return to continue ";
- fgets(STDIN);
- }
- function getsubst($subst,$str) {
- $out='';
- $str=mb_str_split($str,1,'UTF-8');
- $len=count($str);
- for ($i=0; $i<$len; $i++)
- if (array_key_exists($str[$i],$subst))
- $out.="\33[0;7m{$subst[$str[$i]]}\33[0;0m";
- else
- $out.=$str[$i];
- str_replace("\33[0;0m\33[0;7m",'',$out);
- return $out;
- }
- function norm($str) {
- global $norma;
- $out='';
- $str=mb_str_split($str,1,'UTF-8');
- $len=count($str);
- for ($i=0; $i<$len; $i++) {
- $nc=$str[$i];
- foreach ($norma as $k=>$nnca)
- if (in_array($str[$i],$nnca)) {
- $nc=$k;
- break;
- }
- $out.=$nc;
- }
- return $out;
- }
- function strsubsta($arr) {
- $out='';
- foreach ($arr as $key=>$val)
- $out.="{$key}={$val}; ";
- if (strlen($out)>0)
- $out=substr($out,0,-2);
- return $out;
- }
- function edie($msg,$ec) {
- fwrite(STDERR,'Error: '.$msg);
- exit($ec);
- }
- ?>
|