168 lines
5.8 KiB
Ada
168 lines
5.8 KiB
Ada
with Ada.Text_IO;
|
|
|
|
procedure Vignere_Cryptanalysis is
|
|
|
|
subtype Letter is Character range 'A' .. 'Z';
|
|
|
|
function "+"(X, Y: Letter) return Letter is
|
|
begin
|
|
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
|
|
+ (Character'Pos(Y)-Character'Pos('A')) ) mod 26
|
|
+ Character'Pos('A'));
|
|
end;
|
|
|
|
function "-"(X, Y: Letter) return Letter is
|
|
begin
|
|
return Character'Val( ( (Character'Pos(X)-Character'Pos('A'))
|
|
- (Character'Pos(Y)-Character'Pos('A')) ) mod 26
|
|
+ Character'Pos('A'));
|
|
end;
|
|
|
|
type Frequency_Array is array (Letter) of Float;
|
|
|
|
English: Frequency_Array :=
|
|
( 0.08167, 0.01492, 0.02782, 0.04253, 0.12702, 0.02228, 0.02015,
|
|
0.06094, 0.06966, 0.00153, 0.00772, 0.04025, 0.02406, 0.06749,
|
|
0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09056, 0.02758,
|
|
0.00978, 0.02360, 0.00150, 0.01974, 0.00074 );
|
|
|
|
function Get_Frequency(S: String) return Frequency_Array is
|
|
Result: Frequency_Array := (others => 0.0);
|
|
Offset: Float := 1.0/Float(S'Length);
|
|
begin
|
|
for I in S'Range loop
|
|
if S(I) in Letter then
|
|
Result(S(I)) := Result(S(I)) + Offset;
|
|
end if;
|
|
end loop;
|
|
return Result;
|
|
end Get_Frequency;
|
|
|
|
function Remove_Whitespace(S: String) return String is
|
|
begin
|
|
if S="" then
|
|
return "";
|
|
elsif S(S'First) in Letter then
|
|
return S(S'First) & Remove_Whitespace(S(S'First+1 .. S'Last));
|
|
else
|
|
return Remove_Whitespace(S(S'First+1 .. S'Last));
|
|
end if;
|
|
end Remove_Whitespace;
|
|
|
|
function Distance(A, B: Frequency_Array;
|
|
Offset: Character := 'A') return Float is
|
|
Result: Float := 0.0;
|
|
Diff: Float;
|
|
begin
|
|
for C in A'Range loop
|
|
Diff := A(C+Offset) - B(C);
|
|
Result := Result + (Diff * Diff);
|
|
end loop;
|
|
return Result;
|
|
end Distance;
|
|
|
|
function Find_Key(Cryptogram: String; Key_Length: Positive) return String is
|
|
|
|
function Find_Caesar_Key(S: String) return Letter is
|
|
Frequency: Frequency_Array := Get_Frequency(S);
|
|
Candidate: Letter := 'A'; -- a fake candidate
|
|
Candidate_Dist : Float := Distance(Frequency, English, 'A');
|
|
New_Dist: Float;
|
|
|
|
begin
|
|
|
|
for L in Letter range 'B' .. 'Z' loop
|
|
New_Dist := Distance(Frequency, English, L);
|
|
if New_Dist <= Candidate_Dist then
|
|
Candidate_Dist := New_Dist;
|
|
Candidate := L;
|
|
end if;
|
|
end loop;
|
|
return Candidate;
|
|
end Find_Caesar_Key;
|
|
|
|
function Get_Slide(S: String; Step: Positive) return String is
|
|
begin
|
|
if S'Length= 0 then
|
|
return "";
|
|
else
|
|
return S(S'First) & Get_Slide(S(S'First+Step .. S'Last), Step);
|
|
end if;
|
|
end Get_Slide;
|
|
|
|
Key: String(1 .. Key_Length);
|
|
|
|
S: String renames Cryptogram;
|
|
|
|
begin
|
|
for I in Key'Range loop
|
|
Key(I) := Find_Caesar_Key(Get_Slide(S(S'First+I-1 .. S'Last),
|
|
Key_Length));
|
|
end loop;
|
|
return Key;
|
|
end Find_Key;
|
|
|
|
function Key_Char(Key: String; Index: Positive) return Letter is
|
|
begin
|
|
if Index > Key'Last then
|
|
return Key_Char(Key, Index-Key'Last);
|
|
else
|
|
return Key(Index);
|
|
end if;
|
|
end Key_Char;
|
|
|
|
Ciphertext: String := Remove_Whitespace(
|
|
"MOMUD EKAPV TQEFM OEVHP AJMII CDCTI FGYAG JSPXY ALUYM NSMYH" &
|
|
"VUXJE LEPXJ FXGCM JHKDZ RYICU HYPUS PGIGM OIYHF WHTCQ KMLRD" &
|
|
"ITLXZ LJFVQ GHOLW CUHLO MDSOE KTALU VYLNZ RFGBX PHVGA LWQIS" &
|
|
"FGRPH JOOFW GUBYI LAPLA LCAFA AMKLG CETDW VOELJ IKGJB XPHVG" &
|
|
"ALWQC SNWBU BYHCU HKOCE XJEYK BQKVY KIIEH GRLGH XEOLW AWFOJ" &
|
|
"ILOVV RHPKD WIHKN ATUHN VRYAQ DIVHX FHRZV QWMWV LGSHN NLVZS" &
|
|
"JLAKI FHXUF XJLXM TBLQV RXXHR FZXGV LRAJI EXPRV OSMNP KEPDT" &
|
|
"LPRWM JAZPK LQUZA ALGZX GVLKL GJTUI ITDSU REZXJ ERXZS HMPST" &
|
|
"MTEOE PAPJH SMFNB YVQUZ AALGA YDNMP AQOWT UHDBV TSMUE UIMVH" &
|
|
"QGVRW AEFSP EMPVE PKXZY WLKJA GWALT VYYOB YIXOK IHPDS EVLEV" &
|
|
"RVSGB JOGYW FHKBL GLXYA MVKIS KIEHY IMAPX UOISK PVAGN MZHPW" &
|
|
"TTZPV XFCCD TUHJH WLAPF YULTB UXJLN SIJVV YOVDJ SOLXG TGRVO" &
|
|
"SFRII CTMKO JFCQF KTINQ BWVHG TENLH HOGCS PSFPV GJOKM SIFPR" &
|
|
"ZPAAS ATPTZ FTPPD PORRF TAXZP KALQA WMIUD BWNCT LEFKO ZQDLX" &
|
|
"BUXJL ASIMR PNMBF ZCYLV WAPVF QRHZV ZGZEF KBYIO OFXYE VOWGB" &
|
|
"BXVCB XBAWG LQKCM ICRRX MACUO IKHQU AJEGL OIJHH XPVZW JEWBA" &
|
|
"FWAML ZZRXJ EKAHV FASMU LVVUT TGK");
|
|
|
|
Best_Plain: String := Ciphertext;
|
|
Best_Dist: Float := Distance(English, Get_Frequency(Best_Plain));
|
|
Best_Key: String := Ciphertext;
|
|
Best_Key_L: Natural := 0;
|
|
|
|
begin -- Vignere_Cryptanalysis
|
|
for I in 1 .. Ciphertext'Length/10 loop
|
|
declare
|
|
Key: String(1 .. I) := Find_Key(Ciphertext, I);
|
|
Plaintext: String(Ciphertext'Range);
|
|
begin
|
|
for I in Ciphertext'Range loop
|
|
Plaintext(I) := Ciphertext(I) - Key_Char(Key, I);
|
|
end loop;
|
|
if Distance(English, Get_Frequency(Plaintext)) < Best_Dist then
|
|
Best_Plain := Plaintext;
|
|
Best_Dist := Distance(English, Get_Frequency(Plaintext));
|
|
Best_Key(1 .. I) := Key;
|
|
Best_Key_L := I;
|
|
if Best_dist < 0.01 then
|
|
declare
|
|
use Ada.Text_IO;
|
|
begin
|
|
Put_Line("Key =" & Best_Key(1 .. Best_Key_L));
|
|
Put_Line("Distance = " & Float'Image(Best_Dist));
|
|
New_Line;
|
|
Put_Line("Plaintext =");
|
|
Put_Line(Best_Plain);
|
|
New_Line; New_Line;
|
|
end;
|
|
end if;
|
|
end if;
|
|
end;
|
|
end loop;
|
|
end Vignere_Cryptanalysis;
|