-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVigenereAnalyser.cpp
128 lines (104 loc) · 4.21 KB
/
VigenereAnalyser.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*****************************************************************************************
*
* Title: Extract the key and plaintext from a Vigenère cipher
* Author: User: MagiMaster
* Date: 2011
* Code version: 2.0
* Availability: http://rosettacode.org/wiki/Vigen%C3%A8re_cipher/Cryptanalysis
*
***************************************************************************************/
//Explanation of code
//As an input, it takes only the encrypted text.
//It is assumed that the un encrypted text is in English for this to work.
//Frequency analysis is done first,
//The key is found,
//The key is used to decrypt and output the original plaintext.
//The algorithm is not perfect and can have mistakes
#include "VigenereAnalyser.h"
// An array of frequencies is created
listOfResults & VigenereAnalyser::frequency(const string& input) {
for (char current = 'A'; current <= 'Z'; ++current) {
frequencyResults[current - 'A'] = make_pair(current, 0);
}
for (size_t i = 0; i < input.size(); ++i){
frequencyResults[input[i] - 'A'].second++;
}
//Frequency is returned
return frequencyResults;
}
//Analysis correlation
double VigenereAnalyser::correlation(const string& input) {
double result = 0.0;
frequency(input);
sort(frequencyResults.begin(), frequencyResults.end(), [](pair<char, double> u, pair<char, double> v)->bool
{ return u.second < v.second; });
for (size_t i = 0; i < 26; ++i)
result += frequencyResults[i].second * sorted[i];
return result;
}
VigenereAnalyser::VigenereAnalyser(const array<double, 26>& targetFreqs) {
targets = targetFreqs;
sorted = targets;
sort(sorted.begin(), sorted.end());
}
pair<string, string> VigenereAnalyser::scanText(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 scanText", "");
vector<string> pieces(bestLength);
for (size_t i = 0; i < cleaned.size(); ++i)
pieces[i % bestLength] += cleaned[i];
vector<listOfResults> 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);
}