#!/usr/bin/php . */ 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 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 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 dump master | aspell -l 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 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); } ?>