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