1 /*
2 *cryptoslam: A tool for decoding cryptograms
3 *
4 *Copyright (C) 1997,2000,2003 Brian Enigma
5 * enigma@netninja.com
6 * http://sourceforge.net/projects/cryptoslam/
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or EXISTANCE OF KITTEN. See the GNU General
16 * Public License for more details.
17 *
18 * http://www.gnu.org/copyleft/gpl.html
19 *
20 */
21
22 #include "config.h"
23 #include "cryptogram.h"
24 #include <string.h>
25 #include <curses.h>
26 #include <stdlib.h>
27 #include <time.h>
28
Cryptogram()29 Cryptogram::Cryptogram()
30 {
31 cyphertext = "";
32 plaintext = "";
33 reset();
34 }
35
~Cryptogram()36 Cryptogram::~Cryptogram()
37 {
38 }
39
reset()40 void Cryptogram::reset()
41 {
42 cyphertext = SAMPLE_TEXT;
43 plaintext = "";
44 memset(alphabet, ' ', sizeof(alphabet));
45 calcPlaintext();
46 }
47
loadFile(const char * filename)48 void Cryptogram::loadFile(const char *filename)
49 {
50 FILE *f;
51 char block[4096];
52 int size;
53
54 cyphertext = SAMPLE_TEXT;
55 plaintext = "";
56 memset(alphabet, ' ', sizeof(alphabet));
57
58 f = fopen(filename, "rb");
59 if (!f)
60 {
61 reset();
62 return;
63 }
64 size = fread(block, 1, 4096, f);
65 block[4095] = 0;
66 block[size] = 0;
67 fclose(f);
68
69 if (strncmp(block, FILE_HEADER, strlen(FILE_HEADER) + 1) == 0)
70 {
71 memcpy(alphabet, block + strlen(FILE_HEADER) + 1, sizeof(alphabet));
72 cyphertext = strdup(block + strlen(FILE_HEADER) + 1 + sizeof(alphabet));
73 } else {
74 cyphertext = strdup(block);
75 }
76 calcPlaintext();
77 }
78
saveFileText(const char * filename)79 void Cryptogram::saveFileText(const char *filename)
80 {
81 FILE *f;
82
83 f = fopen(filename, "wb");
84 if (!f)
85 return;
86 fwrite(plaintext.c_str(), 1, plaintext.length(), f);
87 fwrite("\n\n", 1, 2, f);
88 fwrite(cyphertext.c_str(), 1, cyphertext.length(), f);
89 fclose(f);
90 }
91
saveFileBinary(const char * filename)92 void Cryptogram::saveFileBinary(const char *filename)
93 {
94 FILE *f;
95
96 f = fopen(filename, "wb");
97 if (!f)
98 return;
99 // Write the header
100 fwrite(FILE_HEADER, 1, strlen(FILE_HEADER) + 1, f);
101 // Write the alphabet
102 fwrite(alphabet, 1, sizeof(alphabet), f);
103 // Write the cyphertext
104 fwrite(cyphertext.c_str(), 1, cyphertext.length(), f);
105 fclose(f);
106 }
107
saveKeys(const char * filename)108 void Cryptogram::saveKeys(const char *filename)
109 {
110 FILE *f;
111 f = fopen(filename, "wb");
112 if (!f)
113 return;
114 fwrite(alphabet, 1, sizeof(alphabet), f);
115 fclose(f);
116 }
117
loadKeys(const char * filename)118 void Cryptogram::loadKeys(const char *filename)
119 {
120 FILE *f;
121 int i;
122 f = fopen(filename, "rb");
123 if (!f)
124 return;
125 for (i=0; i<sizeof(alphabet); i++)
126 alphabet[i] = ' ';
127 fread(alphabet, 1, sizeof(alphabet), f);
128 fclose(f);
129 for (i=0; i<sizeof(alphabet); i++)
130 if ((alphabet[i] < 'A') || (alphabet[i] > 'Z'))
131 alphabet[i] = ' ';
132 calcPlaintext();
133 }
134
set(char cypherLetter,char plainLetter)135 void Cryptogram::set(char cypherLetter, char plainLetter)
136 {
137 // Normalize to uppercase
138 cypherLetter = normalize(cypherLetter);
139 plainLetter = normalize(plainLetter);
140 if (isKey(cypherLetter))
141 alphabet[cypherLetter - 'A'] = plainLetter;
142 }
143
unset(char cypherLetter)144 void Cryptogram::unset(char cypherLetter)
145 {
146 set(cypherLetter, ' ');
147 }
148
randomizeKeys()149 void Cryptogram::randomizeKeys()
150 {
151 // Probably a better way of doing this....
152 int r1, r2;
153 char temp;
154 for (int i=0; i<26; i++)
155 alphabet[i] = i + 'A';
156 srand(time(NULL));
157 for (int i=0; i<1000; i++)
158 {
159 r1 = rand() % 26;
160 r2 = rand() % 26;
161 temp = alphabet[r1];
162 alphabet[r1] = alphabet[r2];
163 alphabet[r2] = temp;
164 }
165 }
166
randomizeMessage()167 void Cryptogram::randomizeMessage()
168 {
169 string tempStr;
170 bool matchKey;
171 do{
172 matchKey = false;
173 randomizeKeys();
174 for (int i=0; i<26; i++)
175 {
176 if (alphabet[i]==i+'A')
177 matchKey = true;
178 }
179 }while(matchKey);
180 calcPlaintext();
181 cyphertext = plaintext;
182 plaintext = string(" ", cyphertext.length());
183 calcPlaintext();
184 for (int i=0; i<26; i++)
185 alphabet[i] = ' ';
186 }
187
newRandomMessage()188 void Cryptogram::newRandomMessage()
189 {
190 int lines;
191 do{
192 system("fortune > tmp.tmp");
193 loadFile("tmp.tmp");
194 lines = 0;
195 for (string::iterator c = cyphertext.begin();
196 c != cyphertext.end();
197 c++)
198 {
199 if (*c == '\n')
200 lines++;
201 }
202 }while((lines < 3) || (lines > 8));
203 randomizeMessage();
204 }
205
calcPlaintext()206 void Cryptogram::calcPlaintext()
207 {
208 if (plaintext.length() != cyphertext.length())
209 plaintext = string(" ", cyphertext.length());
210 string::iterator p = plaintext.begin();
211 string::iterator c = cyphertext.begin();
212 char ch;
213 while (c != cyphertext.end())
214 {
215 // Get the character
216 ch = *c;
217 // Normalize it to uppercase
218 ch = normalize(ch);
219 // Calc the plaintext
220 if (isKey(ch))
221 {
222 *p = alphabet[ch - 'A'];
223 if (*p == ' ')
224 *p = '_';
225 } else {
226 *p = *c;
227 }
228 p++;
229 c++;
230 }
231 }
232
letterDistribution(char letters[],int counts[])233 void Cryptogram::letterDistribution(char letters[], int counts[])
234 {
235 char letter;
236 for (char c = 'A'; c <= 'Z'; c++)
237 {
238 letters[c-'A'] = c;
239 counts[c-'A'] = 0;
240 }
241 for (string::iterator t = cyphertext.begin(); t != cyphertext.end(); t++)
242 {
243 letter = normalize(*t);
244 if (isKey(letter))
245 counts[letter-'A']++;
246 }
247 // BUBBLESORT! YEAY!
248 int tempInt;
249 char tempChar;
250 for (int i=0; i < 26; i++)
251 for (int j = i; j < 26; j++)
252 if (counts[j] > counts[i])
253 {
254 tempChar = letters[i];
255 letters[i] = letters[j];
256 letters[j] = tempChar;
257 tempInt = counts[i];
258 counts[i] = counts[j];
259 counts[j] = tempInt;
260 }
261 }
262
wordDistribution()263 wordDistribution_t Cryptogram::wordDistribution()
264 {
265 wordDistribution_t result;
266 result.insert(wordDistributionPair_t(5, string("five")));
267 result.insert(wordDistributionPair_t(9, string("nine")));
268 result.insert(wordDistributionPair_t(2, string("two")));
269 result.insert(wordDistributionPair_t(12, string("twelve")));
270 result.insert(wordDistributionPair_t(15, string("fifteen")));
271 result.insert(wordDistributionPair_t(7, string("seven")));
272 result.insert(wordDistributionPair_t(9, string("nine-b")));
273 result.insert(wordDistributionPair_t(7, string("seven-b")));
274 return result;
275 }
276
normalize(char letter)277 char Cryptogram::normalize(char letter)
278 {
279 if ( (letter >= 'a') && (letter <= 'z') )
280 return letter - ('a' - 'A');
281 else
282 return letter;
283 }
284
isKey(char letter)285 bool Cryptogram::isKey(char letter)
286 {
287 bool result = false;
288 if ( (letter >= 'A') && (letter <= 'Z') )
289 result = true;
290 return result;
291 }
292
paintAlphabet(WINDOW * w)293 void Cryptogram::paintAlphabet(WINDOW *w)
294 {
295 // Print the alphabet...
296 char used[26];
297 bool duplicates = false;
298 char letter;
299
300 for (int i=0; i<26; i++)
301 used[i] = 0;
302
303 wcolor_set(w, 2, NULL);
304 wmove(w, 0, 0);
305 wprintw(w, "Cyphertext: ");
306 for (char c='A'; c <= 'Z'; c++)
307 wprintw(w, "%c", c);
308 wprintw(w, "\nPlaintext: ");
309 for (int i=0; i<26; i++)
310 {
311 letter = alphabet[i];
312 wprintw(w, "%c", letter);
313 if (isKey(letter))
314 {
315 if (used[letter-'A'] == 1)
316 duplicates = true;
317 else
318 used[letter-'A'] = 1;
319 }
320 }
321 if (duplicates)
322 wprintw(w, " <-- Warning: duplicates");
323 else
324 wprintw(w, " ");
325 mvwprintw(w, 2, 0, "%s %s by %s", PROGNAME, PROGVER, BYLINE);
326 }
327
paintMessage(WINDOW * w)328 void Cryptogram::paintMessage(WINDOW *w)
329 {
330 // 911:enigma THIS NEEDS WORK!
331 int here=0, newHere=0, max;
332 char *line;
333 // Print the message, interlaced...
334 wclear(w);
335 wmove(w, 0, 0);
336 // Find next \n
337 wcolor_set(w, 3, NULL);
338 max = cyphertext.length();
339 // Traverse the string, one line at a time
340 while (here != max)
341 {
342 // Print plaintext line
343 line = strdup(plaintext.c_str() + here);
344 for (char *c = line; *c; c++)
345 if (*c == '\n') *c = 0;
346 wattron(w, A_BOLD);
347 wprintw(w, "%s\n", line);
348 wattroff(w, A_BOLD);
349 free(line);
350 // Print cyphertext line
351 line = strdup(cyphertext.c_str() + here);
352 for (char *c = line; *c; c++)
353 if (*c == '\n') *c = 0;
354 wattron(w, A_DIM);
355 wprintw(w, "%s\n", line);
356 wattroff(w, A_DIM);
357 free(line);
358 // Move on to next line
359 newHere = cyphertext.find('\n', here);
360 // If no more newlines, ensure we are at the end
361 if (newHere == -1)
362 newHere = max;
363 else
364 here = newHere + 1;
365 //else
366 // here = newHere;
367 }
368 }
369
paintStats(WINDOW * w)370 void Cryptogram::paintStats(WINDOW *w)
371 {
372 // Print the statistics...
373 char letters[26];
374 int counts[26];
375 int row, col;
376 letterDistribution(letters, counts);
377 wordDistribution_t words = wordDistribution();
378
379 wclear(w);
380 mvwprintw(w, 1, 0,
381 "Number of times each letter/word appears in cyphertext:");
382 for (int i=0; i < 26; i++)
383 {
384 if (i < 13)
385 {
386 row = 3 + i;
387 col = 1;
388 } else {
389 row = 3 + i - 13;
390 col = 20;
391 }
392 wmove(w, row, col);
393 wprintw(w, "%c %4d\n", letters[i], counts[i]);
394 }
395 int c = 0;
396 for (wordDistribution_t::reverse_iterator t = words.rbegin();
397 t != words.rend() && c < 13;
398 t++, c++)
399 {
400 wmove(w, 3+c, 40);
401 wprintw(w, "%3d %s", (*t).first, (*t).second.c_str());
402 }
403 }
404