|
rot13
|
May 24 2006, 11:49 AM
Post #1
|
- Posts:
- 306
- Group:
- Trusted Member
- Member
- #14
- Joined:
- September 29, 2005
|
Since we haven't done an ADFGVX here before, and this was the first time I ever tried one, I thought I'd share my approach. Hopefully someone has a better way.
The first goal is to untangle the transposition into the correct letter pairs. I decided to go with an Index of Coincidence approach. It allowed me to at least identify the likely pairings, although it did not necessarily mean that the letters were all in the right order. The idea is pretty simple - if you try to match together columns that don't match, the distribution of pairs should be a little more even. When you match the right columns together, the frequency of unique pairs should be closer to English, so the IC should be a good bit higher.
I hacked together a simple program in D (my new fave language for crypto tools) to try all possible permutations for key lengths from 3 to 8 (you can make it do more, but since 9 takes a good bit longer and I know now that the key length is 8, I dropped it back down). Here is the program:
- Code:
-
import std.stdio; import std.stream;
char[] best_result; double best_ic = 0; int best_depth;
char[] load_letters(char[] filename) { char[] chars_in = cast(char[])std.file.read(filename); char[] chars_out;
foreach (ch; chars_in) { if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'))) { chars_out ~= ch; } } return chars_out; }
int adfgvx_letter_to_number(char ch) { if ((ch == 'A') || (ch == 'a')) return 0; if ((ch == 'D') || (ch == 'd')) return 1; if ((ch == 'F') || (ch == 'f')) return 2; if ((ch == 'G') || (ch == 'g')) return 3; if ((ch == 'V') || (ch == 'v')) return 4; if ((ch == 'X') || (ch == 'x')) return 5; }
double compute_ic(char[] letters) { int counts[36] = 0; int all_counts;
for (int i=0; i < letters.length; i += 2) { int val = adfgvx_letter_to_number(letters[i]) * 6 + adfgvx_letter_to_number(letters[i+1]); counts[val]++; all_counts++; }
int sum = 0; for (int i=0; i < 36; i++) { sum += counts[i] * (counts[i]-1); }
return (cast(double) sum) / (cast(double) (all_counts*(all_counts-1))); }
void adfgvx_untramp(int[] order, int depth, int max_depth, char[] letters) { if (depth == max_depth) { // If order is the original key permutation, reverse tells how // to get back to that permutation int reverse[20]; for (int i=0; i < max_depth; i++) { reverse[order[i]] = i; } int num_per_row = letters.length / max_depth; int num_extra = letters.length % max_depth;
char[] results[20];
int pos = 0;
for (int i=0; i < max_depth; i++) { // Grab the letters from the text results[reverse[i]] = letters[pos..pos+num_per_row]; pos += num_per_row;
// If this is one of the columns that had an extra letter, grab it if (reverse[i] < num_extra) { results[reverse[i]] ~= letters[pos++]; } }
// Put the columns back together row-by-row char[] result; for (int row=0; row < num_per_row; row++) { for (int col=0; col < max_depth; col++) { result ~= results[col][row]; } }
// Add in the columns that had extra letters for (int col=0; col < num_extra; col++) { result ~= results[col][num_per_row]; }
double ic = compute_ic(result); if (ic > best_ic) { writefln("Best ic now %f at depth %d", ic, max_depth); best_ic = ic; best_result = result; best_depth = max_depth; } } else {
OrderLoop: for (int i=0; i < max_depth; i++) { for (int j=0; j < depth; j++) { if (order[j] == i) continue OrderLoop; } order[depth] = i; adfgvx_untramp(order, depth+1, max_depth, letters); } } }
int main(char[][] args) { char[] letters = load_letters(args[1]);
writefln("Loaded %d letters", letters.length); int[20] order = -1;
for (int i=3; i < 9; i++) { adfgvx_untramp(order, 0, i, letters); } writefln("Best key length = %d", best_depth); writefln("Best ic = %f", best_ic); writefln("Best order = "); for (int i=0; i < best_result.length; i += 2) { writef("%s%s ", best_result[i], best_result[i+1]); } writefln();
return 0; }
I run this program against the challenge text and this is the output:
- Code:
-
Loaded 1168 letters Best ic now 0.039105 at depth 3 Best ic now 0.039663 at depth 3 Best ic now 0.041331 at depth 4 Best ic now 0.043058 at depth 4 Best ic now 0.043451 at depth 6 Best ic now 0.043751 at depth 6 Best ic now 0.044021 at depth 8 Best ic now 0.044474 at depth 8 Best ic now 0.045690 at depth 8 Best ic now 0.047417 at depth 8 Best ic now 0.047622 at depth 8 Best ic now 0.048985 at depth 8 Best ic now 0.050136 at depth 8 Best ic now 0.054095 at depth 8 Best ic now 0.066566 at depth 8 Best ic now 0.074755 at depth 8 Best key length = 8 Best ic = 0.074755 Best order = AX VX FA DD VX DV AX VX FA DD DG AF VV FF VD DV VX FF DD AX DD FA VV DV VV VX GA DV DD DG VX DV VF AA FV VD AF GX VX AA DV XF DV VV VF FA VX XA VF VX VF AA DD VA FV VF FA FV VX DD DD XF VD DD AX VX FA DD DG AF VV FF VD DV VX FF DD AX DD FA VV DV VV VX AX XF VD DD AX VX VX DD VF XF DG DV AF VA VV DD DD XF DD DG VF AA GX GX AA DD DD VX VX FF GA VF VX DD DD AX VX DG DD AX DV FA AX VX VV DD VX AF VX DV FF DD AX XF AA AF VX AX GF VF AF VF VX VD DD AX AF FA FF DG VX VV DV AX AV VX VV DV DD VV AX FF VV AF DD AV AA DG VV AX AF DV AA FF VA VF XF DD VX AF XA AX FA VV VX XA DD AX DV VV FF AF DV AF DV VA DD FA VV DV GX VX VX DV DG DD VD VF VF VV DD VA DD FA VV DV VV VX AX XF XF VF DG DD AV DD VV DV AF VV GA VD VD XA DD FF VD DG DV DD AX VX AF AX GX VA VF VF DD GV XA FF DV AV FF VD DV VV FF AF VF AA DD VA AF XF AX VX VV XA XA FA AX VX VV DD AF DV AF FF VA DV FA DV DG AF GX FF AA XA AF GV XV GX VX DV AX VX GX DD VV DV VA VX VA VF VD DD AV VX DV DD DD AA DV XF VA VV FF DV DV DD FF VD VF VD DV FA VX VX DD GX VF VX GV VF GX AV AA DV VV DD VX VF DD AX DV FA AA VX VA DV VX DD VX VF DD AX AF FA FF DG DV VV FF VD AF XF AX VV FF DD VF VX VF GF VD AF VD AF AX VX DG DD GF DD AF VF AF AA GA VD FA VV VX XA AX VX XV DD GX DV VX GX DG XA DD VD DV FF DV GA VD AF VX VV AF AX DV VA FF VD DD AX DV AX VX FF FV VF XV GX XF DV XV DV DD AX AX VX VD DD DD XF VX VD VF VX AX VX FA DD DV DD VX VV FA VV VX XA VF VV VD VF DV AX VX FF FA VF DV DD DV VX DD DG DG VX DV DD VF VX DD DG VV GX VX DD DD AX XF XV XA VF FF GX DV AX DD XD VF VX VD DG AF AX VX VA AV VF DD AF DD AA DV VV VV AX AF DV VX FF DD AX DV FA AF VX DD VV VD DD XF VF DD AX AX VX VX DV VV AF DD VD VX AF DD AX VF DG DD VD AX VX VD AF VD GA DG VF AX VX VF DD AX VX DG DD DV AX VD VV FV VF AF DG VD DD VV FF
Since people might want to try cracking it from here, I'll put the rest of my steps in a second posting.
|