Kasinski elemzés

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, nem a diáktársaknak!

A feladatok egymásra épülnek ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

A feladatban a Kasinski elemzéssel foglalkozunk, amely egy polialfabetikus helyettesítő rejtjelezés visszafejtésére kidolgozott módszer. A módszert Friedrich Kasiski dolgozta ki és publikálta elsőként 1863-ban (Charles Babbage már 1846-ban is foglalkozott ezzel).

Egy ilyen polialfabetikus helyettesítő rejtjelezés a Vigenère rejtjel, amelyet sokáig feltörhetetlennek tartottak. A kódolás mivel minden betűhöz külön kódtáblát használ, a hagyományos gyakoriságelemzéssel (frequency analysis) csak nehezen törhető fel.

A rejtjel gyenge pontját a kódoláshoz használt kulcs hossza (pontosabban a rövidsége) jelenti. Amennyiben a kulcs hossza nagyságrendekkel rövidebb, mint a kódolandó szöveg, úgy a rejtjelben ismétlődések fordulhatnak elő. A rejtjelben található ismétlődések távolsága alapján meghatározható a kódoláshoz felhasznált kulcs hossza. A kulcs hosszának ismeretében a rejtjel gyakoriságelemzéssel könnyedén visszafejthető.

A feladatban az elemzést segítő függvényeket fogjuk megadni, azaz a titkosításhoz használt kulcs hosszát próbáljuk meghatározni.

A feladatban a Cipher a rejtjelezett szöveget jelenti, amelynek részleteit/szeleteit/szavait a Term típusszinonimával jelölünk.

type Cipher = String
type Term = String

Ismétlés keresése (1 pont)

Adjuk meg azt a műveletet, amely eldönti, hogy egy adott szöveg n hosszúságú kezdőszelete ismétlődik-e a kezdőszelet utáni részben!

Példa: Ha az "appleappletree" három hosszúságú kezdőszeletét tekintjük, akkor látható, hogy az "app" szó megtalálható a "leappletree" fennmaradó részben. Tehát a függvénynek igazat kell visszaadnia ebben az esetben.

Feltehetjük, hogy az n nemnegatív egész érték és kisebb a szöveg hosszánál!

Megjegyzés: A definícióban használhatjuk a Data.List modul isInfixOf vagy isPrefixOf műveletét!


Test>
True :: Bool
Test>
True :: Bool
Test>
False :: Bool
Test>
True :: Bool
Test>
False :: Bool

Összes n hosszúságú ismételt szelet (2 pont)

Definiáljuk azt a műveletet, amely megadja egy szöveg összes olyan n (pozitív egész) hosszúságú szeletét, amely ismétlődik a szövegben!

A következő megkötéseket vegyük figyelembe:


Test>
[] :: [Term]
Test>
[] :: [Term]
Test>
["ab"] :: [Term]
Test>
["ab", "bc", "ca"] :: [Term]
Test>
["ab", "bc"] :: [Term]
Test>
["aba", "bab", "aba", "bab", "aba"] :: [Term]
Test>
["ab", "ba"] :: [Term]

Ismételt szeletek (2 pont)

Rejtjelezés során egy vagy két betűs ismétlődések is előállhatnak véletlenül, amelyek eltorzíthatják az elemzést. Ezért az elemzés során a három, vagy ennél hosszabb ismétlődő szeleteket fogjuk figyelembe venni.

Állítsuk elő egy szöveg összes szeletét, amely legalább három hosszúságú és ismétlődik a listában!

Megjegyzés: A duplikátumok megszüntetésére használhatjuk a nub műveletet.


Test>
["aba", "bab", "abab"] :: [Term]
Test>
["aba", "bab", "abab", "baba", "ababa"] :: [Term]
Test>
["VHV", "HVS", "QUC", "UCE", "VHVS", "QUCE"] :: [Term]

Egy betűsorozat pozíciói (3 pont)

Adjuk meg azt a függvényt, amely a szövegen belül egy adott betűsorozat előfordulásainak összes kezdőpozícióját megadja!

Megjegyzés: A betűk indexelését 0-tól kezdjük!


