Data commit
This commit is contained in:
parent
7387c8f97b
commit
cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions
5
Task/Vigen-re-cipher-Cryptanalysis/00-META.yaml
Normal file
5
Task/Vigen-re-cipher-Cryptanalysis/00-META.yaml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
category:
|
||||
- Encryption
|
||||
from: http://rosettacode.org/wiki/Vigenère_cipher/Cryptanalysis
|
||||
note: Encryption
|
||||
30
Task/Vigen-re-cipher-Cryptanalysis/00-TASK.txt
Normal file
30
Task/Vigen-re-cipher-Cryptanalysis/00-TASK.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
Given some text you suspect has been encrypted with a Vigenère cipher, extract the key and plaintext. There are several methods for doing this. See [[wp:Vigenère_cipher#Cryptanalysis|the Wikipedia entry]] for more information. Use the following encrypted text:
|
||||
<pre>
|
||||
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
|
||||
</pre>
|
||||
|
||||
Letter frequencies for English can be found [[wp:Letter_frequency|here]].
|
||||
|
||||
Specifics for this task:
|
||||
* Take only the ciphertext as input. You can assume it's all capitalized and has no punctuation, but it might have whitespace.
|
||||
* Assume the plaintext is written in English.
|
||||
* Find and output the key.
|
||||
* Use that key to decrypt and output the original plaintext. Maintaining the whitespace from the ciphertext is optional.
|
||||
* The algorithm doesn't have to be perfect (which may not be possible) but it should work when given enough ciphertext. The example above is fairly long, and should be plenty for any algorithm.
|
||||
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
-V ascii_uppercase = Array(‘A’..‘Z’)
|
||||
|
||||
F vigenere_decrypt(target_freqs, input)
|
||||
V nchars = :ascii_uppercase.len
|
||||
V ordA = ‘A’.code
|
||||
V sorted_targets = sorted(target_freqs)
|
||||
|
||||
F frequency(input)
|
||||
V result = :ascii_uppercase.map(c -> (c, 0.0))
|
||||
L(c) input
|
||||
result[c - @ordA][1]++
|
||||
R result
|
||||
|
||||
F correlation(input)
|
||||
V result = 0.0
|
||||
V freq = sorted(@frequency(input), key' a -> a[1])
|
||||
|
||||
L(f) freq
|
||||
result += f[1] * @sorted_targets[L.index]
|
||||
R result
|
||||
|
||||
V cleaned = input.uppercase().filter(c -> c.is_uppercase()).map(c -> c.code)
|
||||
V best_len = 0
|
||||
V best_corr = -100.0
|
||||
|
||||
L(i) 2 .< cleaned.len I/ 20
|
||||
V pieces = [[Int]()] * i
|
||||
L(c) cleaned
|
||||
pieces[L.index % i].append(c)
|
||||
V corr = -0.5 * i + sum(pieces.map(p -> @correlation(p)))
|
||||
|
||||
I corr > best_corr
|
||||
best_len = i
|
||||
best_corr = corr
|
||||
|
||||
I best_len == 0
|
||||
R (‘Text is too short to analyze’, ‘’)
|
||||
|
||||
V pieces = [[Int]()] * best_len
|
||||
L(c) cleaned
|
||||
pieces[L.index % best_len].append(c)
|
||||
|
||||
V freqs = pieces.map(p -> @frequency(p))
|
||||
|
||||
V key = ‘’
|
||||
L(fr_) freqs
|
||||
V fr = sorted(fr_, key' a -> a[1], reverse' 1B)
|
||||
V m = 0
|
||||
V max_corr = 0.0
|
||||
L(j) 0 .< nchars
|
||||
V corr = 0.0
|
||||
V c = ordA + j
|
||||
L(frc) fr
|
||||
V d = (frc[0].code - c + nchars) % nchars
|
||||
corr += frc[1] * target_freqs[d]
|
||||
|
||||
I corr > max_corr
|
||||
m = j
|
||||
max_corr = corr
|
||||
|
||||
key ‘’= Char(code' m + ordA)
|
||||
|
||||
V r = (enumerate(cleaned).map((i, c) -> Char(code' (c - @key[i % @best_len].code + @nchars) % @nchars + @ordA)))
|
||||
R (key, r.join(‘’))
|
||||
|
||||
V encoded = ‘
|
||||
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’
|
||||
|
||||
V english_frequences = [
|
||||
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]
|
||||
|
||||
V (key, decoded) = vigenere_decrypt(english_frequences, encoded)
|
||||
print(‘Key: ’key)
|
||||
print("\nText: "decoded)
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,167 @@
|
|||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
using namespace std;
|
||||
|
||||
typedef array<pair<char, double>, 26> FreqArray;
|
||||
|
||||
class VigenereAnalyser
|
||||
{
|
||||
private:
|
||||
array<double, 26> targets;
|
||||
array<double, 26> sortedTargets;
|
||||
FreqArray freq;
|
||||
|
||||
// Update the freqs array
|
||||
FreqArray& frequency(const string& input)
|
||||
{
|
||||
for (char c = 'A'; c <= 'Z'; ++c)
|
||||
freq[c - 'A'] = make_pair(c, 0);
|
||||
|
||||
for (size_t i = 0; i < input.size(); ++i)
|
||||
freq[input[i] - 'A'].second++;
|
||||
|
||||
return freq;
|
||||
}
|
||||
|
||||
double correlation(const string& input)
|
||||
{
|
||||
double result = 0.0;
|
||||
frequency(input);
|
||||
|
||||
sort(freq.begin(), freq.end(), [](pair<char, double> u, pair<char, double> v)->bool
|
||||
{ return u.second < v.second; });
|
||||
|
||||
for (size_t i = 0; i < 26; ++i)
|
||||
result += freq[i].second * sortedTargets[i];
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public:
|
||||
VigenereAnalyser(const array<double, 26>& targetFreqs)
|
||||
{
|
||||
targets = targetFreqs;
|
||||
sortedTargets = targets;
|
||||
sort(sortedTargets.begin(), sortedTargets.end());
|
||||
}
|
||||
|
||||
pair<string, string> analyze(string input)
|
||||
{
|
||||
string cleaned;
|
||||
for (size_t i = 0; i < input.size(); ++i)
|
||||
{
|
||||
if (input[i] >= 'A' && input[i] <= 'Z')
|
||||
cleaned += input[i];
|
||||
else if (input[i] >= 'a' && input[i] <= 'z')
|
||||
cleaned += input[i] + 'A' - 'a';
|
||||
}
|
||||
|
||||
size_t bestLength = 0;
|
||||
double bestCorr = -100.0;
|
||||
|
||||
// Assume that if there are less than 20 characters
|
||||
// per column, the key's too long to guess
|
||||
for (size_t i = 2; i < cleaned.size() / 20; ++i)
|
||||
{
|
||||
vector<string> pieces(i);
|
||||
for (size_t j = 0; j < cleaned.size(); ++j)
|
||||
pieces[j % i] += cleaned[j];
|
||||
|
||||
// The correlation increases artificially for smaller
|
||||
// pieces/longer keys, so weigh against them a little
|
||||
double corr = -0.5*i;
|
||||
for (size_t j = 0; j < i; ++j)
|
||||
corr += correlation(pieces[j]);
|
||||
|
||||
if (corr > bestCorr)
|
||||
{
|
||||
bestLength = i;
|
||||
bestCorr = corr;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestLength == 0)
|
||||
return make_pair("Text is too short to analyze", "");
|
||||
|
||||
vector<string> pieces(bestLength);
|
||||
for (size_t i = 0; i < cleaned.size(); ++i)
|
||||
pieces[i % bestLength] += cleaned[i];
|
||||
|
||||
vector<FreqArray> freqs;
|
||||
for (size_t i = 0; i < bestLength; ++i)
|
||||
freqs.push_back(frequency(pieces[i]));
|
||||
|
||||
string key = "";
|
||||
for (size_t i = 0; i < bestLength; ++i)
|
||||
{
|
||||
sort(freqs[i].begin(), freqs[i].end(), [](pair<char, double> u, pair<char, double> v)->bool
|
||||
{ return u.second > v.second; });
|
||||
|
||||
size_t m = 0;
|
||||
double mCorr = 0.0;
|
||||
for (size_t j = 0; j < 26; ++j)
|
||||
{
|
||||
double corr = 0.0;
|
||||
char c = 'A' + j;
|
||||
for (size_t k = 0; k < 26; ++k)
|
||||
{
|
||||
int d = (freqs[i][k].first - c + 26) % 26;
|
||||
corr += freqs[i][k].second * targets[d];
|
||||
}
|
||||
|
||||
if (corr > mCorr)
|
||||
{
|
||||
m = j;
|
||||
mCorr = corr;
|
||||
}
|
||||
}
|
||||
|
||||
key += m + 'A';
|
||||
}
|
||||
|
||||
string result = "";
|
||||
for (size_t i = 0; i < cleaned.size(); ++i)
|
||||
result += (cleaned[i] - key[i % key.length()] + 26) % 26 + 'A';
|
||||
|
||||
return make_pair(result, key);
|
||||
}
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
string input =
|
||||
"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";
|
||||
|
||||
array<double, 26> english = {
|
||||
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};
|
||||
|
||||
VigenereAnalyser va(english);
|
||||
pair<string, string> output = va.analyze(input);
|
||||
|
||||
cout << "Key: " << output.second << endl << endl;
|
||||
cout << "Text: " << output.first << endl;
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
const char *encoded =
|
||||
"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";
|
||||
|
||||
const double freq[] = {
|
||||
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
|
||||
};
|
||||
|
||||
int best_match(const double *a, const double *b) {
|
||||
double sum = 0, fit, d, best_fit = 1e100;
|
||||
int i, rotate, best_rotate = 0;
|
||||
for (i = 0; i < 26; i++)
|
||||
sum += a[i];
|
||||
for (rotate = 0; rotate < 26; rotate++) {
|
||||
fit = 0;
|
||||
for (i = 0; i < 26; i++) {
|
||||
d = a[(i + rotate) % 26] / sum - b[i];
|
||||
fit += d * d / b[i];
|
||||
}
|
||||
|
||||
if (fit < best_fit) {
|
||||
best_fit = fit;
|
||||
best_rotate = rotate;
|
||||
}
|
||||
}
|
||||
|
||||
return best_rotate;
|
||||
}
|
||||
|
||||
double freq_every_nth(const int *msg, int len, int interval, char *key) {
|
||||
double sum, d, ret;
|
||||
double out[26], accu[26] = {0};
|
||||
int i, j, rot;
|
||||
|
||||
for (j = 0; j < interval; j++) {
|
||||
for (i = 0; i < 26; i++)
|
||||
out[i] = 0;
|
||||
for (i = j; i < len; i += interval)
|
||||
out[msg[i]]++;
|
||||
key[j] = rot = best_match(out, freq);
|
||||
key[j] += 'A';
|
||||
for (i = 0; i < 26; i++)
|
||||
accu[i] += out[(i + rot) % 26];
|
||||
}
|
||||
|
||||
for (i = 0, sum = 0; i < 26; i++)
|
||||
sum += accu[i];
|
||||
|
||||
for (i = 0, ret = 0; i < 26; i++) {
|
||||
d = accu[i] / sum - freq[i];
|
||||
ret += d * d / freq[i];
|
||||
}
|
||||
|
||||
key[interval] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main() {
|
||||
int txt[strlen(encoded)];
|
||||
int len = 0, j;
|
||||
char key[100];
|
||||
double fit, best_fit = 1e100;
|
||||
|
||||
for (j = 0; encoded[j] != '\0'; j++)
|
||||
if (isupper(encoded[j]))
|
||||
txt[len++] = encoded[j] - 'A';
|
||||
|
||||
for (j = 1; j < 30; j++) {
|
||||
fit = freq_every_nth(txt, len, j, key);
|
||||
printf("%f, key length: %2d, %s", fit, j, key);
|
||||
if (fit < best_fit) {
|
||||
best_fit = fit;
|
||||
printf(" <--- best so far");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
import std.stdio, std.algorithm, std.typecons, std.string,
|
||||
std.array, std.numeric, std.ascii;
|
||||
|
||||
string[2] vigenereDecrypt(in double[] targetFreqs, in string input) {
|
||||
enum nAlpha = std.ascii.uppercase.length;
|
||||
|
||||
static double correlation(in string txt, in double[] sTargets)
|
||||
pure nothrow /*@safe*/ @nogc {
|
||||
uint[nAlpha] charCounts = 0;
|
||||
foreach (immutable c; txt)
|
||||
charCounts[c - 'A']++;
|
||||
return charCounts[].sort().release.dotProduct(sTargets);
|
||||
}
|
||||
|
||||
static frequency(in string txt) pure nothrow @safe {
|
||||
auto freqs = new Tuple!(char,"c", uint,"d")[nAlpha];
|
||||
foreach (immutable i, immutable c; std.ascii.uppercase)
|
||||
freqs[i] = tuple(c, 0);
|
||||
foreach (immutable c; txt)
|
||||
freqs[c - 'A'].d++;
|
||||
return freqs;
|
||||
}
|
||||
|
||||
static string[2] decode(in string cleaned, in string key)
|
||||
pure nothrow @safe {
|
||||
assert(!key.empty);
|
||||
string decoded;
|
||||
foreach (immutable i, immutable c; cleaned)
|
||||
decoded ~= (c - key[i % $] + nAlpha) % nAlpha + 'A';
|
||||
return [key, decoded];
|
||||
}
|
||||
|
||||
static size_t findBestLength(in string cleaned,
|
||||
in double[] sTargets)
|
||||
pure nothrow /*@safe*/ {
|
||||
size_t bestLength;
|
||||
double bestCorr = -100.0;
|
||||
|
||||
// Assume that if there are less than 20 characters
|
||||
// per column, the key's too long to guess
|
||||
foreach (immutable i; 2 .. cleaned.length / 20) {
|
||||
auto pieces = new Appender!string[i];
|
||||
foreach (immutable j, immutable c; cleaned)
|
||||
pieces[j % i] ~= c;
|
||||
|
||||
// The correlation seems to increase for smaller
|
||||
// pieces/longer keys, so weigh against them a little
|
||||
double corr = -0.5 * i;
|
||||
foreach (const p; pieces)
|
||||
corr += correlation(p.data, sTargets);
|
||||
|
||||
if (corr > bestCorr) {
|
||||
bestLength = i;
|
||||
bestCorr = corr;
|
||||
}
|
||||
}
|
||||
|
||||
return bestLength;
|
||||
}
|
||||
|
||||
static string findKey(in string cleaned, in size_t bestLength,
|
||||
in double[] targetFreqs) pure nothrow @safe {
|
||||
auto pieces = new string[bestLength];
|
||||
foreach (immutable i, immutable c; cleaned)
|
||||
pieces[i % bestLength] ~= c;
|
||||
|
||||
string key;
|
||||
foreach (fr; pieces.map!frequency) {
|
||||
fr.sort!q{ a.d > b.d };
|
||||
|
||||
size_t m;
|
||||
double maxCorr = 0.0;
|
||||
foreach (immutable j, immutable c; uppercase) {
|
||||
double corr = 0.0;
|
||||
foreach (immutable frc; fr) {
|
||||
immutable di = (frc.c - c + nAlpha) % nAlpha;
|
||||
corr += frc.d * targetFreqs[di];
|
||||
}
|
||||
|
||||
if (corr > maxCorr) {
|
||||
m = j;
|
||||
maxCorr = corr;
|
||||
}
|
||||
}
|
||||
|
||||
key ~= m + 'A';
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
immutable cleaned = input.toUpper.removechars("^A-Z");
|
||||
|
||||
//immutable sortedTargets = targetFreqs.sorted;
|
||||
immutable sortedTargets = targetFreqs.dup.sort().release.idup;
|
||||
|
||||
immutable bestLength = findBestLength(cleaned, sortedTargets);
|
||||
if (bestLength == 0)
|
||||
throw new Exception("Text is too short to analyze.");
|
||||
|
||||
immutable string key = findKey(cleaned, bestLength, targetFreqs);
|
||||
return decode(cleaned, key);
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
immutable encoded = "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";
|
||||
|
||||
immutable englishFrequences = [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];
|
||||
|
||||
immutable key_dec = vigenereDecrypt(englishFrequences, encoded);
|
||||
writefln("Key: %s\n\nText: %s", key_dec[0], key_dec[1]);
|
||||
}
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var encoded =
|
||||
"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"
|
||||
|
||||
var freq = [26]float64{
|
||||
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,
|
||||
}
|
||||
|
||||
func sum(a []float64) (sum float64) {
|
||||
for _, f := range a {
|
||||
sum += f
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func bestMatch(a []float64) int {
|
||||
sum := sum(a)
|
||||
bestFit, bestRotate := 1e100, 0
|
||||
for rotate := 0; rotate < 26; rotate++ {
|
||||
fit := 0.0
|
||||
for i := 0; i < 26; i++ {
|
||||
d := a[(i+rotate)%26]/sum - freq[i]
|
||||
fit += d * d / freq[i]
|
||||
}
|
||||
if fit < bestFit {
|
||||
bestFit, bestRotate = fit, rotate
|
||||
}
|
||||
}
|
||||
return bestRotate
|
||||
}
|
||||
|
||||
func freqEveryNth(msg []int, key []byte) float64 {
|
||||
l := len(msg)
|
||||
interval := len(key)
|
||||
out := make([]float64, 26)
|
||||
accu := make([]float64, 26)
|
||||
for j := 0; j < interval; j++ {
|
||||
for k := 0; k < 26; k++ {
|
||||
out[k] = 0.0
|
||||
}
|
||||
for i := j; i < l; i += interval {
|
||||
out[msg[i]]++
|
||||
}
|
||||
rot := bestMatch(out)
|
||||
key[j] = byte(rot + 65)
|
||||
for i := 0; i < 26; i++ {
|
||||
accu[i] += out[(i+rot)%26]
|
||||
}
|
||||
}
|
||||
sum := sum(accu)
|
||||
ret := 0.0
|
||||
for i := 0; i < 26; i++ {
|
||||
d := accu[i]/sum - freq[i]
|
||||
ret += d * d / freq[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func decrypt(text, key string) string {
|
||||
var sb strings.Builder
|
||||
ki := 0
|
||||
for _, c := range text {
|
||||
if c < 'A' || c > 'Z' {
|
||||
continue
|
||||
}
|
||||
ci := (c - rune(key[ki]) + 26) % 26
|
||||
sb.WriteRune(ci + 65)
|
||||
ki = (ki + 1) % len(key)
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func main() {
|
||||
enc := strings.Replace(encoded, " ", "", -1)
|
||||
txt := make([]int, len(enc))
|
||||
for i := 0; i < len(txt); i++ {
|
||||
txt[i] = int(enc[i] - 'A')
|
||||
}
|
||||
bestFit, bestKey := 1e100, ""
|
||||
fmt.Println(" Fit Length Key")
|
||||
for j := 1; j <= 26; j++ {
|
||||
key := make([]byte, j)
|
||||
fit := freqEveryNth(txt, key)
|
||||
sKey := string(key)
|
||||
fmt.Printf("%f %2d %s", fit, j, sKey)
|
||||
if fit < bestFit {
|
||||
bestFit, bestKey = fit, sKey
|
||||
fmt.Print(" <--- best so far")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Println("\nBest key :", bestKey)
|
||||
fmt.Printf("\nDecrypted text:\n%s\n", decrypt(enc, bestKey))
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
{-# LANGUAGE TupleSections #-}
|
||||
import Data.List(transpose, nub, sort, maximumBy)
|
||||
import Data.Ord (comparing)
|
||||
import Data.Char (ord)
|
||||
import Data.Map (Map, fromListWith, toList, findWithDefault)
|
||||
|
||||
average :: Fractional a => [a] -> a
|
||||
average as = sum as / fromIntegral (length as)
|
||||
|
||||
-- Create a map from each entry in list to the number of occurrences of
|
||||
-- that entry in the list.
|
||||
countEntries :: Ord a => [a] -> Map a Int
|
||||
countEntries = fromListWith (+) . fmap (,1)
|
||||
|
||||
-- Break a string up into substrings of n chars.
|
||||
breakup :: Int -> [a] -> [[a]]
|
||||
breakup _ [] = []
|
||||
breakup n as =
|
||||
let (h, r) = splitAt n as
|
||||
in h:breakup n r
|
||||
|
||||
-- Dole out elements of a string over a n element distribution.
|
||||
distribute :: [a] -> Int -> [[a]]
|
||||
distribute as n = transpose $ breakup n as
|
||||
|
||||
-- The probability that members of a pair of characters taken randomly
|
||||
-- from a given string are equal.
|
||||
coincidence :: (Ord a, Fractional b) => [a] -> b
|
||||
coincidence str =
|
||||
let charCounts = snd <$> toList (countEntries str)
|
||||
strln = length str
|
||||
d = fromIntegral $ strln * (strln - 1)
|
||||
n = fromIntegral $ sum $ fmap (\cc -> cc * (cc-1)) charCounts
|
||||
in n / d
|
||||
|
||||
-- Use the average probablity of coincidence for all the members of
|
||||
-- a distribution to rate the distribution - the higher the better.
|
||||
-- The correlation increases artificially for smaller
|
||||
-- pieces/longer keys, so weigh against them a little
|
||||
rate :: (Ord a, Fractional b) => [[a]] -> b
|
||||
rate d = average (fmap coincidence d) - fromIntegral (length d) / 3000.0
|
||||
|
||||
-- Multiply elements of lists together and add up the results.
|
||||
dot :: Num a => [a] -> [a] -> a
|
||||
dot v0 v1 = sum $ zipWith (*) v0 v1
|
||||
|
||||
-- Given two lists of floats, rotate one of them by the number of
|
||||
-- characters indicated by letter and then 'dot' them together.
|
||||
rotateAndDot :: Num a => [a] -> [a] -> Char -> a
|
||||
rotateAndDot v0 v1 letter = dot v0 (drop (ord letter - ord 'A') (cycle v1))
|
||||
|
||||
-- Find decoding offset that results in best match
|
||||
-- between actual char frequencies and expected frequencies.
|
||||
getKeyChar :: RealFrac a => [a] -> String -> Char
|
||||
getKeyChar expected sample =
|
||||
let charCounts = countEntries sample
|
||||
countInSample c = findWithDefault 0 c charCounts
|
||||
actual = fmap (fromIntegral . countInSample) ['A'..'Z']
|
||||
in maximumBy (comparing $ rotateAndDot expected actual) ['A'..'Z']
|
||||
|
||||
main = do
|
||||
let cr = filter (/=' ') crypt
|
||||
-- Assume that if there are less than 20 characters
|
||||
-- per column, the key's too long to guess
|
||||
distributions = fmap (distribute cr) [1..length cr `div` 20]
|
||||
bestDistribution = maximumBy (comparing rate) distributions
|
||||
key = fmap (getKeyChar englishFrequencies) bestDistribution
|
||||
alphaSum a b = ['A'..'Z'] !! ((ord b - ord a) `mod` 26)
|
||||
mapM_ putStrLn ["Key: " ++ key, "Decrypted Text: " ++ zipWith alphaSum (cycle key) cr]
|
||||
|
||||
englishFrequencies =
|
||||
[ 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 ]
|
||||
|
||||
crypt = "\
|
||||
\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\
|
||||
\"
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
NB. https://en.wikipedia.org/wiki/Kasiski_examination
|
||||
kasiski=: {{
|
||||
grams=. ({: #"1~1 < ;@{.)|:(#/.~;"0~.) g=. 3 <\ y
|
||||
deltas=. ;grams (2 -~/\ I.@E.)L:0 enc
|
||||
{:,{.\:~(#/.~,.~.)1 -.~,+./~ deltas
|
||||
}}
|
||||
|
||||
NB. https://en.wikipedia.org/wiki/Letter_frequency
|
||||
AZ=: 8 u: 65+i.26
|
||||
lfreq=: 0.01*do{{)n
|
||||
8.2 1.5 2.8 4.3 13 2.2 2 6.1 7 0.15
|
||||
0.77 4 2.4 6.7 7.5 1.9 0.095 6 6.3 9.1
|
||||
2.8 0.98 2.4 0.15 2 0.074
|
||||
}}-.LF
|
||||
|
||||
|
||||
caesarkey=: {{
|
||||
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ
|
||||
AZ{~(i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs
|
||||
}}
|
||||
vigenerekey=: {{ caesarkey"1|:(-kasiski y) ]\y }}
|
||||
|
||||
uncaesar=: {{ 26&|@-&(AZ i.x)&.(AZ&i.) y }}"0 1
|
||||
unvigenere=: {{ ' '-.~,x uncaesar"0 1&.|:(-#x) ]\y }}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
enc=: {{)n
|
||||
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
|
||||
}}-.LF,' '
|
||||
|
||||
vigenerekey enc
|
||||
THECHESHIRECAT
|
||||
_80]\'THECHESHIRECAT' unvigenere enc
|
||||
THISWASTHEPOEMTHATALICEREADJABBERWOCKYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMB
|
||||
LEINTHEWABEALLMIMSYWERETHEBOROGOVESANDTHEMOMERATHSOUTGRABEBEWARETHEJABBERWOCKMYS
|
||||
ONTHEJAWSTHATBITETHECLAWSTHATCATCHBEWARETHEJUBJUBBIRDANDSHUNTHEFRUMIOUSBANDERSNA
|
||||
TCHHETOOKHISVORPALSWORDINHANDLONGTIMETHEMANXOMEFOEHESOUGHTSORESTEDHEBYTHETUMTUMT
|
||||
REEANDSTOODAWHILEINTHOUGHTANDASINUFFISHTHOUGHTHESTOODTHEJABBERWOCKWITHEYESOFFLAM
|
||||
ECAMEWHIFFLINGTHROUGHTHETULGEYWOODANDBURBLEDASITCAMEONETWOONETWOANDTHROUGHANDTHR
|
||||
OUGHTHEVORPALBLADEWENTSNICKERSNACKHELEFTITDEADANDWITHITSHEADHEWENTGALUMPHINGBACK
|
||||
ANDHASTTHOUSLAINTHEJABBERWOCKCOMETOMYARMSMYBEAMISHBOYOFRABJOUSDAYCALLOOHCALLAYHE
|
||||
CHORTLEDINHISJOYTWASBRILLIGANDTHESLITHYTOVESDIDGYREANDGIMBLEINTHEWABEALLMIMSYWER
|
||||
ETHEBOROGOVESANDTHEMOMERATHSOUTGRABEITSEEMSVERYPRETTYSHESAIDWHENSHEHADFINISHEDIT
|
||||
BUTITSRATHERHARDTOUNDERSTANDWYTWITSJWYAH
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
decaesar=: {{
|
||||
freqs=. (<:#/.~AZ,y)%#y=. y ([-.-.) AZ
|
||||
ndx=. (i. <./)lfreq +/&.:*:@:-"1 (i.26)|."0 1 freqs
|
||||
26&|@-&ndx&.(AZ&i.) y
|
||||
}}
|
||||
devigenere=: {{ ' '-.~,decaesar"1&.|:(-kasiski y) ]\y }}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
public class Vig{
|
||||
static String encodedMessage =
|
||||
"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";
|
||||
|
||||
final static double freq[] = {
|
||||
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
|
||||
};
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
int lenghtOfEncodedMessage = encodedMessage.length();
|
||||
char[] encoded = new char [lenghtOfEncodedMessage] ;
|
||||
char[] key = new char [lenghtOfEncodedMessage] ;
|
||||
|
||||
encodedMessage.getChars(0, lenghtOfEncodedMessage, encoded, 0);
|
||||
int txt[] = new int[lenghtOfEncodedMessage];
|
||||
int len = 0, j;
|
||||
|
||||
double fit, best_fit = 1e100;
|
||||
|
||||
for (j = 0; j < lenghtOfEncodedMessage; j++)
|
||||
if (Character.isUpperCase(encoded[j]))
|
||||
txt[len++] = encoded[j] - 'A';
|
||||
|
||||
for (j = 1; j < 30; j++) {
|
||||
fit = freq_every_nth(txt, len, j, key);
|
||||
System.out.printf("%f, key length: %2d ", fit, j);
|
||||
System.out.print(key);
|
||||
if (fit < best_fit) {
|
||||
best_fit = fit;
|
||||
System.out.print(" <--- best so far");
|
||||
}
|
||||
System.out.print("\n");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static String decrypt(String text, final String key) {
|
||||
String res = "";
|
||||
text = text.toUpperCase();
|
||||
for (int i = 0, j = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if (c < 'A' || c > 'Z') continue;
|
||||
res += (char)((c - key.charAt(j) + 26) % 26 + 'A');
|
||||
j = ++j % key.length();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int best_match(final double []a, final double []b) {
|
||||
double sum = 0, fit, d, best_fit = 1e100;
|
||||
int i, rotate, best_rotate = 0;
|
||||
for (i = 0; i < 26; i++)
|
||||
sum += a[i];
|
||||
for (rotate = 0; rotate < 26; rotate++) {
|
||||
fit = 0;
|
||||
for (i = 0; i < 26; i++) {
|
||||
d = a[(i + rotate) % 26] / sum - b[i];
|
||||
fit += d * d / b[i];
|
||||
}
|
||||
|
||||
if (fit < best_fit) {
|
||||
best_fit = fit;
|
||||
best_rotate = rotate;
|
||||
}
|
||||
}
|
||||
|
||||
return best_rotate;
|
||||
}
|
||||
|
||||
static double freq_every_nth(final int []msg, int len, int interval, char[] key) {
|
||||
double sum, d, ret;
|
||||
double [] accu = new double [26];
|
||||
double [] out = new double [26];
|
||||
int i, j, rot;
|
||||
|
||||
for (j = 0; j < interval; j++) {
|
||||
for (i = 0; i < 26; i++)
|
||||
out[i] = 0;
|
||||
for (i = j; i < len; i += interval)
|
||||
out[msg[i]]++;
|
||||
rot = best_match(out, freq);
|
||||
try{
|
||||
key[j] = (char)(rot + 'A');
|
||||
} catch (Exception e) {
|
||||
System.out.print(e.getMessage());
|
||||
}
|
||||
for (i = 0; i < 26; i++)
|
||||
accu[i] += out[(i + rot) % 26];
|
||||
}
|
||||
|
||||
for (i = 0, sum = 0; i < 26; i++)
|
||||
sum += accu[i];
|
||||
|
||||
for (i = 0, ret = 0; i < 26; i++) {
|
||||
d = accu[i] / sum - freq[i];
|
||||
ret += d * d / freq[i];
|
||||
}
|
||||
|
||||
key[interval] = '\0';
|
||||
return ret;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,207 @@
|
|||
# ciphertext block {{{1
|
||||
const ciphertext = filter(isalpha, """
|
||||
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
|
||||
""")
|
||||
# }}}
|
||||
|
||||
# character frequencies {{{1
|
||||
const letters = Dict{Char, Float32}(
|
||||
'E' => 12.702,
|
||||
'T' => 9.056,
|
||||
'A' => 8.167,
|
||||
'O' => 7.507,
|
||||
'I' => 6.966,
|
||||
'N' => 6.749,
|
||||
'S' => 6.327,
|
||||
'H' => 6.094,
|
||||
'R' => 5.987,
|
||||
'D' => 4.253,
|
||||
'L' => 4.025,
|
||||
'C' => 2.782,
|
||||
'U' => 2.758,
|
||||
'M' => 2.406,
|
||||
'W' => 2.361,
|
||||
'F' => 2.228,
|
||||
'G' => 2.015,
|
||||
'Y' => 1.974,
|
||||
'P' => 1.929,
|
||||
'B' => 1.492,
|
||||
'V' => 0.978,
|
||||
'K' => 0.772,
|
||||
'J' => 0.153,
|
||||
'X' => 0.150,
|
||||
'Q' => 0.095,
|
||||
'Z' => 0.074)
|
||||
const digraphs = Dict{AbstractString, Float32}(
|
||||
"TH" => 15.2,
|
||||
"HE" => 12.8,
|
||||
"IN" => 9.4,
|
||||
"ER" => 9.4,
|
||||
"AN" => 8.2,
|
||||
"RE" => 6.8,
|
||||
"ND" => 6.3,
|
||||
"AT" => 5.9,
|
||||
"ON" => 5.7,
|
||||
"NT" => 5.6,
|
||||
"HA" => 5.6,
|
||||
"ES" => 5.6,
|
||||
"ST" => 5.5,
|
||||
"EN" => 5.5,
|
||||
"ED" => 5.3,
|
||||
"TO" => 5.2,
|
||||
"IT" => 5.0,
|
||||
"OU" => 5.0,
|
||||
"EA" => 4.7,
|
||||
"HI" => 4.6,
|
||||
"IS" => 4.6,
|
||||
"OR" => 4.3,
|
||||
"TI" => 3.4,
|
||||
"AS" => 3.3,
|
||||
"TE" => 2.7,
|
||||
"ET" => 1.9,
|
||||
"NG" => 1.8,
|
||||
"OF" => 1.6,
|
||||
"AL" => 0.9,
|
||||
"DE" => 0.9,
|
||||
"SE" => 0.8,
|
||||
"LE" => 0.8,
|
||||
"SA" => 0.6,
|
||||
"SI" => 0.5,
|
||||
"AR" => 0.4,
|
||||
"VE" => 0.4,
|
||||
"RA" => 0.4,
|
||||
"LD" => 0.2,
|
||||
"UR" => 0.2)
|
||||
const trigraphs = Dict{AbstractString, Float32}(
|
||||
"THE" => 18.1,
|
||||
"AND" => 7.3,
|
||||
"ING" => 7.2,
|
||||
"ION" => 4.2,
|
||||
"ENT" => 4.2,
|
||||
"HER" => 3.6,
|
||||
"FOR" => 3.4,
|
||||
"THA" => 3.3,
|
||||
"NTH" => 3.3,
|
||||
"INT" => 3.2,
|
||||
"TIO" => 3.1,
|
||||
"ERE" => 3.1,
|
||||
"TER" => 3.0,
|
||||
"EST" => 2.8,
|
||||
"ERS" => 2.8,
|
||||
"HAT" => 2.6,
|
||||
"ATI" => 2.6,
|
||||
"ATE" => 2.5,
|
||||
"ALL" => 2.5,
|
||||
"VER" => 2.4,
|
||||
"HIS" => 2.4,
|
||||
"HES" => 2.4,
|
||||
"ETH" => 2.4,
|
||||
"OFT" => 2.2,
|
||||
"STH" => 2.1,
|
||||
"RES" => 2.1,
|
||||
"OTH" => 2.1,
|
||||
"ITH" => 2.1,
|
||||
"FTH" => 2.1,
|
||||
"ONT" => 2.0)
|
||||
# 1}}}
|
||||
|
||||
function decrypt(enc::ASCIIString, key::ASCIIString)
|
||||
const enclen = length(enc)
|
||||
const keylen = length(key)
|
||||
|
||||
if keylen < enclen
|
||||
key = (key^(div(enclen - keylen, keylen) + 2))[1:enclen]
|
||||
end
|
||||
|
||||
msg = Array(Char, enclen)
|
||||
|
||||
for i=1:enclen
|
||||
msg[i] = Char((Int(enc[i]) - Int(key[i]) + 26) % 26 + 65)
|
||||
end
|
||||
|
||||
msg::Array{Char, 1}
|
||||
end
|
||||
|
||||
function cryptanalyze(enc::ASCIIString; maxkeylen::Integer = 20)
|
||||
const enclen = length(enc)
|
||||
maxkey = ""
|
||||
maxdec = ""
|
||||
maxscore = 0.0
|
||||
|
||||
for keylen=1:maxkeylen
|
||||
key = Array(Char, keylen)
|
||||
idx = filter(x -> x % keylen == 0, 1:enclen) - keylen + 1
|
||||
|
||||
for i=1:keylen
|
||||
maxsubscore = 0.0
|
||||
|
||||
for j='A':'Z'
|
||||
subscore = 0.0
|
||||
|
||||
for k in decrypt(enc[idx], ascii(string(j)))
|
||||
subscore += get(letters, k, 0.0)
|
||||
end
|
||||
|
||||
if subscore > maxsubscore
|
||||
maxsubscore = subscore
|
||||
key[i] = j
|
||||
end
|
||||
end
|
||||
|
||||
idx += 1
|
||||
end
|
||||
|
||||
key = join(key)
|
||||
const dec = decrypt(enc, key)
|
||||
score = 0.0
|
||||
|
||||
for i in dec
|
||||
score += get(letters, i, 0.0)
|
||||
end
|
||||
|
||||
for i=1:enclen - 2
|
||||
const digraph = string(dec[i], dec[i + 1])
|
||||
const trigraph = string(dec[i], dec[i + 1], dec[i + 2])
|
||||
|
||||
if haskey(digraphs, digraph)
|
||||
score += 2 * get(digraphs, digraph, 0.0)
|
||||
end
|
||||
|
||||
if haskey(trigraphs, trigraph)
|
||||
score += 3 * get(trigraphs, trigraph, 0.0)
|
||||
end
|
||||
end
|
||||
|
||||
if score > maxscore
|
||||
maxscore = score
|
||||
maxkey = key
|
||||
maxdec = dec
|
||||
end
|
||||
end
|
||||
|
||||
(maxkey, join(maxdec))::Tuple{ASCIIString, ASCIIString}
|
||||
end
|
||||
|
||||
key, dec = cryptanalyze(ciphertext)
|
||||
println("key: ", key, "\n\n", dec)
|
||||
|
||||
# post-compilation profiling run
|
||||
gc()
|
||||
t = @elapsed cryptanalyze(ciphertext)
|
||||
println("\nelapsed time: ", t, " seconds")
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// version 1.1.3
|
||||
|
||||
val encoded =
|
||||
"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"
|
||||
|
||||
val freq = doubleArrayOf(
|
||||
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
|
||||
)
|
||||
|
||||
fun bestMatch(a: DoubleArray): Int {
|
||||
val sum = a.sum()
|
||||
var bestFit = 1e100
|
||||
var bestRotate = 0
|
||||
for (rotate in 0..25) {
|
||||
var fit = 0.0
|
||||
for (i in 0..25) {
|
||||
val d = a[(i + rotate) % 26] / sum - freq[i]
|
||||
fit += d * d / freq[i]
|
||||
}
|
||||
if (fit < bestFit) {
|
||||
bestFit = fit
|
||||
bestRotate = rotate
|
||||
}
|
||||
}
|
||||
return bestRotate
|
||||
}
|
||||
|
||||
fun freqEveryNth(msg: IntArray, key: CharArray): Double {
|
||||
val len = msg.size
|
||||
val interval = key.size
|
||||
val out = DoubleArray(26)
|
||||
val accu = DoubleArray(26)
|
||||
for (j in 0 until interval) {
|
||||
out.fill(0.0)
|
||||
for (i in j until len step interval) out[msg[i]]++
|
||||
val rot = bestMatch(out)
|
||||
key[j] = (rot + 65).toChar()
|
||||
for (i in 0..25) accu[i] += out[(i + rot) % 26]
|
||||
}
|
||||
val sum = accu.sum()
|
||||
var ret = 0.0
|
||||
for (i in 0..25) {
|
||||
val d = accu[i] / sum - freq[i]
|
||||
ret += d * d / freq[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fun decrypt(text: String, key: String): String {
|
||||
val sb = StringBuilder()
|
||||
var ki = 0
|
||||
for (c in text) {
|
||||
if (c !in 'A'..'Z') continue
|
||||
val ci = (c.toInt() - key[ki].toInt() + 26) % 26
|
||||
sb.append((ci + 65).toChar())
|
||||
ki = (ki + 1) % key.length
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
val enc = encoded.replace(" ", "")
|
||||
val txt = IntArray(enc.length) { enc[it] - 'A' }
|
||||
var bestFit = 1e100
|
||||
var bestKey = ""
|
||||
val f = "%f %2d %s"
|
||||
println(" Fit Length Key")
|
||||
for (j in 1..26) {
|
||||
val key = CharArray(j)
|
||||
val fit = freqEveryNth(txt, key)
|
||||
val sKey = key.joinToString("")
|
||||
print(f.format(fit, j, sKey))
|
||||
if (fit < bestFit) {
|
||||
bestFit = fit
|
||||
bestKey = sKey
|
||||
print(" <--- best so far")
|
||||
}
|
||||
println()
|
||||
}
|
||||
println()
|
||||
println("Best key : $bestKey")
|
||||
println("\nDecrypted text:\n${decrypt(enc, bestKey)}")
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import sequtils, strutils, sugar, tables, times
|
||||
|
||||
const
|
||||
|
||||
CipherText = """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""".splitWhitespace.join()
|
||||
|
||||
FreqLetters = {'E': 12.702, 'T': 9.056, 'A': 8.167, 'O': 7.507,
|
||||
'I': 6.966, 'N': 6.749, 'S': 6.327, 'H': 6.094,
|
||||
'R': 5.987, 'D': 4.253, 'L': 4.025, 'C': 2.782,
|
||||
'U': 2.758, 'M': 2.406, 'W': 2.361, 'F': 2.228,
|
||||
'G': 2.015, 'Y': 1.974, 'P': 1.929, 'B': 1.492,
|
||||
'V': 0.978, 'K': 0.772, 'J': 0.153, 'X': 0.150,
|
||||
'Q': 0.095, 'Z': 0.074}.toTable
|
||||
|
||||
FreqDigraphs = {"TH": 15.2, "HE": 12.8, "IN": 9.4, "ER": 9.4,
|
||||
"AN": 8.2, "RE": 6.8, "ND": 6.3, "AT": 5.9,
|
||||
"ON": 5.7, "NT": 5.6, "HA": 5.6, "ES": 5.6,
|
||||
"ST": 5.5, "EN": 5.5, "ED": 5.3, "TO": 5.2,
|
||||
"IT": 5.0, "OU": 5.0, "EA": 4.7, "HI": 4.6,
|
||||
"IS": 4.6, "OR": 4.3, "TI": 3.4, "AS": 3.3,
|
||||
"TE": 2.7, "ET": 1.9, "NG": 1.8, "OF": 1.6,
|
||||
"AL": 0.9, "DE": 0.9, "SE": 0.8, "LE": 0.8,
|
||||
"SA": 0.6, "SI": 0.5, "AR": 0.4, "VE": 0.4,
|
||||
"RA": 0.4, "LD": 0.2, "UR": 0.2}.toTable
|
||||
|
||||
FreqTrigraphs = {"THE": 18.1, "AND": 7.3, "ING": 7.2, "ION": 4.2,
|
||||
"ENT": 4.2, "HER": 3.6, "FOR": 3.4, "THA": 3.3,
|
||||
"NTH": 3.3, "INT": 3.2, "TIO": 3.1, "ERE": 3.1,
|
||||
"TER": 3.0, "EST": 2.8, "ERS": 2.8, "HAT": 2.6,
|
||||
"ATI": 2.6, "ATE": 2.5, "ALL": 2.5, "VER": 2.4,
|
||||
"HIS": 2.4, "HES": 2.4, "ETH": 2.4, "OFT": 2.2,
|
||||
"STH": 2.1, "RES": 2.1, "OTH": 2.1, "ITH": 2.1,
|
||||
"FTH": 2.1, "ONT": 2.0}.toTable
|
||||
|
||||
func decrypt(enc, key: string): string =
|
||||
let encLen = enc.len
|
||||
let keyLen = key.len
|
||||
result.setLen(encLen)
|
||||
var k = 0
|
||||
for i in 0..<encLen:
|
||||
result[i] = chr((ord(enc[i]) - ord(key[k]) + 26) mod 26 + ord('A'))
|
||||
k = (k + 1) mod keyLen
|
||||
|
||||
func cryptanalyze(enc: string; maxKeyLen = 20): tuple[maxKey, maxDec: string] =
|
||||
let encLen = enc.len
|
||||
var maxScore = 0.0
|
||||
|
||||
for keyLen in 1..maxKeyLen:
|
||||
var key = newString(keyLen)
|
||||
var idx = collect(newSeq):
|
||||
for i in 1..encLen:
|
||||
if i mod keyLen == 0:
|
||||
i - keyLen
|
||||
|
||||
for i in 0..<keyLen:
|
||||
var maxSubscore = 0.0
|
||||
for j in 'A'..'Z':
|
||||
var subscore = 0.0
|
||||
let encidx = idx.mapIt(enc[it]).join()
|
||||
for k in decrypt(encidx, $j):
|
||||
subscore += FreqLetters[k]
|
||||
if subscore > maxSubscore:
|
||||
maxSubscore = subscore
|
||||
key[i] = j
|
||||
for item in idx.mitems: inc item
|
||||
|
||||
let dec = decrypt(enc, key)
|
||||
var score = 0.0
|
||||
for i in dec:
|
||||
score += FreqLetters[i]
|
||||
|
||||
for i in 0..(encLen - 3):
|
||||
let digraph = dec[i..(i+1)]
|
||||
let trigraph = dec[i..(i+2)]
|
||||
score += 2 * FreqDigraphs.getOrDefault(digraph)
|
||||
score += 3 * FreqTrigraphs.getOrDefault(trigraph)
|
||||
|
||||
if score > maxScore:
|
||||
maxScore = score
|
||||
result.maxKey = key
|
||||
result.maxDec = dec
|
||||
|
||||
let t0 = cpuTime()
|
||||
let (key, dec) = CipherText.cryptanalyze()
|
||||
echo "key: ", key, '\n'
|
||||
echo dec, '\n'
|
||||
echo "Elapsed time: ", (cpuTime() - t0).formatFloat(ffDecimal, precision = 3), " s"
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
(* Task : Vigenere cipher/Cryptanalysis *)
|
||||
|
||||
(*
|
||||
Given some text you suspect has been encrypted
|
||||
with a Vigenère cipher, extract the key and plaintext.
|
||||
Uses correlation factors similar to other solutions.
|
||||
(originally tried Friedman test, didn't produce good result)
|
||||
|
||||
Coded in a way that allows non-english (by passing frequencies).
|
||||
*)
|
||||
|
||||
(*** Helpers ***)
|
||||
|
||||
(* Implementation of Float.round to avoid v4.08 *)
|
||||
let round (x : float) : float =
|
||||
let rem = mod_float x 1. in
|
||||
if rem >= 0.5
|
||||
then ceil x
|
||||
else floor x
|
||||
|
||||
(* A function that updates array element at a position *)
|
||||
let array_update (arr : 'a array) (idx : int) (update : 'a -> 'a) : unit =
|
||||
let curr = Array.get arr idx in
|
||||
Array.set arr idx (update curr)
|
||||
|
||||
(*** Actual task at hand ***)
|
||||
|
||||
(* the n'th element of array is how often the n'th letter was found *)
|
||||
let observe_coincidences ?(step : int = 1) ?(offset : int = 0) (text : string) : int array =
|
||||
let arr = Array.make 26 0 in
|
||||
let a_code = Char.code 'A' in
|
||||
String.iteri (fun idx c -> if idx mod step = offset then array_update arr (Char.code c - a_code) succ) text;
|
||||
arr
|
||||
|
||||
(* Obtain correlation factor for the observed coincidences *)
|
||||
let correlation_factor ?(sort : bool = true) (coincidences : int array) (freqs : float list) : float =
|
||||
let clist = Array.to_list coincidences in
|
||||
let clist = (if sort then List.sort compare clist else clist) in
|
||||
List.fold_left2 (fun acc c f -> acc +. (float_of_int c *. f)) 0. clist freqs
|
||||
|
||||
(* Translation of the test used in other Rosetta Code solutions *)
|
||||
let shifted_coincidences_test (freqs : float list) (text : string) : int =
|
||||
let sorted_freqs = List.sort compare freqs in
|
||||
let bestCorr = -100. in
|
||||
let max_keylen = String.length text / 20 in
|
||||
let rec helper idx (cur_len, cur_corr) (best_len, best_corr) =
|
||||
if cur_len = max_keylen then (* Finished testing everything *)
|
||||
best_len
|
||||
else if idx = cur_len then (* Finished testing this key length *)
|
||||
let (best_len, best_corr) = if cur_corr > best_corr then (cur_len, cur_corr) else (best_len, best_corr) in
|
||||
helper 0 (cur_len + 1, ~-.0.5 *. float_of_int (cur_len + 1)) (best_len, best_corr)
|
||||
else
|
||||
let coincidences = observe_coincidences ~step:cur_len ~offset:idx text in
|
||||
let factor = correlation_factor coincidences sorted_freqs in
|
||||
helper (succ idx) (cur_len, cur_corr +. factor) (best_len, best_corr)
|
||||
in
|
||||
helper 0 (2, ~-.1.) (1, ~-.100.)
|
||||
|
||||
(* Returns the most likely shift value for this set *)
|
||||
let break_caesar ?(step : int = 1) ?(offset : int = 0) (text : string) (freqs : float list) : int =
|
||||
let c_arr = observe_coincidences ~step ~offset text in
|
||||
let rec helper l curShift (maxShift, maxCorr) =
|
||||
if curShift = 26
|
||||
then maxShift
|
||||
else
|
||||
let corr = correlation_factor ~sort:false c_arr l in
|
||||
let l' = List.tl l @ [List.hd l] in
|
||||
if corr > maxCorr
|
||||
then helper l' (curShift + 1) (curShift, corr)
|
||||
else helper l' (curShift + 1) (maxShift, maxCorr)
|
||||
in
|
||||
helper freqs 0 (-1, -100.)
|
||||
|
||||
let break (keylen : int) (text : string) (freqs : float list) : key =
|
||||
let rec getCaesars idx acc =
|
||||
if idx >= keylen then acc else
|
||||
let shift = break_caesar ~step:keylen ~offset:idx text freqs in
|
||||
let new_code = if shift = 0 then Char.code 'A' else Char.code 'Z' + 1 - shift in
|
||||
getCaesars (succ idx) (acc ^ Char.(new_code |> chr |> escaped))
|
||||
in
|
||||
getCaesars 0 ""
|
||||
|
||||
let cryptanalyze (freqs : float list) (text : string) : key * string =
|
||||
let text = ascii_upper_letters_only text in
|
||||
let keylen = shifted_coincidences_test freqs text in
|
||||
let key = break keylen text freqs in
|
||||
let pt = decrypt key text in
|
||||
(key, pt)
|
||||
|
||||
(*** Output ***)
|
||||
|
||||
let _ =
|
||||
let long_text = "\
|
||||
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"
|
||||
in
|
||||
let english_freqs = [
|
||||
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
|
||||
]
|
||||
in
|
||||
let (key, pt) = cryptanalyze english_freqs long_text in
|
||||
Printf.printf "Key: %s\n\nText: %s" key pt
|
||||
;;
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
use strict;
|
||||
use warnings;
|
||||
use feature 'say';
|
||||
|
||||
# from Wikipedia
|
||||
my %English_letter_freq = (
|
||||
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75,
|
||||
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23,
|
||||
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07
|
||||
);
|
||||
my @alphabet = sort keys %English_letter_freq;
|
||||
my $max_key_lengths = 5; # number of keylengths to try
|
||||
|
||||
sub myguess {
|
||||
my ($text) = (@_);
|
||||
my ($seqtext, @spacing, @factors, @sortedfactors, $pos, %freq, %Keys);
|
||||
|
||||
# Kasiski examination
|
||||
$seqtext = $text;
|
||||
while ($seqtext =~ /(...).*\1/) {
|
||||
$seqtext = substr($seqtext, 1+index($seqtext, $1));
|
||||
push @spacing, 1 + index($seqtext, $1);
|
||||
}
|
||||
|
||||
for my $j (@spacing) {
|
||||
push @factors, grep { $j % $_ == 0 } 2..$j;
|
||||
}
|
||||
$freq{$_}++ for @factors;
|
||||
@sortedfactors = grep { $_ >= 4 } sort { $freq{$b} <=> $freq{$a} } keys %freq; # discard very short keys
|
||||
|
||||
for my $keylen ( @sortedfactors[0..$max_key_lengths-1] ) {
|
||||
my $keyguess = '';
|
||||
for (my $i = 0; $i < $keylen; $i++) {
|
||||
my($mykey, %chi_values, $bestguess);
|
||||
for (my $j = 0; $j < length($text); $j += $keylen) {
|
||||
$mykey .= substr($text, ($j+$i) % length($text), 1);
|
||||
}
|
||||
|
||||
for my $subkey (@alphabet) {
|
||||
my $decrypted = mycrypt($mykey, $subkey);
|
||||
my $length = length($decrypted);
|
||||
for my $char (@alphabet) {
|
||||
my $expected = $English_letter_freq{$char} * $length / 100;
|
||||
my $observed;
|
||||
++$observed while $decrypted =~ /$char/g;
|
||||
$chi_values{$subkey} += ($observed - $expected)**2 / $expected if $observed;
|
||||
}
|
||||
}
|
||||
|
||||
$Keys{$keylen}{score} = $chi_values{'A'};
|
||||
for my $sk (sort keys %chi_values) {
|
||||
if ($chi_values{$sk} <= $Keys{$keylen}{score}) {
|
||||
$bestguess = $sk;
|
||||
$Keys{$keylen}{score} = $chi_values{$sk};
|
||||
}
|
||||
}
|
||||
$keyguess .= $bestguess;
|
||||
}
|
||||
$Keys{$keylen}{key} = $keyguess;
|
||||
}
|
||||
map { $Keys{$_}{key} } sort { $Keys{$a}{score} <=> $Keys{$b}{score}} keys %Keys;
|
||||
}
|
||||
|
||||
sub mycrypt {
|
||||
my ($text, $key) = @_;
|
||||
my ($new_text, %values_numbers);
|
||||
|
||||
my $keylen = length($key);
|
||||
@values_numbers{@alphabet} = 0..25;
|
||||
my %values_letters = reverse %values_numbers;
|
||||
|
||||
for (my $i = 0; $i < length($text); $i++) {
|
||||
my $val = -1 * $values_numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode
|
||||
+ $values_numbers{substr($text, $i, 1)};
|
||||
$new_text .= $values_letters{ $val % 26 };
|
||||
}
|
||||
return $new_text;
|
||||
}
|
||||
|
||||
my $cipher_text = <<~'EOD';
|
||||
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
|
||||
EOD
|
||||
|
||||
my $text = uc($cipher_text) =~ s/[^@{[join '', @alphabet]}]//gr;
|
||||
|
||||
for my $key ( myguess($text) ) {
|
||||
say "Key $key\n" .
|
||||
"Key length " . length($key) . "\n" .
|
||||
"Plaintext " . substr(mycrypt($text, $key), 0, 80) . "...\n";
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
(phixonline)-->
|
||||
<span style="color: #000080;font-style:italic;">--
|
||||
-- demo\rosetta\Cryptanalysis.exw
|
||||
--</span>
|
||||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">t0</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">time</span><span style="color: #0000FF;">()</span>
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">ciphertext</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute_all</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"""
|
||||
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"""</span><span style="color: #0000FF;">,{</span><span style="color: #008000;">" "</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">},{</span><span style="color: #008000;">""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">})</span>
|
||||
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">letters</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span>
|
||||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">'E'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12.702</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'T'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.056</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8.167</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'O'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.507</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'I'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.966</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'N'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.749</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'S'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.327</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'H'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.094</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'R'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.987</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'D'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.253</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'L'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.025</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'C'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.782</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'U'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.758</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'M'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.406</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'W'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.361</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'F'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.228</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'G'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.015</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Y'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.974</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'P'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.929</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'B'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.492</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'V'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.978</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'K'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.772</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'J'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.153</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'X'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.150</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.095</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.074</span><span style="color: #0000FF;">}})</span>
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">digraphs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span>
|
||||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">"TH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">15.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">12.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">9.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">8.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ND"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.9</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ON"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.7</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ST"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.5</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EN"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.5</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ED"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TO"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.0</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OU"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">5.0</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.7</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"IS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.7</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ET"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.9</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NG"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OF"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AL"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.9</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"DE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.9</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"LE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"SI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.5</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"VE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"LD"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"UR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">0.2</span><span style="color: #0000FF;">}})</span>
|
||||
<span style="color: #008080;">constant</span> <span style="color: #000000;">trigraphs</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">(</span>
|
||||
<span style="color: #0000FF;">{{</span><span style="color: #008000;">"THE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">18.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"AND"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ING"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">7.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ION"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ENT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">4.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"FOR"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"THA"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"NTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.3</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"INT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TIO"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ERE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"TER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">3.0</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"EST"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ERS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.8</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HAT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ATI"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.6</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ATE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.5</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ALL"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.5</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"VER"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HIS"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"HES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ETH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.4</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OFT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.2</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"STH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"RES"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"OTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ITH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"FTH"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.1</span><span style="color: #0000FF;">},</span>
|
||||
<span style="color: #0000FF;">{</span><span style="color: #008000;">"ONT"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">2.0</span><span style="color: #0000FF;">}})</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">keylen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">key</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">))</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]-</span><span style="color: #000000;">key</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]+</span><span style="color: #000000;">26</span><span style="color: #0000FF;">,</span><span style="color: #000000;">26</span><span style="color: #0000FF;">)+</span><span style="color: #008000;">'A'</span>
|
||||
<span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">k</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">msg</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">cryptanalyze</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">maxkeylen</span><span style="color: #0000FF;">=</span><span style="color: #000000;">20</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">integer</span> <span style="color: #000000;">enclen</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">maxkey</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">maxdec</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span>
|
||||
<span style="color: #000000;">k1</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" "</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">maxscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span>
|
||||
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">keylen</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxkeylen</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">key</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">idx</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">enclen</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #008080;">if</span> <span style="color: #7060A8;">mod</span><span style="color: #0000FF;">(</span><span style="color: #000000;">i</span><span style="color: #0000FF;">,</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">idx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">keylen</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">keylen</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">maxsubscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span>
|
||||
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">j</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'A'</span> <span style="color: #008080;">to</span> <span style="color: #008000;">'Z'</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">subscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span>
|
||||
|
||||
<span style="color: #000000;">k1</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">j</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">encidx</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">ii</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">encidx</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">enc</span><span style="color: #0000FF;">[</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">[</span><span style="color: #000000;">ii</span><span style="color: #0000FF;">]]</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">dec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">encidx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">k1</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">di</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">subscore</span> <span style="color: #0000FF;">+=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">di</span><span style="color: #0000FF;">],</span><span style="color: #000000;">letters</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">subscore</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">maxsubscore</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">maxsubscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">subscore</span>
|
||||
<span style="color: #000000;">key</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">j</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #000000;">idx</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sq_add</span><span style="color: #0000FF;">(</span><span style="color: #000000;">idx</span><span style="color: #0000FF;">,</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">dec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">decrypt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">enc</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">key</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #004080;">atom</span> <span style="color: #000000;">score</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0.0</span>
|
||||
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">],</span><span style="color: #000000;">letters</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">enclen</span> <span style="color: #0000FF;">-</span> <span style="color: #000000;">2</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">digraph</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #004080;">string</span> <span style="color: #000000;">trigraph</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span> <span style="color: #0000FF;">+</span> <span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
|
||||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">2</span> <span style="color: #0000FF;">*</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">digraph</span><span style="color: #0000FF;">,</span><span style="color: #000000;">digraphs</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #000000;">score</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">3</span> <span style="color: #0000FF;">*</span> <span style="color: #7060A8;">getd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">trigraph</span><span style="color: #0000FF;">,</span><span style="color: #000000;">trigraphs</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #008080;">if</span> <span style="color: #000000;">score</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">maxscore</span> <span style="color: #008080;">then</span>
|
||||
<span style="color: #000000;">maxscore</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">score</span>
|
||||
<span style="color: #000000;">maxkey</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">key</span>
|
||||
<span style="color: #000000;">maxdec</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">dec</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
|
||||
<span style="color: #008080;">return</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">maxkey</span><span style="color: #0000FF;">,</span><span style="color: #000000;">maxdec</span><span style="color: #0000FF;">}</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #008080;">function</span> <span style="color: #000000;">fold</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">s</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">integer</span> <span style="color: #000000;">w</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">w</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">s</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">by</span> <span style="color: #000000;">w</span> <span style="color: #008080;">do</span>
|
||||
<span style="color: #000000;">s</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"\n"</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
|
||||
<span style="color: #008080;">return</span> <span style="color: #000000;">s</span>
|
||||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
|
||||
|
||||
<span style="color: #004080;">string</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">dec</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">cryptanalyze</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ciphertext</span><span style="color: #0000FF;">)</span>
|
||||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"key: %s\n\n%s\n\n"</span><span style="color: #0000FF;">,</span> <span style="color: #0000FF;">{</span><span style="color: #000000;">key</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">fold</span><span style="color: #0000FF;">(</span><span style="color: #000000;">dec</span><span style="color: #0000FF;">,</span><span style="color: #000000;">80</span><span style="color: #0000FF;">)})</span>
|
||||
|
||||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"elapsed time: %3.2f seconds"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">time</span><span style="color: #0000FF;">()-</span><span style="color: #000000;">t0</span><span style="color: #0000FF;">})</span>
|
||||
|
||||
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">wait_key</span><span style="color: #0000FF;">()</span>
|
||||
<!--
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
from string import uppercase
|
||||
from operator import itemgetter
|
||||
|
||||
def vigenere_decrypt(target_freqs, input):
|
||||
nchars = len(uppercase)
|
||||
ordA = ord('A')
|
||||
sorted_targets = sorted(target_freqs)
|
||||
|
||||
def frequency(input):
|
||||
result = [[c, 0.0] for c in uppercase]
|
||||
for c in input:
|
||||
result[c - ordA][1] += 1
|
||||
return result
|
||||
|
||||
def correlation(input):
|
||||
result = 0.0
|
||||
freq = frequency(input)
|
||||
freq.sort(key=itemgetter(1))
|
||||
|
||||
for i, f in enumerate(freq):
|
||||
result += f[1] * sorted_targets[i]
|
||||
return result
|
||||
|
||||
cleaned = [ord(c) for c in input.upper() if c.isupper()]
|
||||
best_len = 0
|
||||
best_corr = -100.0
|
||||
|
||||
# Assume that if there are less than 20 characters
|
||||
# per column, the key's too long to guess
|
||||
for i in xrange(2, len(cleaned) // 20):
|
||||
pieces = [[] for _ in xrange(i)]
|
||||
for j, c in enumerate(cleaned):
|
||||
pieces[j % i].append(c)
|
||||
|
||||
# The correlation seems to increase for smaller
|
||||
# pieces/longer keys, so weigh against them a little
|
||||
corr = -0.5 * i + sum(correlation(p) for p in pieces)
|
||||
|
||||
if corr > best_corr:
|
||||
best_len = i
|
||||
best_corr = corr
|
||||
|
||||
if best_len == 0:
|
||||
return ("Text is too short to analyze", "")
|
||||
|
||||
pieces = [[] for _ in xrange(best_len)]
|
||||
for i, c in enumerate(cleaned):
|
||||
pieces[i % best_len].append(c)
|
||||
|
||||
freqs = [frequency(p) for p in pieces]
|
||||
|
||||
key = ""
|
||||
for fr in freqs:
|
||||
fr.sort(key=itemgetter(1), reverse=True)
|
||||
|
||||
m = 0
|
||||
max_corr = 0.0
|
||||
for j in xrange(nchars):
|
||||
corr = 0.0
|
||||
c = ordA + j
|
||||
for frc in fr:
|
||||
d = (ord(frc[0]) - c + nchars) % nchars
|
||||
corr += frc[1] * target_freqs[d]
|
||||
|
||||
if corr > max_corr:
|
||||
m = j
|
||||
max_corr = corr
|
||||
|
||||
key += chr(m + ordA)
|
||||
|
||||
r = (chr((c - ord(key[i % best_len]) + nchars) % nchars + ordA)
|
||||
for i, c in enumerate(cleaned))
|
||||
return (key, "".join(r))
|
||||
|
||||
|
||||
def main():
|
||||
encoded = """
|
||||
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"""
|
||||
|
||||
english_frequences = [
|
||||
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]
|
||||
|
||||
(key, decoded) = vigenere_decrypt(english_frequences, encoded)
|
||||
print "Key:", key
|
||||
print "\nText:", decoded
|
||||
|
||||
main()
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#lang at-exp racket
|
||||
|
||||
(define max-keylen 30)
|
||||
|
||||
(define text
|
||||
@~a{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})
|
||||
|
||||
(define first-char (char->integer #\A))
|
||||
(define chars# (- (char->integer #\Z) first-char -1))
|
||||
|
||||
(define freqs ; english letter frequencies from wikipedia
|
||||
((compose1 list->vector (curry map (curryr / 100000.0)))
|
||||
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406
|
||||
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74)))
|
||||
|
||||
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")])
|
||||
(- (char->integer c) first-char)))
|
||||
(define N (vector-length text*))
|
||||
|
||||
(define (col-guesses len)
|
||||
(for/list ([ofs len])
|
||||
(define text (for/list ([i (in-range ofs N len)]) (vector-ref text* i)))
|
||||
(define cN (length text))
|
||||
(define cfreqs (make-vector chars# 0))
|
||||
(for ([c (in-list text)])
|
||||
(vector-set! cfreqs c (add1 (vector-ref cfreqs c))))
|
||||
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN)))
|
||||
(argmin car
|
||||
(for/list ([d chars#])
|
||||
(cons (for/sum ([i chars#])
|
||||
(expt (- (vector-ref freqs i)
|
||||
(vector-ref cfreqs (modulo (+ i d) chars#)))
|
||||
2))
|
||||
d)))))
|
||||
|
||||
(define best-key
|
||||
(cdr (argmin car
|
||||
(for/list ([len (range 1 (add1 max-keylen))])
|
||||
(define guesses (col-guesses len))
|
||||
(cons (/ (apply + (map car guesses)) len) (map cdr guesses))))))
|
||||
|
||||
(printf "Best key found: ")
|
||||
(for ([c best-key]) (display (integer->char (+ c first-char))))
|
||||
(newline)
|
||||
|
||||
(printf "Decoded text:\n")
|
||||
(define decode-num
|
||||
(let ([cur '()])
|
||||
(λ(n) (when (null? cur) (set! cur best-key))
|
||||
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur))))))
|
||||
(for ([c text])
|
||||
(define n (- (char->integer c) first-char))
|
||||
(if (not (< -1 n chars#)) (display c)
|
||||
(display (integer->char (+ first-char (decode-num n))))))
|
||||
(newline)
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
#lang at-exp racket
|
||||
|
||||
(define max-keylen 30)
|
||||
|
||||
(define text
|
||||
@~a{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})
|
||||
|
||||
(define first-char (char->integer #\A))
|
||||
(define chars# (- (char->integer #\Z) first-char -1))
|
||||
|
||||
(define freqs ; english letter frequencies from wikipedia
|
||||
((compose1 list->vector (curry map (curryr / 100000.0)))
|
||||
'(8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406
|
||||
6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74)))
|
||||
|
||||
(define (n*n-1 n) (* n (sub1 n)))
|
||||
|
||||
(define text* (for/vector ([c (regexp-replace* #px"\\s+" text "")])
|
||||
(- (char->integer c) first-char)))
|
||||
(define N (vector-length text*))
|
||||
(define (get-col-length+freqs width offset)
|
||||
(define text (for/list ([i (in-range offset N width)]) (vector-ref text* i)))
|
||||
(define cN (length text))
|
||||
(define freqs (make-vector chars# 0))
|
||||
(for ([c (in-list text)]) (vector-set! freqs c (add1 (vector-ref freqs c))))
|
||||
(values cN freqs))
|
||||
|
||||
(define expected-IC (* chars# (for*/sum ([x freqs]) (* x x))))
|
||||
|
||||
;; maps key lengths to average index of coincidence
|
||||
(define keylen->ICs
|
||||
(for/vector ([len (in-range 1 (add1 (* max-keylen 2)))])
|
||||
(for/sum ([ofs len])
|
||||
(define-values [cN cfreqs] (get-col-length+freqs len ofs))
|
||||
(/ (for/sum ([i chars#]) (n*n-1 (vector-ref cfreqs i)))
|
||||
(/ (n*n-1 cN) chars#) len 1.0))))
|
||||
|
||||
;; given a key length find the key that minimizes errors from alphabet freqs,
|
||||
;; return (cons average-error key)
|
||||
(define (guess-key len)
|
||||
(define guesses
|
||||
(for/list ([ofs len])
|
||||
(define-values [cN cfreqs] (get-col-length+freqs len ofs))
|
||||
(for ([i chars#]) (vector-set! cfreqs i (/ (vector-ref cfreqs i) cN)))
|
||||
(argmin car
|
||||
(for/list ([d chars#])
|
||||
(cons (for/sum ([i chars#])
|
||||
(expt (- (vector-ref freqs i)
|
||||
(vector-ref cfreqs (modulo (+ i d) chars#)))
|
||||
2))
|
||||
d)))))
|
||||
(cons (/ (apply + (map car guesses)) len) (map cdr guesses)))
|
||||
|
||||
;; look for a key length that minimizes error from expected-IC, with some
|
||||
;; stupid consideration of multiples of the length (which should also have low
|
||||
;; errors), for each one guess a key, then find the one that minimizes both (in
|
||||
;; a way that looks like it works, but undoubtedly is wrong in all kinds of
|
||||
;; ways) and return the winner key
|
||||
(define best-key
|
||||
((compose1 cdr (curry argmin car))
|
||||
(for/list ([i (* max-keylen 2)])
|
||||
;; get the error from the expected-IC for the length and its multiples,
|
||||
;; with decreasing weights for the multiples
|
||||
(define with-multiples
|
||||
(for/list ([j (in-range i (* max-keylen 2) (add1 i))] [div N])
|
||||
(cons (/ (abs (- (vector-ref keylen->ICs j) expected-IC)) expected-IC)
|
||||
(/ (add1 div)))))
|
||||
(define total (/ (for/sum ([x with-multiples]) (* (car x) (cdr x)))
|
||||
(for/sum ([x with-multiples]) (cdr x))))
|
||||
(define guess (guess-key (add1 i)))
|
||||
(define guess*total (* total (car guess) (car guess)))
|
||||
;; (printf "~a~a: ~a ~s\n" (if (< i 9) " " "") (add1 i)
|
||||
;; (list total (car guess) guess*total) (cdr guess))
|
||||
(cons guess*total (cdr guess)))))
|
||||
|
||||
(printf "Best key found: ")
|
||||
(for ([c best-key]) (display (integer->char (+ c first-char))))
|
||||
(newline)
|
||||
|
||||
(printf "Decoded text:\n")
|
||||
(define decode-num
|
||||
(let ([cur '()])
|
||||
(λ(n) (when (null? cur) (set! cur best-key))
|
||||
(begin0 (modulo (- n (car cur)) chars#) (set! cur (cdr cur))))))
|
||||
(for ([c text])
|
||||
(define n (- (char->integer c) first-char))
|
||||
(if (not (< -1 n chars#)) (display c)
|
||||
(display (integer->char (+ first-char (decode-num n))))))
|
||||
(newline)
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
# from Wikipedia
|
||||
constant %English-letter-freq = (
|
||||
E => 12.70, L => 4.03, Y => 1.97, P => 1.93, T => 9.06, A => 8.17, O => 7.51, I => 6.97, N => 6.75,
|
||||
S => 6.33, H => 6.09, R => 5.99, D => 4.25, C => 2.78, U => 2.76, M => 2.41, W => 2.36, F => 2.23,
|
||||
G => 2.02, B => 1.29, V => 0.98, K => 0.77, J => 0.15, X => 0.15, Q => 0.10, Z => 0.07
|
||||
);
|
||||
constant @alphabet = %English-letter-freq.keys.sort;
|
||||
constant max_key_lengths = 5; # number of keylengths to try
|
||||
|
||||
sub myguess ($text) {
|
||||
my ($seqtext, @spacing, @factors, $pos, %freq, %Keys);
|
||||
|
||||
# Kasiski examination
|
||||
$seqtext = $text;
|
||||
while ($seqtext ~~ /$<sequence>=[...].*$<sequence>/) {
|
||||
$seqtext = substr($seqtext, 1+index($seqtext, $<sequence>));
|
||||
push @spacing, 1 + index($seqtext, $<sequence>);
|
||||
}
|
||||
for @spacing -> $j {
|
||||
%freq{$_}++ for grep { $j %% $_ }, 2..$j;
|
||||
}
|
||||
|
||||
# discard very short keys, and test only the most likely remaining key lengths
|
||||
(%freq.keys.grep(* > 3).sort({%freq{$_}}).tail(max_key_lengths)).race(:1batch).map: -> $keylen {
|
||||
my $key-guess = '';
|
||||
loop (my $i = 0; $i < $keylen; $i++) {
|
||||
my ($mykey, %chi-square, $best-guess);
|
||||
loop (my $j = 0; $j < $text.chars; $j += $keylen) {
|
||||
$mykey ~= substr($text, ($j+$i) % $text.chars, 1);
|
||||
}
|
||||
|
||||
for @alphabet -> $subkey {
|
||||
my $decrypted = mycrypt($mykey, $subkey);
|
||||
my $length = $decrypted.chars;
|
||||
for @alphabet -> $char {
|
||||
my $expected = %English-letter-freq{$char} * $length / 100;
|
||||
my $observed = $decrypted.comb.grep(* eq $char).elems;
|
||||
%chi-square{$subkey} += ($observed - $expected)² / $expected if $observed;
|
||||
}
|
||||
}
|
||||
%Keys{$keylen}{'score'} = %chi-square{@alphabet[0]};
|
||||
for %chi-square.keys.sort -> $sk {
|
||||
if (%chi-square{$sk} <= %Keys{$keylen}{'score'}) {
|
||||
$best-guess = $sk;
|
||||
%Keys{$keylen}{'score'} = %chi-square{$sk};
|
||||
}
|
||||
}
|
||||
$key-guess ~= $best-guess;
|
||||
}
|
||||
%Keys{$keylen}{'key'} = $key-guess;
|
||||
}
|
||||
%Keys.keys.sort({ %Keys{$_}{'score'} }).map:{ %Keys{$_}{'key'} };
|
||||
}
|
||||
|
||||
sub mycrypt ($text, $key) {
|
||||
constant %values-numbers = @alphabet Z=> ^@alphabet;
|
||||
constant %values-letters = %values-numbers.invert;
|
||||
|
||||
my ($new-text);
|
||||
my $keylen = $key.chars;
|
||||
loop (my $i = 0; $i < $text.chars; $i++) {
|
||||
my $val = -1 * %values-numbers{substr( $key, $i%$keylen, 1)} # negative shift for decode
|
||||
+ %values-numbers{substr($text, $i, 1)};
|
||||
$new-text ~= %values-letters{ $val % @alphabet };
|
||||
}
|
||||
return $new-text;
|
||||
}
|
||||
|
||||
my $cipher-text = .uc.trans(@alphabet => '', :c) given q:to/EOD/;
|
||||
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
|
||||
EOD
|
||||
|
||||
for myguess($cipher-text) -> $key {
|
||||
say "Key $key\n" ~
|
||||
"Key length {$key.chars}\n" ~
|
||||
"Plaintext {substr(mycrypt($cipher-text, $key), 0, 80)}...\n";
|
||||
}
|
||||
|
|
@ -0,0 +1,109 @@
|
|||
use std::iter::FromIterator;
|
||||
|
||||
const CRYPTOGRAM: &str = "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";
|
||||
|
||||
const FREQUENCIES: [f32; 26] = [
|
||||
0.08167, 0.01492, 0.02202, 0.04253, 0.12702, 0.02228, 0.02015, 0.06094, 0.06966, 0.00153,
|
||||
0.01292, 0.04025, 0.02406, 0.06749, 0.07507, 0.01929, 0.00095, 0.05987, 0.06327, 0.09356,
|
||||
0.02758, 0.00978, 0.02560, 0.00150, 0.01994, 0.00077,
|
||||
];
|
||||
|
||||
fn best_match(a: &[f32]) -> u8 {
|
||||
let sum: f32 = a.iter().sum();
|
||||
let mut best_fit = std::f32::MAX;
|
||||
let mut best_rotate = 0;
|
||||
for rotate in 0..=25 {
|
||||
let mut fit = 0.;
|
||||
for i in 0..=25 {
|
||||
let char_freq = FREQUENCIES[i];
|
||||
let idx = (i + rotate as usize) % 26 as usize;
|
||||
let d = a[idx] / sum - char_freq;
|
||||
fit += d * d / char_freq;
|
||||
}
|
||||
if fit < best_fit {
|
||||
best_fit = fit;
|
||||
best_rotate = rotate;
|
||||
}
|
||||
}
|
||||
|
||||
best_rotate
|
||||
}
|
||||
|
||||
fn freq_every_nth(msg: &[u8], key: &mut [char]) -> f32 {
|
||||
let len = msg.len();
|
||||
let interval = key.len();
|
||||
let mut accu = [0.; 26];
|
||||
for j in 0..interval {
|
||||
let mut out = [0.; 26];
|
||||
for i in (j..len).step_by(interval) {
|
||||
let idx = msg[i] as usize;
|
||||
out[idx] += 1.;
|
||||
}
|
||||
let rot = best_match(&out);
|
||||
key[j] = char::from(rot + b'A');
|
||||
for i in 0..=25 {
|
||||
let idx: usize = (i + rot as usize) % 26;
|
||||
accu[i] += out[idx];
|
||||
}
|
||||
}
|
||||
let sum: f32 = accu.iter().sum();
|
||||
let mut ret = 0.;
|
||||
for i in 0..=25 {
|
||||
let char_freq = FREQUENCIES[i];
|
||||
let d = accu[i] / sum - char_freq;
|
||||
ret += d * d / char_freq;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
fn decrypt(text: &str, key: &str) -> String {
|
||||
let key_chars_cycle = key.as_bytes().iter().map(|b| *b as i32).cycle();
|
||||
let is_ascii_uppercase = |c: &u8| (b'A'..=b'Z').contains(c);
|
||||
text.as_bytes()
|
||||
.iter()
|
||||
.filter(|c| is_ascii_uppercase(c))
|
||||
.map(|b| *b as i32)
|
||||
.zip(key_chars_cycle)
|
||||
.fold(String::new(), |mut acc, (c, key_char)| {
|
||||
let ci: u8 = ((c - key_char + 26) % 26) as u8;
|
||||
acc.push(char::from(b'A' + ci));
|
||||
acc
|
||||
})
|
||||
}
|
||||
fn main() {
|
||||
let enc = CRYPTOGRAM
|
||||
.split_ascii_whitespace()
|
||||
.collect::<Vec<_>>()
|
||||
.join("");
|
||||
let cryptogram: Vec<u8> = enc.as_bytes().iter().map(|b| u8::from(b - b'A')).collect();
|
||||
let mut best_fit = std::f32::MAX;
|
||||
let mut best_key = String::new();
|
||||
for j in 1..=26 {
|
||||
let mut key = vec!['\0'; j];
|
||||
let fit = freq_every_nth(&cryptogram, &mut key);
|
||||
let s_key = String::from_iter(key); // 'from_iter' is imported from std::iter::FromIterator;
|
||||
if fit < best_fit {
|
||||
best_fit = fit;
|
||||
best_key = s_key;
|
||||
}
|
||||
}
|
||||
|
||||
println!("best key: {}", &best_key);
|
||||
println!("\nDecrypted text:\n{}", decrypt(&enc, &best_key));
|
||||
}
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
package require Tcl 8.6
|
||||
|
||||
oo::class create VigenereAnalyzer {
|
||||
variable letterFrequencies sortedTargets
|
||||
constructor {{frequencies {
|
||||
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
|
||||
}}} {
|
||||
set letterFrequencies $frequencies
|
||||
set sortedTargets [lsort -real $frequencies]
|
||||
if {[llength $frequencies] != 26} {
|
||||
error "wrong length of frequency table"
|
||||
}
|
||||
}
|
||||
|
||||
### Utility methods
|
||||
# Find the value of $idxvar in the range [$from..$to) that maximizes the value
|
||||
# in $scorevar (which is computed by evaluating $body)
|
||||
method Best {idxvar from to scorevar body} {
|
||||
upvar 1 $idxvar i $scorevar s
|
||||
set bestI $from
|
||||
for {set i $from} {$i < $to} {incr i} {
|
||||
uplevel 1 $body
|
||||
if {![info exist bestS] || $bestS < $s} {
|
||||
set bestI $i
|
||||
set bestS $s
|
||||
}
|
||||
}
|
||||
return $bestI
|
||||
}
|
||||
# Simple list map
|
||||
method Map {var list body} {
|
||||
upvar 1 $var v
|
||||
set result {}
|
||||
foreach v $list {lappend result [uplevel 1 $body]}
|
||||
return $result
|
||||
}
|
||||
# Simple partition of $list into $groups groups; thus, the partition of
|
||||
# {a b c d e f} into 3 produces {a d} {b e} {c f}
|
||||
method Partition {list groups} {
|
||||
set i 0
|
||||
foreach val $list {
|
||||
dict lappend result $i $val
|
||||
if {[incr i] >= $groups} {
|
||||
set i 0
|
||||
}
|
||||
}
|
||||
return [dict values $result]
|
||||
}
|
||||
|
||||
### Helper methods
|
||||
# Get the actual counts of different types of characters in the given list
|
||||
method Frequency cleaned {
|
||||
for {set i 0} {$i < 26} {incr i} {
|
||||
dict set tbl $i 0
|
||||
}
|
||||
foreach ch $cleaned {
|
||||
dict incr tbl [expr {[scan $ch %c] - 65}]
|
||||
}
|
||||
return $tbl
|
||||
}
|
||||
|
||||
# Get the correlation factor of the characters in a given list with the
|
||||
# class-specified language frequency corpus
|
||||
method Correlation cleaned {
|
||||
set result 0.0
|
||||
set freq [lsort -integer [dict values [my Frequency $cleaned]]]
|
||||
foreach f $freq s $sortedTargets {
|
||||
set result [expr {$result + $f * $s}]
|
||||
}
|
||||
return $result
|
||||
}
|
||||
|
||||
# Compute an estimate for the key length
|
||||
method GetKeyLength {cleaned {required 20}} {
|
||||
# Assume that we need at least 20 characters per column to guess
|
||||
set bestLength [my Best i 2 [expr {[llength $cleaned] / $required}] corr {
|
||||
set corr [expr {-0.5 * $i}]
|
||||
foreach chars [my Partition $cleaned $i] {
|
||||
set corr [expr {$corr + [my Correlation $chars]}]
|
||||
}
|
||||
}]
|
||||
if {$bestLength == 0} {
|
||||
error "text is too short to analyze"
|
||||
}
|
||||
return $bestLength
|
||||
}
|
||||
|
||||
# Compute the key from the given frequency tables and the class-specified
|
||||
# language frequency corpus
|
||||
method GetKeyFromFreqs freqs {
|
||||
foreach f $freqs {
|
||||
set m [my Best i 0 26 corr {
|
||||
set corr 0.0
|
||||
foreach {ch count} $f {
|
||||
set d [expr {($ch - $i) % 26}]
|
||||
set corr [expr {$corr + $count*[lindex $letterFrequencies $d]}]
|
||||
}
|
||||
}]
|
||||
append key [format %c [expr {65 + $m}]]
|
||||
}
|
||||
return $key
|
||||
}
|
||||
|
||||
##### The main analyzer method #####
|
||||
method analyze input {
|
||||
# Turn the input into a clean letter sequence
|
||||
set cleaned [regexp -all -inline {[A-Z]} [string toupper $input]]
|
||||
# Get the (estimated) key length
|
||||
set bestLength [my GetKeyLength $cleaned]
|
||||
# Get the frequency mapping for the partitioned input text
|
||||
set freqs [my Map p [my Partition $cleaned $bestLength] {my Frequency $p}]
|
||||
# Get the key itself
|
||||
return [my GetKeyFromFreqs $freqs]
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
set encoded "
|
||||
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
|
||||
"
|
||||
VigenereAnalyzer create englishVigenereAnalyzer
|
||||
set key [englishVigenereAnalyzer analyze $encoded]
|
||||
Vigenere create decoder $key
|
||||
set decoded [decoder decrypt $encoded]
|
||||
puts "Key: $key"
|
||||
puts "Text: $decoded"
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
import strings
|
||||
const encoded =
|
||||
"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"
|
||||
|
||||
const freq = [
|
||||
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,
|
||||
]
|
||||
|
||||
fn sum(a []f64) f64 {
|
||||
mut s := 0.0
|
||||
for f in a {
|
||||
s += f
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
fn best_match(a []f64) int {
|
||||
s := sum(a)
|
||||
mut best_fit, mut best_rotate := 1e100, 0
|
||||
for rotate in 0..26 {
|
||||
mut fit := 0.0
|
||||
for i in 0..26 {
|
||||
d := a[(i+rotate)%26]/s - freq[i]
|
||||
fit += d * d / freq[i]
|
||||
}
|
||||
if fit < best_fit {
|
||||
best_fit, best_rotate = fit, rotate
|
||||
}
|
||||
}
|
||||
return best_rotate
|
||||
}
|
||||
|
||||
fn freq_every_nth(msg []int, mut key []u8) f64 {
|
||||
l := msg.len
|
||||
interval := key.len
|
||||
mut out := []f64{len: 26}
|
||||
mut accu := []f64{len: 26}
|
||||
for j in 0..interval {
|
||||
for z in 0..26 {
|
||||
out[z] = 0.0
|
||||
}
|
||||
for i := j; i < l; i += interval {
|
||||
out[msg[i]]++
|
||||
}
|
||||
rot := best_match(out)
|
||||
key[j] = u8(rot + 65)
|
||||
for i := 0; i < 26; i++ {
|
||||
accu[i] += out[(i+rot)%26]
|
||||
}
|
||||
}
|
||||
s := sum(accu)
|
||||
mut ret := 0.0
|
||||
for i := 0; i < 26; i++ {
|
||||
d := accu[i]/s - freq[i]
|
||||
ret += d * d / freq[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
fn decrypt(text string, key string) string {
|
||||
mut sb := strings.new_builder(128)
|
||||
mut ki := 0
|
||||
for c in text {
|
||||
if c < 'A'[0] || c > 'Z'[0] {
|
||||
continue
|
||||
}
|
||||
ci := (c - key[ki] + 26) % 26
|
||||
sb.write_rune(ci + 65)
|
||||
ki = (ki + 1) % key.len
|
||||
}
|
||||
return sb.str()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
enc := encoded.replace(" ", "")
|
||||
mut txt := []int{len: enc.len}
|
||||
for i in 0..txt.len {
|
||||
txt[i] = int(enc[i] - 'A'[0])
|
||||
}
|
||||
mut best_fit, mut best_key := 1e100, ""
|
||||
println(" Fit Length Key")
|
||||
for j := 1; j <= 26; j++ {
|
||||
mut key := []u8{len: j}
|
||||
fit := freq_every_nth(txt, mut key)
|
||||
s_key := key.bytestr()
|
||||
print("${fit:.6} ${j:2} $s_key")
|
||||
if fit < best_fit {
|
||||
best_fit, best_key = fit, s_key
|
||||
print(" <--- best so far")
|
||||
}
|
||||
println('')
|
||||
}
|
||||
println("\nBest key : $best_key")
|
||||
println("\nDecrypted text:\n${decrypt(enc, best_key)}")
|
||||
}
|
||||
|
|
@ -0,0 +1,317 @@
|
|||
// (1) Copy text into tmp buffer and remove non-alpha chars.
|
||||
|
||||
Chdir(PATH_ONLY)
|
||||
BOF
|
||||
Reg_Copy(10, ALL) // copy text to new buffer
|
||||
Buf_Switch(Buf_Free)
|
||||
Reg_Ins(10)
|
||||
BOF
|
||||
Replace ("|!|A", "", BEGIN+ALL+NOERR) // remove non-alpha chars
|
||||
Reg_Copy_Block(10,0,EOB_pos) // @10 = text to be analysed
|
||||
|
||||
#20 = Buf_Num // buffer for text being analyzed
|
||||
#21 = Buf_Free // buffer for English frequency list (A-Z)
|
||||
Buf_Switch(#21)
|
||||
Ins_Text("8167 1492 2782 4253 12702 2228 2015 6094 6966 153 772 4025 2406 6749 7507 1929 95 5987 6327 9056 2758 978 2360 150 1974 74")
|
||||
File_Open("unixdict.txt") // or use "|(MACRO_DIR)\scribe\english.vdf"
|
||||
#23 = Buf_Num // buffer for dictionary
|
||||
#24 = Buf_Free // buffer for key canditates
|
||||
|
||||
Buf_Switch(#24)
|
||||
for (#1=0; #1<5; #1++) { // Fill table for 5 keys of 50 chars
|
||||
Ins_Char('.', COUNT, 50)
|
||||
Ins_Newline
|
||||
}
|
||||
#22 = Buf_Free // buffer for results
|
||||
|
||||
#25 = Reg_Size(10) // number of letters in the text
|
||||
#26 = 26 // number of characters in the alphabet
|
||||
#61 = min(#25/10, 50) // max key length to try
|
||||
|
||||
// (2) Check Index of coincidence (or Kp) for each key length
|
||||
|
||||
Buf_Switch(#22) // buffer for results
|
||||
Ins_Text("KeyLen Kp dist ") Ins_Newline
|
||||
Ins_Text("-----------------") Ins_Newline
|
||||
#13 = Cur_Pos
|
||||
#7 = 0 // no Caesar encryption
|
||||
for (#5=1; #5<=#61; #5++) {
|
||||
Buf_Switch(#20) // text being analyzed
|
||||
BOF
|
||||
#54 = 0; // sum of Kp's
|
||||
for (#6=0; #6<#5; #6++) { // for each slide
|
||||
Goto_Pos(#6)
|
||||
Call("CHARACTER_FREQUENCIES")
|
||||
Call("INDEX_OF_COINCIDENCE") // #51 = Kp * 10000
|
||||
#54 += #51
|
||||
}
|
||||
#54 /= #5 // average of Kp's
|
||||
Buf_Switch(#22)
|
||||
Num_Ins(#5, COUNT, 3) // write key length
|
||||
IT(": ")
|
||||
Num_Ins(#54, NOCR) // average Kp
|
||||
Num_Ins(670-#54) // distance to English Kp
|
||||
}
|
||||
Buf_Switch(#22)
|
||||
Sort_Merge("5,12", #13, Cur_Pos, REVERSE) // sort the results by Kp value
|
||||
Ins_Newline
|
||||
|
||||
// (3) Check the best 4 key lengths to find which one gives the best decrypt result
|
||||
|
||||
#38 = 0 // max number of correct characters found
|
||||
#19 = 1 // best key length
|
||||
for (#14 = 0; #14<4; #14++) { // try 4 best key lengths
|
||||
Buf_Switch(#22) // results buffer
|
||||
Goto_Pos(#13) Line(#14)
|
||||
#5 = Num_Eval(SUPPRESS) // #5 = key length
|
||||
Call("FIND_KEYS") // find Caesar key for each key character
|
||||
#4 = -1 // try best match key chars only
|
||||
Call("BUILD_KEY")
|
||||
EOF
|
||||
Ins_Text("Key length ")
|
||||
Num_Ins(#5, LEFT)
|
||||
Reg_Ins(10) // encrypted text
|
||||
BOL
|
||||
Call("DECRYPT_LINE")
|
||||
BOL
|
||||
Call("FIND_ENGLISH_WORDS") // #37 = number of English chars
|
||||
EOL Ins_Newline
|
||||
Ins_Text("Correct chars: ")
|
||||
Num_Ins(#37)
|
||||
if (#37 > #38) {
|
||||
#38 = #37
|
||||
#19 = #5
|
||||
}
|
||||
Update()
|
||||
}
|
||||
|
||||
Ins_Text("Using key length: ") Num_Ins(#19) Ins_Newline
|
||||
#5 = #19
|
||||
Call("FIND_KEYS") // find Caesar key for each key character
|
||||
|
||||
// (4) Decrypt with different key combinations and try to find English words.
|
||||
// Try key combinations where max one char is taken from 2nd best Caesar key.
|
||||
|
||||
#38 = 0 // max number of chars in English words found
|
||||
#39 = -1 // best key number found
|
||||
for (#4 = -1; #4 < #19; #4++)
|
||||
{
|
||||
Call("BUILD_KEY")
|
||||
Buf_Switch(#22) // results
|
||||
Reg_Ins(10) // encrypted text
|
||||
BOL
|
||||
Call("DECRYPT_LINE")
|
||||
BOL
|
||||
Update()
|
||||
Call("FIND_ENGLISH_WORDS") // #37 := number of correct letters in text
|
||||
if (#37 > #38) {
|
||||
#38 = #37 // new highest number of correct chars
|
||||
#39 = #4 // new best key
|
||||
}
|
||||
|
||||
EOL IT(" -- ") // display results
|
||||
Num_Ins(#4, COUNT, 3) // key number
|
||||
Ins_Text(": ")
|
||||
for (#6=0; #6<#19; #6++) { // display key
|
||||
#9 = 130 + #6
|
||||
Ins_Char(#@9)
|
||||
}
|
||||
Ins_Text(" correct chars =")
|
||||
Num_Ins(#37)
|
||||
}
|
||||
Ins_Text("Best key = ")
|
||||
Num_Ins(#39, LEFT)
|
||||
#4 = #39
|
||||
Ins_Newline
|
||||
|
||||
// Display results
|
||||
//
|
||||
Buf_Switch(#24) // table for key canditates
|
||||
BOF
|
||||
Reg_Copy_Block(14, Cur_Pos, Cur_Pos+#19) // best Caesar key chars
|
||||
Line(1)
|
||||
Reg_Copy_Block(15, Cur_Pos, Cur_Pos+#19) // 2nd best Caesar key chars
|
||||
Call("BUILD_KEY")
|
||||
Buf_Switch(#22)
|
||||
Ins_Text("Key 1: ") Reg_Ins(14) Ins_Newline
|
||||
Ins_Text("Key 2: ") Reg_Ins(15) Ins_Newline
|
||||
Ins_Text("Key: ")
|
||||
for (#6=0; #6 < #19; #6++) {
|
||||
#9 = #6+130
|
||||
Ins_Char(#@9)
|
||||
}
|
||||
Ins_Newline
|
||||
Ins_Newline
|
||||
|
||||
// decrypt the text with selected key
|
||||
Ins_Text("Decrypted text:") Ins_Newline
|
||||
Reg_Ins(10)
|
||||
BOL
|
||||
Call("DECRYPT_LINE")
|
||||
BOL Reg_Copy(13,1)
|
||||
EOL Ins_Newline
|
||||
|
||||
// Find English words from the text
|
||||
Reg_Ins(13)
|
||||
Call("FIND_ENGLISH_WORDS")
|
||||
EOL
|
||||
Ins_Newline
|
||||
Num_Ins(#37, NOCR) IT(" of ")
|
||||
Num_Ins(#25, NOCR) IT(" characters are English words. ")
|
||||
Ins_Newline
|
||||
|
||||
Buf_Switch(#20) Buf_Quit(OK)
|
||||
Buf_Switch(#21) Buf_Quit(OK)
|
||||
Buf_Switch(#23) Buf_Quit(OK)
|
||||
Buf_Switch(#24) Buf_Quit(OK)
|
||||
|
||||
Statline_Message("Done!")
|
||||
Return
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Caesar decrypt current line and count character frequencies.
|
||||
// in: #5 = step size, #7 = encryption key, #26 = num of chars in alphabet
|
||||
// out: #65...#90 = frequencies, #60 = number of chars
|
||||
|
||||
:CHARACTER_FREQUENCIES:
|
||||
Save_Pos
|
||||
for (#8 = 'A'; #8<='Z'; #8++) {
|
||||
#@8 = 0 // reset frequency counters
|
||||
}
|
||||
#60 = 0 // total number of chars
|
||||
while (!At_EOL) {
|
||||
if (Cur_Char >= 'A' && Cur_Char <= 'Z') {
|
||||
#8 = (Cur_Char-'A'+#26-#7) % #26 + 'A' // decrypted char
|
||||
#@8++
|
||||
#60++
|
||||
}
|
||||
Char(#5)
|
||||
}
|
||||
Restore_Pos
|
||||
Return
|
||||
|
||||
// Calculate Index of Coincidence (Kp).
|
||||
// in: character frequencies in #65...#90, #60 = num of chars
|
||||
// out: #51 = IC * 10000
|
||||
//
|
||||
:INDEX_OF_COINCIDENCE:
|
||||
Num_Push(10,15)
|
||||
#10 = 0
|
||||
for (#11 = 'A'; #11<='Z'; #11++) {
|
||||
#10 += (#@11 * (#@11-1)) // Calculate sigma{ni * (ni-1)}
|
||||
}
|
||||
#12 = #60 * (#60-1) // #12 = N * (N-1)
|
||||
#51 = #10 * 10000 / #12 // #51 = Kp * 10000
|
||||
Num_Pop(10,15)
|
||||
Return
|
||||
|
||||
// Find best and 2nd best Caesar key for each character position of Vigenère key.
|
||||
// in: #5=step size (key length)
|
||||
// out: keys in buffer #24
|
||||
//
|
||||
:FIND_KEYS:
|
||||
for (#6 = 0; #6 < #5; #6++) { // for each char position in the key
|
||||
#30 = -1 // best key char found so far
|
||||
#31 = -1 // 2nd best key char
|
||||
#32 = MAXNUM // smallest error found so far
|
||||
#33 = MAXNUM // 2nd smallest error found so far
|
||||
for (#7 = 0; #7 < #26; #7++) { // for each possible key value
|
||||
#35 = 0 // total frequency error compared to English
|
||||
Buf_Switch(#20) // text being analyzed
|
||||
Goto_Pos(#6)
|
||||
Call("CHARACTER_FREQUENCIES")
|
||||
Buf_Switch(#21) // English frequency table
|
||||
BOF
|
||||
for (#8 = 'A'; #8<='Z'; #8++) { // calculate total frequency error
|
||||
#34 = Num_Eval(SUPPRESS+ADVANCE)
|
||||
#35 += abs((#@8*100000+50000)/#60-#34)
|
||||
}
|
||||
|
||||
if (#35 < #32) { // found better match?
|
||||
#33 = #32
|
||||
#32 = #35
|
||||
#31 = #30
|
||||
#30 = #7
|
||||
} else {
|
||||
if (#35 < #33) { // 2nd best match?
|
||||
#33 = #35
|
||||
#31 = #7
|
||||
}
|
||||
}
|
||||
}
|
||||
Buf_Switch(#24) // table for key canditates
|
||||
BOF
|
||||
Goto_Col(#6+1)
|
||||
Ins_Char(#30+'A', OVERWRITE) // save the best match
|
||||
Line(1)
|
||||
Goto_Col(#6+1)
|
||||
Ins_Char(#31+'A', OVERWRITE) // save 2nd best match
|
||||
}
|
||||
Buf_Switch(#22) // results buffer
|
||||
Return
|
||||
|
||||
// Combine actual key from 1st and 2nd best Caesar key characters
|
||||
// Use 1st key chars and (possibly) one character from 2nd key.
|
||||
// #4 = index of the char to be picked from 2nd key, -1 = none.
|
||||
// #5 = key length
|
||||
//
|
||||
:BUILD_KEY:
|
||||
Buf_Switch(#24) // table for key canditates
|
||||
BOF
|
||||
for (#6=0; #6<#5; #6++) { // copy 1st key
|
||||
#8 = 130 + #6
|
||||
#@8 = Cur_Char
|
||||
Char(1)
|
||||
}
|
||||
if (#4 >= 0) {
|
||||
#8 = 130 + #4 // pick one char from 2st key
|
||||
Line(1)
|
||||
Goto_Col(#4+1)
|
||||
#@8 = Cur_Char
|
||||
}
|
||||
Buf_Switch(#22) // results buffer
|
||||
Return
|
||||
|
||||
// Decrypt text on current line
|
||||
// in: #5 = key length, #130...#189 = key
|
||||
//
|
||||
:DECRYPT_LINE:
|
||||
Num_Push(6,9)
|
||||
#6 = 0
|
||||
While (!At_EOL) {
|
||||
#9 = #6+130
|
||||
#7 = #@9
|
||||
#8 = (Cur_Char - #7 + #26) % #26 + 'A' // decrypted char
|
||||
Ins_Char(#8, OVERWRITE)
|
||||
#6++
|
||||
if (#6 >= #5) {
|
||||
#6 = 0
|
||||
}
|
||||
}
|
||||
Num_Pop(6,9)
|
||||
Return
|
||||
|
||||
// Find English words from text on current line
|
||||
// out: #37 = number of chars matched
|
||||
//
|
||||
:FIND_ENGLISH_WORDS:
|
||||
Buf_Switch(#23) // dictionary
|
||||
BOF
|
||||
While (!At_EOF) {
|
||||
Reg_Copy_Block(12, Cur_Pos, EOL_Pos)
|
||||
if (Reg_Size(12) > 2) {
|
||||
Buf_Switch(#22) // buffer for results
|
||||
BOL
|
||||
while (Search_Block(@12, Cur_Pos, EOL_Pos, NOERR)) {
|
||||
Reg_Ins(12, OVERWRITE)
|
||||
}
|
||||
Buf_Switch(#23)
|
||||
}
|
||||
Line(1, ERRBREAK)
|
||||
}
|
||||
|
||||
Buf_Switch(#22)
|
||||
BOL
|
||||
#37 = Search_Block("|V", Cur_Pos, EOL_Pos, ALL+NOERR)
|
||||
Return
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
import "/math" for Nums
|
||||
import "/iterate" for Stepped
|
||||
import "/str" for Char, Str
|
||||
import "/fmt" for Fmt
|
||||
|
||||
var encoded =
|
||||
"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"
|
||||
|
||||
var freq = [
|
||||
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
|
||||
]
|
||||
|
||||
var bestMatch = Fn.new { |a|
|
||||
var sum = Nums.sum(a)
|
||||
var bestFit = 1e100
|
||||
var bestRotate = 0
|
||||
for (rotate in 0..25) {
|
||||
var fit = 0
|
||||
for (i in 0..25) {
|
||||
var d = a[(i + rotate) % 26] / sum - freq[i]
|
||||
fit = fit + d * d / freq[i]
|
||||
}
|
||||
if (fit < bestFit) {
|
||||
bestFit = fit
|
||||
bestRotate = rotate
|
||||
}
|
||||
}
|
||||
return bestRotate
|
||||
}
|
||||
|
||||
var freqEveryNth = Fn.new { |msg, key|
|
||||
var len = msg.count
|
||||
var interval = key.count
|
||||
var out = List.filled(26, 0)
|
||||
var accu = List.filled(26, 0)
|
||||
for (j in 0...interval) {
|
||||
for (i in 0..25) out[i] = 0
|
||||
for (i in Stepped.new(j...len, interval)) out[msg[i]] = out[msg[i]] + 1
|
||||
var rot = bestMatch.call(out)
|
||||
key[j] = Char.fromCode(rot + 65)
|
||||
for (i in 0..25) accu[i] = accu[i] + out[(i + rot) % 26]
|
||||
}
|
||||
var sum = Nums.sum(accu)
|
||||
var ret = 0
|
||||
for (i in 0..25) {
|
||||
var d = accu[i] / sum - freq[i]
|
||||
ret = ret + d * d / freq[i]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
var decrypt = Fn.new { |text, key|
|
||||
var sb = ""
|
||||
var ki = 0
|
||||
for (c in text) {
|
||||
if (Char.isAsciiUpper(c)) {
|
||||
var ci = (c.bytes[0] - key[ki].bytes[0] + 26) % 26
|
||||
sb = sb + Char.fromCode(ci + 65)
|
||||
ki = (ki + 1) % key.count
|
||||
}
|
||||
}
|
||||
return sb
|
||||
}
|
||||
|
||||
var enc = encoded.replace(" ", "")
|
||||
var txt = List.filled(enc.count, 0)
|
||||
for (i in 0...txt.count) txt[i] = Char.code(enc[i]) - 65
|
||||
var bestFit = 1e100
|
||||
var bestKey = ""
|
||||
var f = "$f $2d $s"
|
||||
System.print(" Fit Length Key")
|
||||
for (j in 1..26) {
|
||||
var key = List.filled(j, "")
|
||||
var fit = freqEveryNth.call(txt, key)
|
||||
var sKey = key.join("")
|
||||
Fmt.write(f, fit, j, sKey)
|
||||
if (fit < bestFit) {
|
||||
bestFit = fit
|
||||
bestKey = sKey
|
||||
System.write(" <--- best so far")
|
||||
}
|
||||
System.print()
|
||||
}
|
||||
System.print()
|
||||
System.print("Best key : %(bestKey)")
|
||||
System.print("\nDecrypted text:\n%(decrypt.call(enc, bestKey))")
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
var[const] uppercase=["A".."Z"].pump(String),
|
||||
english_frequences=T( // A..Z
|
||||
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);
|
||||
|
||||
fcn vigenere_decrypt(target_freqs, input){ // ( (float,...), string)
|
||||
nchars,ordA :=uppercase.len(),"A".toAsc();
|
||||
sorted_targets:=target_freqs.sort();
|
||||
|
||||
frequency:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65)
|
||||
result:=uppercase.pump(List(),List.fp1(0)); // ( ("A",0),("B",0) ...)
|
||||
foreach c in (input){ result[c - ordA][1] += 1 }
|
||||
result // --> mutable list of mutable lists ( ("A",Int)...("Z",Int) )
|
||||
};
|
||||
correlation:='wrap(input){ // (n,n,n,n,...), n is ASCII index ("A"==65)
|
||||
result,freq:=0.0, frequency(input);
|
||||
freq.sort(fcn([(_,a)],[(_,b)]){ a<b }); // sort letters by frequency
|
||||
foreach i,f in (freq.enumerate()){ result+=sorted_targets[i]*f[1] }
|
||||
result // -->Float
|
||||
};
|
||||
|
||||
cleaned:=input.toUpper().pump(List,uppercase.holds,Void.Filter,"toAsc");
|
||||
|
||||
best_len,best_corr := 0,-100.0;
|
||||
# Assume that if there are less than 20 characters
|
||||
# per column, the key's too long to guess
|
||||
foreach i in ([2..cleaned.len()/20]){
|
||||
pieces:=(i).pump(List,List.copy); // ( (),() ... )
|
||||
foreach c in (cleaned){ pieces[__cWalker.idx%i].append(c) }
|
||||
|
||||
# The correlation seems to increase for smaller
|
||||
# pieces/longer keys, so weigh against them a little
|
||||
corr:=-0.5*i + pieces.apply(correlation).sum(0.0);
|
||||
if(corr>best_corr) best_len,best_corr=i,corr;
|
||||
}
|
||||
if(best_len==0) return("Text is too short to analyze", "");
|
||||
|
||||
pieces:=best_len.pump(List,List.copy);
|
||||
foreach c in (cleaned){ pieces[__cWalker.idx%best_len].append(c) }
|
||||
|
||||
key,freqs := "",pieces.apply(frequency);
|
||||
foreach fr in (freqs){
|
||||
fr.sort(fcn([(_,a)],[(_,b)]){ a>b }); // reverse sort by freq
|
||||
m,max_corr := 0,0.0;
|
||||
foreach j in (nchars){
|
||||
corr,c := 0.0,ordA + j;
|
||||
foreach frc in (fr){
|
||||
d:=(frc[0].toAsc() - c + nchars) % nchars;
|
||||
corr+=target_freqs[d]*frc[1];
|
||||
if(corr>max_corr) m,max_corr=j,corr;
|
||||
}
|
||||
}
|
||||
key+=(m + ordA).toChar();
|
||||
}
|
||||
|
||||
cleaned.enumerate().apply('wrap([(i,c])){
|
||||
( (c - (key[i%best_len]).toAsc() + nchars)%nchars + ordA ).toChar()
|
||||
}).concat() :
|
||||
T(key,_);
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
encryptedText:=
|
||||
#<<<
|
||||
"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";
|
||||
#<<<
|
||||
key,decoded:=vigenere_decrypt(english_frequences,encryptedText);
|
||||
println("Key:", key);
|
||||
println("Decoded text:", decoded);
|
||||
Loading…
Add table
Add a link
Reference in a new issue