Augustova šifra Jana Soběslava
Úvod
Augustova šifra je popsána v knize Já Claudius, fiktivní autobiografii Římského císače Claudia. Ten v ní, zkrze pero Roberta Gravese, popisuje šifrovací systém svého předchůdce, císaře Oktaviána, později známého jako Augustus. Graves o tomto systému píše takto:
V obavách z úkladů o život se Tiberius snažil znovu a znovu získat přístup k matčiným kriminalistickým záznamům. Livia však stále předstírala, že se dešifrovací klíč ztratil. Tiberius jí na Seianův návrh řekl, že tedy nemají pro nikoho žádnou cenu, a tak že je spálí. Ona mu odpověděla, ať si klidně poslouží, ale že by bylo moudřejší přece jen si je nechat, pro případ, že by se klíč třeba jednou našel. Možná, že si naň sama najednou vzpomene. „Nuže dobrá matko,“ řekl, „já si je zatím vezmu k sobě. A než si vzpomeneš, budu se po večerech pokoušet přijít na ten klíč sám.“ Vzal si tedy záznamy do svého pokoje a zamkl je do truhly. Lámal si hlavu s luštěním klíče, ale nestačil na to. V systému běžných šifer se psalo běžně latinské E místo řeckého alfa, latinské F místo řeckého beta, G místo gama, H místo delta a tak dále. Ale klíč pro zvláštní šifry snad bylo nemožné rozluštit. Byl totiž založen na první stovce veršů první knihy Íliady, které se musely číst souběžně s psaním šifer, a každému písmenu psaného textu odpovídal počet písmen v abecedě mezi ním a příslušným písmenem v Homérovi. Tak například první písmeno prvního slova prvního verše první knihy Íliady je mí. Předpokládejme, že první písmeno prvního slova nějakého tajného zápisu je ypsilon. V řecké abecedě je mezi mí a ypsilon sedm písmen, takže místo ypsilon by se psalo 7. V tom plánu je třeba představit si abecedu v kruhovém uspořádání, po posledním písmenu omega následuje první alfa, takže vzdálenost mezi ypsilon a alfa by byla 4, kdežto mezi alfa a ypsilon 18. Tento klíč vynalezl Augustus a jistě to trvalo hodně dlouho než se nějaká zpráva takto napsala a zase dešifrovala, ale častým opakováním si už jistě zapamatovali vzdálenost mezi jakmikoli dvěma písmeny abecedy, aniž museli počítat, čímž se hodně času ušetřilo. A jak to všechno vím? Protože o mnoho let později, když záznamy přešly do mého vlastníctví, jsem šifru vyluštil sám. - Robert Graves, Já Claudius
Augustova šifra tedy vychází z klasické Caesarovy transpoziční šifry, kde jsou všechna písmena posunuta o určitý konstantní počet znaků. Oproti této je ovšem o něco důmyslnější, neboť vyžaduje druhý text, který slouží jako klíč – a bez něhož (téměř) nelze původní text brute-force útokem dekódovat: využít frekvenční analýzy, nebo různých číselných řad pro offsety nemají žádný výsledek. Jedinný způsob dešifrování je uhádnout původní text – a pokud ten navíc není human-readable, stává se jeho hádání (téměř) neřešitelným problémem. Pro kryptografa ovšem může být jeho zapamatování velmi jednoduché: může to být kus knihy, báseň, nebo modlitba – tedy texty, které si lze jednoduše zapamatovat a po čase používat i bez jakéhokoliv fyzického důkazu, který by útočníkovi klíč prozradil.
Algoritmus
Šifra vyžaduje čtyři vstupní soubory: vstupní text, šifru, abecedu vstupního souboru a abecedu šifry. Různé abecedy jsou proto, aby byla v původním textu při šifrování a dešifrování zachována interpunkce, diakritika, velikost písmen a mezery.
Oba texty je potřeba nejprve pomocí abeced přeložit do zpracovatelného formátu: šifru zbavíme interpunkce, diakritiky, čísel, velikosti písmen a dalších překážek tak, aby výsledný text obsahoval pouze základních 26 písmen a-z. Text „Včera jsem snědl: 2 jablka a banán“ se tím změní na „vcerajsemsnedljablkaabanan.“
Hlavní text se překládá podle druhé abecedy, která nahrazuje speciální znaky a diakritiku jejich textovými protějšky, např „:“ se změní v „dvojtecka“, nebo „á“ na „dlouhea“, nebo znak mezery „ “ na „mezera.“ Výhoda tohoto řešení je především v lepším zachycení původního textu a jeho dešifrovatelnost i bez použití počítače, naopak největší nevýhoda je narůstající objem výsledného zašifrovaného souboru: každý znak pro mezeru je nahrazen šesti znaky slova „mezera“ a vzhledem k tomu, že jsem například při psaní tohoto dokumentu do tohoto okamžiku napsal 624 mezer, odpovídá to nárustu o 3120 znaků.
Tento problém lze částečně zlehčit použitím krátkých hashů, které v daném jazyce nemají žádný význam, pro každý zvláštní znak. Například pro „á“ použít „zxda“, nebo pro mezeru použít „zxm“. Takovéto řetězce se v normálním textu s velkou pravděpodobností nikdy neobjeví a proto si je počítač při strojovém překladu nesplete a správně přeloží na speciální symbol a přeci jen na mezerách ušetříme polovinu znaků, což je v tomto momentě úspora 2172 znaků. Ovšem při ručním překladu nám z tohoto systému budou vycházet nesmysly.
Pokud předpokládáme, že text budeme překládat ručně, je dobrý nápad použít pro vstupní soubor podobnou abecedu, jako pro šifrovací soubor, ovšem s ošetřeným vstupem pro čísla, o něž v šifrovaném textu rozhodně nechceme přijít. Výsledný textu bude kratší a tudíž i rychlejší na ruční překlad a do dešifrovaného textu mohou kryptografové diakritiku a interpunkci dodat během samotného překladu.
Volba abecedy závisí na tom, jak budeme šifru používat. Pro použití strojů na obou stranách je nejlepší použít hash, pro lidi na obou stranách je vhodný způsob bez diakritiky a jako jakýsi kompromis, který budu dále používat já, je použitelné řešení první uvedené.
V tomto momentě jsme si zvolili text zprávy, klíč a vhodnou abecedu a oba soubory jsme přeložili do strojově použitelné formy. S šifrou je potřeba ještě krátce pracovat: pro potřeby algoritmu je nutné ji za sebe zkopírovat tolikrát, aby byla stejně dlouhá, nebo delší, než přeložená forma vstupního textu.
Nyní už je práce algoritmu poměrně jednoduchá. Jenom poznamenám, že jsem si dovolil drobnou invenci oproti Claudiem popsané verzi a ta spočívá v tom, že rozdíl mezi znaky nezapisuji jako číslici, ale jako znak, který má v abecedě právě pozici s výsledným číslem. V cyklu čte jeden znak textu po druhém, u každého se podívá na odpovídající znak šifry, ten přeloží na jeho pořadí v abecedě o tuto hodnotu znak textu posune. Pokud by se posunem o n pozic dostal znak mimo abecedu, počítá se jeho nové pořadí jako číslo, které zbyde po odečtení počtu znaků abecedy (26) od současné pozice + počet pozic o které se má posunout (n). Pokud tedy máme 25. znak, „y“ posunout o „c“, nebo-li 3 pozice, změní se na pozici 25 – 26 + 3 = 2, nebo-li „b“. Pokud měníme „y“ o „a“, součet 25 a 1 nepřesahuje 26 a proto můžeme jednoduše sečíst a zapsat jako „z“.
Dešifrování probíha obdobně: klíč opět převedeme na 26-ti znakový kód a opět postupujeme znak po znaku, ale tentorkát čísla odečítáme. Pokud máme v textu „z“ a na odpovídající pozici v šifře písmeno „a“, odečteme 1 od 26 a získáme číslo 25, písmeno „y“. Pokud bychom se odečtením dostali na nulu nebo do záporných čísel, počítáme jako počet písmen abecedy – (posun – pozice znaku textu). Pokud tedy máme „b“ a v odpovídajícím znaku šifry je „c“, počítáme 26 – (3 – 2) = 25, neboli opět „y“.
Posledním krokem je přeložit řetězce jako „mezera“, nebo „zxm“ zpět na správné znaky, potažmo přeložit i čísla, které budeme mít přeložené například místo jako „100“ jako „jednanulanula.“
Je důležité aby se obě strany dohodly nejen na společném klíči, ale také na společné abecedě.
Konkrétní implementace
Tento algoritmus jsem naprogramoval v jazyce PHP a okomentovaný zdrojový kód se nachází na konci tohoto dokumentu.
Pro konkrétní příklad jsem jako text určený k zašifrování zvolil citaci použitou v úvodu dokumentu. Jako šifrovací klíč jsem vybral část textu z anglické verze první kapitoly knihy George Orwella 1984:
It was a bright cold day in April, and the clocks were striking thirteen. Winston Smith, his chin nuzzled into breast in an effort to escape the vile wind, slipped quickly through the glass doors of Victory Mansions, though not quickly enough to prevent a swirl of gritty dust from entering along with him. The hallway smelt of boiled cabbage and old rag mats. At one end of it a coloured poster, too large for indoor display, had benn tacked to the wall. It depicted simply an enormous face, more than a metre wide: the face of a man of about forty-five, with a heavy black moustache and ruggedly handsome features. Winston made for the stairs. It was no use trying the lift. EEven at the best times it was seldom working, and at present the electric current was cut off during daylight hours. It was part of the economy drive in preparation foer Hate Week. The flat was seven flights up, and Winston, who was thirty-nine and had a varicose ulcer above his right ankle, went slowly, resting several times on the was. On each landing, opposite the lift shaft, the poster with the enormous face gazed from the wall. It was one of those pictures which are so contrived that the exes follow you about when you move. BIG BROTHER IS WATCHING YOU, the caption beneath it ran. - George Orwell, 1984
Pro praktickou implementaci potřebuji obě abecedy v plain-textovém formátu a navíc řídící se následujícími pravidly:
- Soubor začíná znakem „#“
- Soubor začíná výčtem akceptovaných znaků a po něm následuje znak „&“
- Všechny další znaky jsou poté psané ve dvojicích oddělených znakem „\“
- Každá dvojice obsahuje nejprve řetězec, který má být nahrazen, poté znak „|“ a poté řetězec, kterým se má nahradit
- Pro správnou funkci s českými znaky je naprosto zásadní, aby vstupní soubory měly nastaveno kódování UTF-8
Moje abeceda pro šifru tak vypadá následovně:
# abcdefghijklmnopqrstuvwxyz & ě|e\ š|s\ č|c\ ř|r\ ž|z\ ý|y\ á|a\ í|i\ é|e\ ú|u\ ů|u\ ť|t\ ď|d\ ň|n
Nejprve všechny znaky, které čtu, vkládám mezi akceptované. V momentě, kdy narazím na znak „&“, začínám porovnávat znaky po dvojicích. Teoreticky může soubor obsahovat pouze seznam akceptovaných znaků, v tomto případě nám ale PHP vrátí následujcí seznam:
Akceptované znaky: Array ( [0] => a [1] => b [2] => c [3] => d [4] => e [5] => f [6] => g [7] => h [8] => i [9] => j [10] => k [11] => l [12] => m [13] => n [14] => o [15] => p [16] => q [17] => r [18] => s [19] => t [20] => u [21] => v [22] => w [23] => x [24] => y [25] => z ) Nahrazované znaky: Array ( [0] => ě [1] => š [2] => č [3] => ř [4] => ž [5] => ý [6] => á [7] => í [8] => é [9] => ú [10] => ů [11] => ť [12] => ď [13] => ň ) Nahrazující znaky: Array ( [0] => e [1] => s [2] => c [3] => r [4] => z [5] => y [6] => a [7] => i [8] => e [9] => u [10] => u [11] => t [12] => d [13] => n )
S pomocí těchto seznamů poté s šifrou udělám následující kroky:
- Všechny nahrazované znaky nahradím odpovídajícími nahrazujícími znaky
- Zvláštní případ je znak mezery, tato je v abecedě zapsána prostě jako „space“ a v tomto momentě je nahrazena za skutečný znak mezery
- Odstraním mezery, odřádkování a podobné znaky
- Převedu všechny písmena na malá
- Všechny znaky, které nejsou definovány jako akceptované nebo nahrazované, budou prostě zahozeny
Nová šifra, která vznika z úryvku 1984 v tomto bodě vypadá následovně:
itwasabrightcolddayinaprilandtheclockswerestrikingthirteenwinstonsmithhischinnuzzledintobreastinanefforttoescapethevilewindslippedquicklythroughtheglassdoorsofvictorymansionsthoughnotquicklyenoughtopreventaswirlofgrittydustfromenteringalongwithhimthehallwaysmeltofboiledcabbageandoldragmatsatoneendofitacolouredpostertoolargeforindoordisplayhadbenntackedtothewallitdepictedsimplyanenormousfacemorethanametrewidethefaceofamanofaboutfortyfivewithaheavyblackmoustacheandruggedlyhandsomefeatureswinstonmadeforthestairsitwasnousetryingthelifteevenatthebesttimesitwasseldomworkingandatpresenttheelectriccurrentwascutoffduringdaylighthoursitwaspartoftheeconomydriveinpreparationfoerhateweektheflatwassevenflightsupandwinstonwhowasthirtynineandhadavaricoseulcerabovehisrightanklewentslowlyrestingseveraltimesonthewasoneachlandingoppositetheliftshafttheposterwiththeenormousfacegazedfromthewallitwasoneofthosepictureswhicharesocontrivedthattheexesfollowyouaboutwhenyoumovebigbrotheriswatchingyouthecaptionbeneathitran
Naprosto přesně splňuje požadavky.
Nyní stejný postup aplikujeme i na vstupní soubor. Jeho abeceda je podstatně složitější – jak jsem zmiňoval v teoretickém úvodu, je to proto, aby byla při kódování a dekódování zachována interpunkce a diakritika. Tato není pro strojové čtení dokonalá, pokud se v textu vyskytuje například slovní spojení „velké jezero,“ může se stát, že bude během procesu přeloženo jako „Jezero“ (protože řetězec „velkej“ bude přeložen na „J“), pro běžné potřeby by ovšem měla stačit a vypadá následovně:
# abcdefghijklmnopqrstuvwxyz & space|mezera\ Ě|velkeeshackem\ Š|velkesshackem\ Č|velkecshackem\ Ř|velkershackem\ Ž|velkezshackem\ Ý|velkeyscarkou\ Á|velkeascarkou\ Í|velkeiscarkou\ É|velkeescarkou\ Ú|velkeuscarkou\ Ů|velkeuskrouzkem\ Ť|velketshackem\ Ď|velkedshackem\ Ň|velkenshackem\ ě|eshackem\ š|sshackem\ č|cshackem\ ř|rshackem\ ž|zshackem\ ý|yscarkou\ á|ascarkou\ í|iscarkou\ é|escarkou\ ú|uscarkou\ ů|uskrouzkem\ ť|tshackem\ ď|dshackem\ ň|nshackem\ A|velkea\ B|velkeb\ C|velkec\ D|velked\ E|velkee\ F|velkef\ G|velkeg\ H|velkeh\ I|velkei\ J|velkej\ K|velkek\ L|velkel\ M|velkem\ N|velken\ O|velkeo\ P|velkep\ Q|velkeq\ R|velker\ S|velkes\ T|velket\ U|velkeu\ V|velkev\ W|velkew\ X|velkex\ Y|velkey\ Z|velkez\ ,|carka\ .|tecka\ ;|strednik\ -|pomlcka\ :|dvojtecka\ ?|otaznik\ !|vykricnik\ +|plus\ 0|nula\ 1|jedna\ 2|dva\ 3|tri\ 4|ctyri\ 5|pet\ 6|sest\ 7|sedm\ 8|osm\ 9|devet
A úryvek z Já Claudius nyní vypadá takto:
velkevmezeraobavascarkouchmezerazmezerauscarkoukladuskrouzkemmezeraomezerazshackemivotmezerasemezeravelketiberiusmezerasnazshackemilmezeraznovumezeraamezeraznovumezeraziscarkouskatmezeraprshackemiscarkoustupmezerakmezeramatcshackeminyscarkoummezerakriminalistickyscarkoummezerazascarkouznamuskrouzkemmteckamezeravelkeliviamezeravsshackemakmezerastascarkoulemezeraprshackemedstiscarkouralacarkamezerazshackememezerasemezeradesshackemifrovaciscarkoumezerakliscarkoucshackemmezeraztratilteckamezeravelketiberiusmezerajiscarkoumezeranamezeravelkeseianuskrouzkemvmezeranascarkouvrhmezerarshackemeklcarkamezerazshackememezeratedymezeranemajiscarkoumezerapromezeranikohomezerazshackemascarkoudnoumezeracenucarkamezeraamezeratakmezerazshackememezerajemezeraspascarkouliscarkouteckamezeravelkeonamezeramumezeraodpoveshackemdeshackemlacarkamezeraatshackemmezerasimezeraklidneshackemmezeraposlouzshackemiscarkoucarkamezeraalemezerazshackememezerabymezerabylomezeramoudrshackemejsshackemiscarkoumezeraprshackemecemezerajenmezerasimezerajemezeranechatcarkamezerapromezeraprshackemiscarkoupadcarkamezerazshackememezerabymezerasemezerakliscarkoucshackemmezeratrshackemebamezerajednoumezeranasshackemelteckamezeravelkemozshackemnascarkoucarkamezerazshackememezerasimezerananshackemmezerasamamezeranajednoumezeravzpomeneteckamezeravelkenuzshackememezeradobrascarkoumezeramatkocarkamezerarshackemeklcarkamezerajascarkoumezerasimezerajemezerazatiscarkoummezeravezmumezerakmezerasobeshackemteckamezeravelkeamezeranezshackemmezerasimezeravzpomenesshackemcarkamezerabudumezerasemezerapomezeravecshackemerechmezerapokousshackemetmezeraprshackemijiscarkoutmezeranamezeratenmezerakliscarkoucshackemmezerasascarkoumteckamezeravelkevzalmezerasimezeratedymezerazascarkouznamymezeradomezerasvescarkouhomezerapokojemezeraamezerazamklmezerajemezeradomezeratruhlyteckamezeravelkelascarkoumalmezerasimezerahlavumezerasmezeralusshackemteshackemniscarkoummezerakliscarkoucshackemecarkamezeraalemezeranestacshackemilmezeranamezeratoteckamezeravelkevmezerasystescarkoumumezerabeshackemzshackemnyscarkouchmezerasshackemifermezerasemezerapsalomezerabeshackemzshackemneshackemmezeralatinskescarkoumezeravelkeemezeramiscarkoustomezerarshackemeckescarkouhomezeraalfacarkamezeralatinskescarkoumezeravelkefmezeramiscarkoustomezerarshackemeckescarkouhomezerabetacarkamezeravelkegmezeramiscarkoustomezeragamacarkamezeravelkehmezeramiscarkoustomezeradeltamezeraamezeratakmezeradascarkouleteckamezeravelkealemezerakliscarkoucshackemmezerapromezerazvlascarkousshackemtniscarkoumezerasshackemifrymezerasnadmezerabylomezeranemozshackemnescarkoumezerarozlusshackemtitteckamezeravelkebylmezeratotizshackemmezerazalozshackemenmezeranamezeraprvniscarkoumezerastovcemezeraversshackemuskrouzkemmezeraprvniscarkoumezeraknihymezeravelkeiscarkouliadycarkamezerakterescarkoumezerasemezeramuselymezeracshackemiscarkoustmezerasoubeshackemzshackemneshackemmezerasmezerapsaniscarkoummezerasshackemifercarkamezeraamezerakazshackemdescarkoumumezerapiscarkousmenumezerapsanescarkouhomezeratextumezeraodpoviscarkoudalmezerapocshackemetmezerapiscarkousmenmezeravmezeraabecedeshackemmezeramezimezeraniscarkoummezeraamezeraprshackemiscarkouslusshackemnyscarkoummezerapiscarkousmenemmezeravmezeravelkehomescarkouroviteckamezeravelketakmezeranaprshackemiscarkoukladmezeraprvniscarkoumezerapiscarkousmenomezeraprvniscarkouhomezeraslovamezeraprvniscarkouhomezeraversshackememezeraprvniscarkoumezeraknihymezeravelkeiscarkouliadymezerajemezeramiscarkouteckamezeravelkeprshackemedpoklascarkoudejmecarkamezerazshackememezeraprvniscarkoumezerapiscarkousmenomezeraprvniscarkouhomezeraslovamezeraneshackemjakescarkouhomezeratajnescarkouhomezerazascarkoupisumezerajemezeraypsilonteckamezeravelkevmezerarshackemeckescarkoumezeraabecedeshackemmezerajemezeramezimezeramiscarkoumezeraamezeraypsilonmezerasedmmezerapiscarkousmencarkamezeratakzshackememezeramiscarkoustomezeraypsilonmezerabymezerasemezerapsalomezerasedmteckamezeramezeravelkevmezeratommezeraplascarkounumezerajemezeratrshackemebamezeraprshackemedstavitmezerasimezeraabecedumezeravmezerakruhovescarkoummezerausporshackemascarkoudascarkouniscarkoucarkamezerapomezeraposledniscarkoummezerapiscarkousmenumezeraomegamezeranascarkousledujemezeraprvniscarkoumezeraalfacarkamezeratakzshackememezeravzdascarkoulenostmezeramezimezeraypsilonmezeraamezeraalfamezerabymezerabylamezeractyricarkamezerakdezshackemtomezeramezimezeraalfamezeraamezeraypsilonmezerajednaosmteckamezeravelketentomezerakliscarkoucshackemmezeravynalezlmezeraaugustusmezeraamezerajisteshackemmezeratomezeratrvalomezerahodneshackemmezeradlouhomezeranezshackemmezerasemezeraneshackemjakascarkoumezerazprascarkouvamezerataktomezeranapsalamezeraamezerazasemezeradesshackemifrovalacarkamezeraalemezeracshackemastyscarkoummezeraopakovascarkouniscarkoummezerasimezerauzshackemmezerajisteshackemmezerazapamatovalimezeravzdascarkoulenostmezeramezimezerajakmikolimezeradveshackemmamezerapiscarkousmenymezeraabecedycarkamezeraanizshackemmezeramuselimezerapocshackemiscarkoutatcarkamezeracshackemiscarkoumzshackemmezerasemezerahodneshackemmezeracshackemasumezerausshackemetrshackemiloteckamezeravelkeamezerajakmezeratomezeravsshackemechnomezeraviscarkoumotaznikmezeravelkeprotozshackememezeraomezeramnohomezeraletmezerapozdeshackemjicarkamezerakdyzshackemmezerazascarkouznamymezeraprshackemesshackemlymezeradomezeramescarkouhomezeravlastniscarkouctviscarkoucarkamezerajsemmezerasshackemifrumezeravylusshackemtilmezerasascarkoumteck
Jak je vidět, objem dat skutečně narostl značně, z 1924 na 5456 znaků, což představuje nárust o zhruba 184%. Pokud ovšem použijeme stejnou abecedu, jako pro šifru (tzn. obětujeme diakritiku, interpunkci, mezery a číslice), získáme výsledný text 1537 znaků dlouhý, což dokonce znamená 20% úsporu místa.
Nicméně oba soubory máme v tomto momentě připraveny k použití. Ještě dle potřeby natáhneme velikost šifry a v tomto případě nám algoritmus oznámí:
Počet kopírování šifry: 3 Velikost šifry: 8064 Velikost vstupu: 5456
Nyní přistupujeme konečně k vlastní konverzi. Postupujeme přesně podle teoreticky popsaného algoritmu a napsaný kód je snad všeříkající, dodám k němu jenom to, že kdekoliv se objevuje kouzelné přičítání, nebo odečítání jedničky, je to proto, že indexy pole v PHP – a myslím že většině dalších jazyků – nezačíná od čísla jedna, jak by počítali lidé, ale od nuly.
A voila, získáváme dokonale nečitelný řetězec:
eyilxwowilzurqmzetbjflemltnsdyzfcytcpkxzkhtlcxftzhxcbcltznhnafyosknxgmhnkdhbvoxkeynzxhgtbwwblyvsaswgbtdeyinuhsyzmujvndfpwodltjsajqzgvhkqquhfdqbuyhjymbfxdtgsscurdpyowqnawllpfdichfhbattvmjscegfqzztqmrqjpkzgnvijnrqggrentyqehtnikwnhyyrawmzdmgyvrvgmhneuswqnuzxmhlgnoenyepawtypngbfyfaowrmvcpbmoufvmzftznotsvnffzmbzrjvqkxfpwfxkubelekgsegwwpuonfqwndhfvcxhomddcpsoayujwfdmylwmqlnyriwbgyebbfpcjjnavvgsnfztrjliaginhewrbvieyzfyfpjoksneshyicrfysxxlnbjynpluzlwznaygdbnwvhxtllrchtveufltrildzbnxkpgnryfwfsrxwnftptzxfxnhtjccxfyanjtscpdtfzjnryrdaouuujlnxuajhpstyciswxdlidmpxvpjfsxwmrpfzpjvxicsvqftujfkxvuwsjrqpowsandhwrjfuwtadxetskqzrnfhxfckvlhyzpiwfjdftvqscijsyhjwdefdzddwawsryeztcfrfljognphcmnuxzfwvicitavgbaxkfyjbaojysufzpffexvssygoqizjfsyzjrmgolyjnsqmaiswkwvhoxwvesfkdcggtcubklhzepieqhhfaysqgxhddpxiwotxeawsnggnmjkpcxxtsflwohlhupfgljqrtcbpvjlpuujlnxutbtnuwsjcbxtjjxbcuyhjwozdvsixyidhrfmrifwgnjwxipglbbxddtanhiuzzhzsjuevejsbzjdtfejstupnmzjypigmnytmwfzdfkoqdpyxajngozlxuticgnupypgvllkhbihcnfftwdztcbltuyzfssibjrygrshfgmiyjbxnhjfsftsbpuyctdenflnnfhswqdvenkfuwpppsbwsukienknfhjzbzuhpsjrgvugyhhyulkjknivsnzralxljqetowrfsyaocmrkftjumtrxcfhfetnolmwhxigltjsajqdzihcmslazpxrmgmghyfsxvpywwcuqvhttjzabglqpqdyutgamqzudzihcmujzzztwthxshgjahblzjjwdajsaeuliuxlgrhufqejenalaqgozfvyhmanhisaimohflfreqlpycbjyjdhscpbqjebsjyirfynwtipgtbjysgzgvyahgmkzdpidjsmmswztyfelekgsmcfgpkgjkaavlmaitfaonlrnfvepgmebsmdbbehanpyppvjeamlfnjfpbnhxtxlrzztrjlitwnreyjfgnqjtmwgadyxydnsydanotuyxpnyysdijwnlilujaaqcepepfwvvefpbjdyeezglwehdtlseftmjxfoyukmtztsfgtnrsexobwtmwtpaygfnhbtlvpxdjguzayhgmeqagvziqrsaylixgrxtyanuhvywfktaqghuxrcpvsyfqlnypwwtuceicxxtffewvhwhwrjfumslkvwzksmeabqhvlngctmtmgvftvywfkquwhbktmwfnawhpzvcxqhbvqupuneftngoypxuisetrrypwsfsqayoboxqgjjfmutmtxmqtwqbiskuisagbbaxliismboaytvnrincnaawadshrzlhwstxtohiadgdowgfnpdfmtyicqbbldjftgnnlkfvfelxgnmjkpxyujwfkprtnhhqsoxaioaojtvturyhjdjbyesfrumkfhzhorsivyhyzfxwbwmtmtnmdabneeixshzyhjobxgbmebvzsrikmppvpuvqcnufkhwdpufrjkpnawmujnpsoktbukpjkjvggwmbbytmbcazgbhjfzczfiwhuqhectgqqensbsuqivsgdgvnwyhdauvtngwoaffsnpefnnlxtrtvwbjilndgqyndissqtneiogemrcewdndbrfdlwsavxijbunrywfgpqovetabsnlumixuibsazzedftbofdowefkukstvfiqtetmwfvnfcsyamwlwjjvsdxdjbqymenthdozlsgjhlhyzfcqmdxibtrxggirvwpjjbvgmxbssyzpmzporevhnfvqrhogfvcbdcweawoumyxlsdzgtwiylzpvmctkzrxquwcxitfltfhsnfsmozyhjzbyupdzkxtgmiuotiqweutjcdrjnsgzqwubyxpoapgtnjwoftzjwbuzmbzrjvqkxfpwabtlfjhrohujfodjkxxfulfqiheofqolldxjdygudjilqtvydjhjpcxgtaxegrucrsowepyxrudrrowwuinsarwulfhvieyzfjbvhpxlbvztzfezvgkowlzbnhpbjfmnmefnzmxjvnngfhodvpfdyiehlgjvmozpaisteffamunufkhxilmwoplirzcamkfkepdexiyoblgwvvpyehoffgmeqagmsfzrsayligaqhgyirwtwyjpslmmgzrjcwdloylcynypwwtwctfcxxtffewvwlolwhyujfshmukkrozthztiaddjawnqonztlebnfuawlpfuttevworxdqwwijishssqnwayapdxksaaduwhtzxyhjxmtnlwvxraesxmelzmlcqdyijdgdldiwstjnxtmasjqjwwxdovspynaawadzgnckpjrftprjttxabjillcfxnximwtdzxydrjkuthlkjohsswidzjstfuajjfstffnxaqmmqnnlgqshajwpyunelgldwvyngmeugtmtmynmpukpjaokupxdknqjziahokebrtmwxtyqiyobihosxywigdtkvpyuwwtpaqdklwrbuhgqujtwritmsuuujecwtqplewxbzznftnwxzpctbpztvjtjwkkruzpgdljfhxunnlqpkmishtsuazcwmcrftmaulflcojfsftsxkxiyxegesjxieqdvqasvuxtfewdndbrwyfyrncjdpmkjjetzgjnbfyosknegmhnkdiksqzdeemelyybowefkuvsawrkftjuhxxvdsatouraiqwxjaisqaqfwwyvlnpyhmksgfvcmtzzeitvotbfrhiwanijelraskjewlwigfvcgbyezvppldwokhlhygqnjhpsbpfbbumgqvmaiphxzvegetmjfuawuylumzalwnlivjclamidwqjjrvnwwijqnpmyjdhscrscsjggpdogvtraykbjxghffodabgfqdytuwwefgohnkwpgwpmotstrnfehagzjfulfqiqvxswgwbuvtybdgmebsmhnlwxxjfejqxvrpqqbdwjcafrvkqpxrrowwusowilryrjojzjfsjotffgqphmwpjzotuyxpbylkiawxvcadiwlktvqdnlztuxlbymqlstjnohhoqlmedsdanxifsejmixgvsvugynniekgstamtwlnexvywfkoemosckbjfricrqiklfuexqblewzurxhivrexaumsognegpehdmsxalasvbmbgatrstyzfsqxkuuthpescjgwxsdrpbdskdzjjhhnodelhzewjcgfntbstpbkwpxewzulhibryijjfjvspwwqzhtcudbzthcinyebwfgytpkhnywfktwomoiwntmwdzidovhxifyosoipyfvylnkbzqtsrnsdmsekanwihgtrzllrwaggwrqbvsarwonskqwxfaysqgxkokneiwalsjnfkfpgvrexgojqxzbkzcztpmzdovtottpugtcuptammlqyfmnktyzfcxlwfjhxoanwrjntjnnklomrstfzjvgtnzyhjobeqvywfkpdxpriutsjhjvyyeyxzsjpmawwtbhoslseaqoydnytmwfrtfstlqgxoavtepmelcsilturgwogvlbwgubwkqtzlypmkotdozliuutxulzsojrxuudrnefnnlxtbngrnyiuetoihvlnnfhseyfnilipugwrgxphswdimvxnnllidkjtwgejqdybolpjlswdwdaloffouznwlolzqmhbkejafnjxgeggyojkdqexfmijrehxaysnyvupjvrkrvnmqewmhhlvoujgqsthqtvsftfaadhpkbnsyhdhzxxcyztmntlrkayyurhogfvcgtpwwwfzzbvxacmbkgwaunzoulbguzrrsgjrnfhwlsozzjlswdyyvtauyqwfqtzjlcbkbtamdtvjnqotjasvpmqwalebuegubsejfeqevyahgmqtdfquoxlfunngudsypgbwisvpzviilxmdjmnxqrsnysdxjdcbyhjobmxouqjpnuufqxirhmxqgnzdfztuxxbmjqcstmuusdxfgwebaeaytpkuhsiunrasgglnxnwggcitibdfztyunmefnzxkodwrogfyahzfbikmnabxqqymsoeftmjxfkcnljlejqdybaraiwgiggmexlbcjojflnxntmtmivmngoukpqvsyejnfrmioptojsyljajsbbxddxryioropkpvsgffeoyizfvpsgdipfehdlcxxpzrwwotmstvxugquyqsvsgiszfjrbiutuwkjhxqlqmsgtfyzffptntezdsbarnnujftfktbjrykbnxzppfifhllsfsqayobtejijnkdjjabtxafaepcmlwpfhwjjnxtmasiootcabggisopqoabfpkpjgprwaggwxqvxrnyioagsmhhjagxltomiaxmybvhvpareflyaneteogiylwtawrfcmdbjdmozrqhzhdfjhwxfvktxzbsytqruoxlfbxtgqewqnatrrgvwbfvmhdkjqssbrtmwxqueluolhjgrttsutsjhjdvzujwvkjusbejstupohaiomewsfggmejpffahqxxjdozscejwppfqjthvhyfttvwlpnkfjttjfthzjqzgpxemrltsuazcwrnxiuktyensiyoblfowilzukdpritgjqluevqasvukxkmrnpfxxmrxtwaldgzbilcyrjhobvtwzsfvuibmlddunnsmavqqonogtbwwbcutafnjxgiggyojkdwixbihgnyjzqbsfqiuhfzznldcwnougivgvcsrllfktztacxexkcrthzzplsfnbssyzpjtmntlrhwrsaljntmhtyicweawojpsanktpirwvdcbemdusworwoeiqigobozstjntmzjmuahiswaramtzdyyoktpydxlddmgnlxtverwiemfzftxsuscrjniggvytfpdzjmmsctsywspapeufpxipjtcyrinmbvqdlncdetpoayahcfnmtgujwfdmbmlfsthgnjkdzuldsoamamfhctilhrgxdryhffbffmufotszgbjil
Jeho délka je přesně očekávaných 5456 znaků, což nám může působit jako základní ukazatel, že jsme postupovali správně. Abychom se ujistili, že tento nesmysl je skutečně textem, který jsme zakódovali, použijeme ještě obrácenou funkci, kterou opět nemá smysl podrobně rozebírat a musím vás opět odkázat do zdrojového kódu.
Nicméně tato funkce nám vrací kýžený povědomý výstup:
velkevmezeraobavascarkouchmezerazmezerauscarkoukladuskrouzkemmezeraomezerazshackemivotmezera...
Na který teď použijeme obrácenou funkci pro nahrazování znaků a výsledkem je první citovaný text:
V obavách z úkladů o život se Tiberius snažil znovu a znovu získat přístup k matčiným kriminalistickým záznamům. Livia však stále předstírala, že se dešifrovací klíč ztratil. Tiberius jí na Seianův návrh řekl, že tedy nemají pro nikoho žádnou cenu, a tak že je spálí. Ona mu odpověděla, ať si klidně poslouží, ale že by bylo moudřejší přece jen si je nechat, pro případ, že by se klíč třeba jednou našel. Možná, že si naň sama najednou vzpomene. Nuže dobrá matko, řekl, já si je zatím vezmu k sobě. A než si vzpomeneš, budu se po večerech pokoušet přijít na ten klíč sám. Vzal si tedy záznamy do svého pokoje a zamkl je do truhly. Lámal si hlavu s luštěním klíče, ale nestačil na to. V systému běžných šifer se psalo běžně latinské E místo řeckého alfa, latinské F místo řeckého beta, G místo gama, H místo delta a tak dále. Ale klíč pro zvláštní šifry snad bylo nemožné rozluštit. Byl totiž založen na první stovce veršů první knihy Íliady, které se musely číst souběžně s psaním šifer, a každému písmenu psaného textu odpovídal počet písmen v abecedě mezi ním a příslušným písmenem v Homérovi. Tak například první písmeno prvního slova prvního verše první knihy Íliady je mí. Předpokládejme, že první písmeno prvního slova nějakého tajného zápisu je ypsilon. V řecké abecedě je mezi mí a ypsilon 7 písmen, takže místo ypsilon by se psalo 7. V tom plánu je třeba představit si abecedu v kruhovém uspořádání, po posledním písmenu omega následuje první alfa, takže vzdálenost mezi ypsilon a alfa by byla 4, kdežto mezi alfa a ypsilon 18. Tento klíč vynalezl augustus a jistě to trvalo hodně dlouho než se nějaká zpráva takto napsala a zase dešifrovala, ale častým opakováním si už jistě zapamatovali vzdálenost mezi jakmikoli dvěma písmeny abecedy, aniž museli počítat, čímž se hodně času ušetřilo. A jak to všechno vím? Protože o mnoho let později, když záznamy přešly do mého vlastníctví, jsem šifru vyluštil sámtec
https://www.diffchecker.com nám prozradí, že jedinné změny, které se mezi šifrováním a dešifrováním staly, byly uvozovky, které zmizely, číslice, které byly původně zapsány slovem a dvě chyby: jedna z nich je z nějakého důvodu malé „a“ ve jméně „Augustus“ a druhá jsou písmena „tec“ na konci, které měly patrně být „tecka.“ Až na tyto drobnosti považuji výsledek za mimořádně povedený.
Zdrojový kód
<?php
/* pokud mam vsechny potrebne soubory a v postu je informace jestli sifrujeme nebo desifrujeme */
if (isset($_FILES['input_file'], $_FILES['cipher_file'], $_FILES['input_alphabet'], $_FILES['cipher_alphabet']) && (isset($_POST['encode']) || isset($_POST['decode']))) {
/* rozhodnem se jestli sifrujeme nebo desifrujeme */
if (isset($_POST['encode']))
$encode = true;
else if (isset($_POST['decode']))
$encode = false;
/* nacteni souboru do promennych */
$files = array("input" => $_FILES['input_file'],
"cipher" => $_FILES['cipher_file'],
"input_alphabet" => $_FILES['input_alphabet'],
"cipher_alphabet" => $_FILES['cipher_alphabet']);
foreach ($files as $var => $file) {
$var_name = $var . "_filename";
$var_size = $var . "_size";
$$var_name = $file['tmp_name'];
/* ziskame promenne $input_filename, $cipher_filename, $input_alphabet_filename a $cipher_alphabet_filename */
$$var_size = filesize($$var_name);
/* ziskame promenne $input_size, $cipher_size, $input_alphabet_size a $cipher_alphabet_size */
$file = fopen($$var_name, "r") or die("Nepodařilo se otevřít soubor");
$$var = fread($file, $input_size);
/* ziskame promenne $input, $cipher, $input_alphabet a $cipher_alphabet */
fclose($file);
/* Vypis nactenych dat */
//echo $var . " (" . $$var_name . ", " . $$var_size . "): <br/>" . $$var . "<br/><br/>";
}
$input_backup = $input;
$cipher_backup = $cipher;
/* funkce na nacteni sifer do potrebnych poli */
function readAlphabet($alphabet, $alphabet_size) {
$acceptedCharsRead = false;
$acceptedChars = array();
$replacedChars = array();
$replacingChars = array();
$alphabet = preg_replace("/(\r\n|\n|\r| )/", "", $alphabet); /* odstranime mezery, odradkovani atd */
$alphabet = str_replace("space", " ", $alphabet); /* vratime tam chtene mezery */
$start = strpos($alphabet, "#") + 1; /* najdeme zacatek */
for ($i = $start; $i < $alphabet_size; $i++) { /* postupujeme po znacich */
$curentChar = substr($alphabet, $i, 1);
if ($curentChar == "&") { /* pokud jsme narazili na oddelovac akceptovanych znaku */
$acceptedCharsRead = true;
$alphabet = substr($alphabet, $i + 1, $alphabet_size - $i); /* akceptovane uz nepotrebujeme a tak je z abecedy vyhodime */
} else if (!$acceptedCharsRead) { /* pokud ne a jeste jsme vsechy neprecetli, zarazujeme znak mezi akceptovane */
array_push($acceptedChars, $curentChar);
} else { /* pokud jsme precetli vsechny akceptovane znaky */
break; /* vyskocime z cyklu abysme mohli zbytek abecedy zpracovat pres explode */
}
}
$pairs = explode("\\", $alphabet);
foreach ($pairs as $pair) {
$pair_array = explode("|", $pair);
$replacedChars[] = $pair_array[0];
$replacingChars[] = $pair_array[1];
}
/* vypis obsahu abecedy */
//echo "Přeložená abeceda: <br/>Akceptované znaky: ";
//print_r($acceptedChars);
//echo "<br/><br/> Nahrazované znaky: ";
//print_r($replacedChars);
//echo "<br/><br/> Nahrazující znaky: ";
//print_r($replacingChars);
//echo "<br/><br/>";
return(array("accepted" => $acceptedChars, "replaced" => $replacedChars, "replacing" => $replacingChars));
}
/* funkce pro nahrazeni textu podle zadanych znaku */
function replaceChars($string, $charset) {
$string = str_replace($charset["replaced"], $charset["replacing"], $string); /* nahradime znaky */
$string = preg_replace("/(\r\n|\n|\r|\ )/", "", $string); /* odstranime mezery atd */
$string = $string = strtolower($string); /* vse prevedeme na male znaky */
$result = "";
for ($i = 1; $i < strlen($string); $i++) { /* postupujeme po znacich */
$curentChar = substr($string, $i - 1, 1);
if (in_array($curentChar, $charset["accepted"])) { /* jen ty co jsou pripustne pridavame do vystupu */
$result .= $curentChar;
}
}
return $result;
}
function reverseReplaceChars($string, $charset) {
$string = str_replace($charset["replacing"], $charset["replaced"], $string); /* nahradime znaky */
return $string;
}
/* úprava šifry */
$cipherChars = readAlphabet($cipher_alphabet, $cipher_alphabet_size); /* nacteni do poli */
$cipher = replaceChars($cipher, $cipherChars);
//echo "Nová šifra:<br/>" . $cipher . "<br/><br/>";
/* úprava vstupního souboru */
$inputChars = readAlphabet($input_alphabet, $input_alphabet_size); /* nacteni do poli */
$input = replaceChars($input, $inputChars);
//echo "Nový vstup:<br/>" . $input . "<br/><br/>";
/* natahneme sifru */
$iterator = 0;
while (strlen($cipher) <= strlen($input)) {
$iterator++;
$cipher .= $cipher;
}
/* vypis infa o kopirovani sifry */
//echo "Počet kopírování šifry: " . $iterator . "<br/>";
//echo "Velikost šifry: " . strlen($cipher) . "<br/>";
//echo "Velikost vstupu: " . strlen($input) . "<br/><br/>";
function encode($a, $b, $alphabet) {
$max = strlen($alphabet); /* delka abecedy */
$aindex = strpos($alphabet, $a) + 1; /* pozice nahrazovaneho znaku */
$bindex = strpos($alphabet, $b) + 1; /* pozice znaku sifry */
if ($aindex == 0) { // pokud z nejakeho duvodu neni znak v abecede
return "";
} else {
if ($bindex + $aindex > $max) { /* pokud prekrocime delku abecedy */
$new_position = $aindex - $max + $bindex;
} else { /* pokud se vejdeme */
$new_position = $aindex + $bindex;
}
return $new_position;
}
}
function decode($a, $b, $alphabet) {
$max = strlen($alphabet); /* delka abecedy */
$aindex = strpos($alphabet, $a) + 1; /* pozice nahrazovaneho znaku */
$bindex = strpos($alphabet, $b) + 1; /* pozice znaku sifry */
if ($aindex == 0) { // pokud z nejakeho duvodu neni znak v abecede
return "";
} else {
if ($aindex - $bindex <= 0) { /* pokud se dostaneme do zapornych cisel */
$new_position = $max - ($bindex - $aindex);
} else { /* pokud se vejdeme */
$new_position = $aindex - $bindex;
}
return $new_position;
}
}
function position($position, $alphabet) {
return substr($alphabet, $position - 1, 1); /* najdeme pozici znaku */
}
$alphabet = implode("", $inputChars["accepted"]);
/* vypis pouzite abecedy - standardne znaky a-z */
//echo "Abeceda: ", $alphabet . "<br/><br/>";
$result = "";
$result_numbers = "";
for ($i = 0; $i < strlen($input); $i++) { /* projedeme cely retezec */
if ($encode) { /* kodujeme */
$new_position = encode($input[$i], $cipher[$i], $alphabet); /* ziskame novou pozici */
} else { /* dekodujeme */
$new_position = decode($input[$i], $cipher[$i], $alphabet); /* ziskame novou pozici */
}
$char = position($new_position, $alphabet); /* pozice noveho znaku */
$result .= $char;
$result_numbers .= $new_position . " "; /* pro zajimavost si ukladam i cisla, jak to bylo v augustove verzi */
}
if (!$encode) { /* pokud dekodujeme, musime zase nahradit znaky z abecedy */
$result = reverseReplaceChars($result, $inputChars);
}
/* vypíšeme si výsledek */
//print_r($result);
}
?>
Demo
Stáhněte si balíček připravených souborů. Složka "texts" je určena pro texty k zašifrování, v "ciphers" je pár připravených šifer, "alphabets" obsahuje abecedu pro šifru i abecedu pro plnou diakritiku vstupu a v "encrypted" se nachází zašifrovaná verze připraveného textu. Experimentujte, měňte abecedy, šifrujte si vlastní texty a pokud vám to příjde epický, můžete mi koupit čaj nebo tak něco.