Test>
[0, 2, 4, 6, 8] :: [Int]
Test>
[1, 3, 5, 7, 9] :: [Int]
Test>
[2, 18] :: [Int]
Test>
[10, 14] :: [Int]
Test>
[0, 16] :: [Int]

Szomszédos elemek különbsége (1 pont)

Definiáljuk azt a függvényt, amely megadja a szomszédos elemek közötti különbségeket!


Test>
[1, 1, 1, 1] :: [Int]
Test>
[4, 4] :: [Int]
Test>
[] :: [Int]
Test>
[12, 8, 4] :: [Int]

Ismétlődések távolságai (2 pont)

A következő lépés, hogy meghatározzuk az ismétlődő szeletek távolságait a rejtjelben. Ennek ismerete alapvető az elemzés szempontjából.

Egy szelet ismétlődéseinek távolságát úgy tudjuk megadni, hogy meghatározzuk annak pozícióit a rejtjelen belül és ezek különbségeit képezzük.

Definiáljuk azt a műveletet, amely az összes szelet szövegbeli előfordulásai közötti távolságot megadja egy listában!


Test>
[16, 16, 16, 16, 16, 16, 16, 16, 16, 16] :: [Int]
Test>
[18, 18, 30, 30, 18, 30] :: [Int]

Kettőnél nagyobb osztók meghatározása (1 pont)

Megjegyzés: a következő két függvényre együtt jár 1 pont.

Adjuk meg egy pozitív egész szám összes osztóját!


Test>
[1, 2, 4] :: [Int]
Test>
[1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60] :: [Int]
Test>
[1, 2, 5, 10] :: [Int]

Határozzuk meg egy lista összes elemének 2-nél nagyobb osztóit! Az osztókat egy rendezett listában adjuk meg!


Test>
[3, 3, 3, 4, 4, 5, 5, 6, 7, 8, 9, 10] :: [Int]
Test>
[3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12, 13, 13, 14, 15, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40] :: [Int]

Rejtjel elemzése és kulcs hosszának meghatározása (3 pont)

Az elemzés segítségével csak következtetni tudunk a kulcs lehetséges hosszára. Az elemzés sikeressége nagyban függ a rejtjel hosszától és az abban fellelhető ismétlődések számától.

A következő, egyben a feladat utolsó lépésében meghatározzuk az összes ismétlés távolságát és megadjuk ezek osztóit. Az osztók listáját tekintve, nagy valószínűséggel, a kulcs hosszát a legtöbbször előforduló osztó fogja megadni.

A további lépések:

Bevezetünk két új szinonimát, a KeyLength az osztó értékét, vagyis a kulcs méretét, a Frequency pedig az előfordulásának számát jelenti.


Test>
[(16, 10), (8, 10), (4, 10)] :: [(KeyLength, Frequency)]
Test>
[(15, 3), (5, 3), (3, 3)] :: [(KeyLength, Frequency)]
Test>
[(6, 6), (3, 6), (30, 3), (18, 3), (15, 3), (10, 3), (9, 3), (5, 3)] :: [(KeyLength, Frequency)]
Test>
[(6, 9), (3, 9), (18, 8), (9, 8), (8, 4), (4, 4), (24, 3), (12, 3), (72, 2), (36, 2), (198, 1), (162, 1), (112, 1), (99, 1), (81, 1), (66, 1), (56, 1), (54, 1), (33, 1), (28, 1), (27, 1), (22, 1), (16, 1), (14, 1), (11, 1), (7, 1)] :: [(KeyLength, Frequency)]

Összefoglaló

Az utolsó függvény eredményeiből látszik, hogy nem határozható meg mindig egyértelműen a kulcs hossza. Az elemzés akkor lehet sikeres, ha a kulcs rövid és a szövegben előfordulnak szóismétlések.

Ha a titkosításhoz véletlenszerű és legalább olyan hosszú kulcsot használunk, mint maga a titkosítandó szöveg, akkor a Vigenère rejtjelezés elméletileg feltörhetetlen rejtjelezési eljárás.

Pontozás (elmélet + gyakorlat)