So, a few months ago (ok, probably about a year ago), I learned of this interesting programming language called D. It’s designed by a guy who specializes in compilers, and so his goal when he created D was to have a clean and easy to use syntax that is also very easy to compile, keep secure, and test.
I started out with something simple like The Euler Project, to learn the basics. That didn’t really keep my interest very long, and my lack of knowledge on how to appropriately program those algorithms irritated me rather than my lack of knowledge of the language. So I turned to an old, old idea that I had: The Cryptology Collection. A collection of classic ciphers and codes, plus the more modern ones (broken or otherwise).
Eventually I’d like to be able to apply this project to another project having to do with Cryptanalysis, and go from ancient to modern techniques with it, but first the (more or less,) basics. Naturally, the first one I started with last time was the ADFGVX Cipher, and as such that will be the one I use this time, but only in the programming language of D, and a bit more care taken to make the code efficient (at some expense to debug and readability, which will be improved later.)
You can follow my development on this project here.
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 | module classic.ciphers.ADFGVX; import std.string; class ADFGVX { private char[][] _polybius; private char[] _letters = ['A','D','F','G','V','X']; public this( ) { //TODO: Randomize the creation of this alphabet. string alphabet = "MBJYA,Z(?PS.)NX; DURITW:!HGLFECVO'KQ"; _polybius = new char[][6]; foreach( i, ref e; _polybius ) { e = new char[6]; } for( int row = 0, count = 0; row < 6; row++ ) { for( int column = 0; column < 6; column++ ) { _polybius[row][column] = alphabet[count]; count++; } } } public string encode( string plain, string key ) { char[] coordinates; char[][char] map; string cipher; //Get the coordinates of each letter in the message. foreach( char c ; plain.toUpper() ) { for( int y = 0; y < 6; y++ ) { for( int x = 0; x < 6; x++ ) { if( _polybius[y][x] == c ) { coordinates ~= _letters[x]; coordinates ~= _letters[y]; } } } } //Map these coordinates under the key letters, going across. foreach( int i, char c; coordinates ) { map[key[i%key.length]] ~= c; } //Transpose the collection of letters under each key letter into a single // line, after sorting the resulting map into alphabetical according to // the key characters. foreach( char c; map.keys.sort ) { cipher ~= map[c] ~ " "; } //Return the cipher text, stripping whitespace at each end of the message. return cipher.strip(); } public string decode( string cipher, string key ) { string plain; char[][char] map; string[] plainMap; char[] coordinates; //Get a "plain" mapping of the message, by splitting the cipher into an array. plainMap = cipher.split(" "); //Initialize the map that we'll be using to translate back into the plaintext. foreach( char c; key ) { map[c] = []; } //De-sort the collection into the actual map we'll be reconstructing the // coordinate string from. foreach( int i, string word; plainMap ) { foreach( char letter; word ) { map[map.keys.sort[i]] ~= letter; } } //Go through the map, and actual reconstruct the coordinate list. for( int i = 0; i < cipher.length; i++ ) { if( map[key[i % map.length]].length > 0 ) { coordinates ~= map[key[i % map.length]][0]; map[key[i % map.length]] = map[key[i % map.length]][1 .. $]; } } //Go through the coordinate list two at a time, grabbing the letters // at the X and Y coordinates. for( int i = 0; i < coordinates.length; i += 2 ) { int x, y; char cx = coordinates[i]; char cy = coordinates[i+i]; for( int k = 0; k < 6; k++ ) { if( cx == _letters[k] ) { x = k; } if( cy == _letters[k] ) { y = k; } } plain ~= _polybius[y][x]; } return plain; } //Test that a known plain text translates into a known cipher text. unittest { ADFGVX cipher = new ADFGVX( ); string text = "HELLO, WORLD."; string key = "fubar"; string test = cipher.encode( text, key ); writeln( test ); assert( test == "VFFDF XVVXX DVXGGD GXVGX VGAFV" ); } //Test that a known cipher text translates into a known plain text. unittest { ADFGVX cipher = new ADFGVX( ); string text = "VFFDF XVVXX DVXGGD GXVGX VGAFV"; string key = "fubar"; string test = cipher.decode( text, key ); assert( test == "HELLO, WORLD." ); } } |
