Computed Cryptograms
By Sam Bellotto Jr.
WHAT WITH SO MUCH ADO about nothing being made over government emails and insecure servers, I find myself asking “if said emails were so super Top Secret, why weren’t they encrypted?” Technologies exist today that can quickly code and decode text messages. Government servers are notoriously hackable; they are like Swiss cheese. You’d think a person would actually be commended for using another server. But I digress.
You see, I have a bit of experience with encryption schemes. In 2008, I co-authored a book with nationally-syndicated puzzle guru Terry Stickels entitled “Quotable Cryptograms.” It was published by Random House and you can still buy it on Amazon. As you may have guessed, the book included 500 fun cryptograms to crack. A variety of encrypting methods were used: from the simple letter-substitution variety (BE SURE TO DRINK YOUR OVALTINE), to the more advanced Rail Fence, Playfair, and Quagmire cryptograms.
But we didn’t encode all of the puzzles by hand; that would have taken ... well, we’d probably still be working on them! Rather, I wrote a variety of algorithms with which the computer would generate the finished puzzles. After the book was released, I took my code and developed a fun little Windows application called “Quiptics,” with which anybody can create their own cryptograms.
I’ve derived about as much income as I could hope for from both the book and the software. So at this time, I’d like to release some of this code to our readers of whom I am certain many have computers and are capable of writing applications. Whether or not you have any interest in doing this, I cannot say. But you might enjoy looking at some of the code. It is written in Delphi Pascal, a very popular language. Admittedly, the code isn’t commented as heavily as it should be, but it isn’t too difficult to figure out, if you have a solid knowledge of encryption and programming.
Common Cryptograms
The typical and most widely-known kind of cryptogram involves a random substitution of one letter in the plaintext alphabet with another letter of the ciphertext alphabet. No letter stands for itself. For example, all of the Bs may be replaced with Ms, and the Fs with Ys. This is done for every letter in the original text. Letters in kind are always replaced with the same letters in kind. In other words, the phrase “CRYPTOGRAMS ARE FUN PUZZLES TO SOLVE” after encryption becomes “MIZUGARIFXH FID CNQ UNSSBDH GA HABLD.”
The most basic and easiest of cryptograms use alphabet offset encryption. In these puzzles, the natural order of the alphabet is maintained—the positions are just shifted. For example, an alphabet offset cryptogram might have A encrypted to F, in which case B is G, C is H and so on. This cryptogram is known as a Caesar cipher. The method is named after Julius Caesar, who used it to communicate with his generals.
Here is the code for both random letter substitution and alphabet offset cryptogram generation:
procedure MakeRandomCipher(PT: String);
procedure MakeOffsetCipher(PT: String);
procedure Shuffle(Abcs:TStringList);
var
alpha: TStringList;
CT: String;
FromManual: Boolean = False;
implementation
procedure Shuffle(Abcs:TStringList);
var
randomIndex: integer;
cnt: integer;
begin
Randomize;
for cnt:=0 to-1+Abcs.Count do
begin
randomIndex:=Random(-cnt+Abcs.Count);
Abcs.Exchange(cnt,cnt+randomIndex);
end;
end;
procedure MakeRandomCipher(PT: String);
var
abc,xyz: String;
tmp: Char;
c,i,key,rg1: Integer;
begin
rg1:=StrToInt(copy(CipherRads,1,1));
if FromManual=False then
begin
alpha:=TStringList.Create;
abc:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for c:=65 to 90 do alpha.Add(Chr(c));
Shuffle(alpha);
xyz:='';
for i:=0 to 25 do xyz:=xyz+alpha[i];
alpha.Free
end else begin
(*Cipher key tool override*)
xyz:=CipherKey;
FromManual:=False
end;
(*Unique cryptogram rules*)
if rg1=1 then
begin
abc:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for key:=1 to Length(xyz)-1 do
begin
if xyz[key]=abc[key] then
begin
tmp:=xyz[key];
xyz[key]:=xyz[key+1];
xyz[key+1]:=tmp
end;
if xyz[26]='Z' then
begin
xyz[26]:=xyz[25];
xyz[25]:='Z'
end
end
end;
CipherKey:=xyz;
CT:=AnsiUpperCase(PT);
(*Encrypt text*)
for i:=1 to length(CT) do
begin
if CT[i] in['A'..'Z'] then
begin
key:=Ord(CT[i])-64;
CT[i]:=xyz[key]
end
end
end;
procedure MakeOffsetCipher(PT: String);
var
abc,xyz: String;
offset,key,i: Integer;
begin
abc:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
xyz:='';
Randomize;
offset:=Random(Length(abc))+1;
xyz:=copy(abc,offset,26)+copy(abc,1,offset-1);
(*Cipher key tool override*)
if FromManual=True then
begin
xyz:=CipherKey;
FromManual:=False
end;
CipherKey:=xyz;
CT:=AnsiUpperCase(PT);
(*Encrypt text*)
for i:=1 to length(CT) do
begin
if CT[i] in['A'..'Z'] then
begin
key:=Ord(CT[i])-64;
CT[i]:=xyz[key]
end
end
end;
Rail Fence Ciphers
The Rail Fence Cipher is a kind of transposition cipher. In other words, all of the letters in the plaintext appear in the encrypted text unchanged but scrambled. In a substitution cipher, like a cryptogram, the message is replaced by different letters in the alphabet. The key to the scrambling is why it is called a Rail Fence Cipher. In this cipher, the message is divided into columns of alternating rows, resembling a rail fence. The Rail Fence Cipher is a very old technique.
Here’s how to make a simple 2-rail cipher of the message QUIPTICS IS THE GREATEST. First count the number of letters in the plaintext. There are 21. For two rails, this must be divisible by 2, so add the needed letters at the end. These are called “nulls” and have little impact on understanding the decoded message. This message needs only one. QUIPTICS IS THE GREATESTX. Next divide the message into sections equalling the number of rails, eliminating spaces, alternating the letters according to the number of rails. Like this:
Q.I.T.C.I.T.E.R.A.E.T.
.U.P.I.S.S.H.G.E.T.S.X
See how it looks like a rail fence? This design is merely to make the process of encryption easier. The rows can be written directly above each other, if you prefer.
Finally combine all the individual groups into a single long string, first rail, second rail, and so on. The encoded message reads QITCITERAETUPISSHGETSX. There is no established format as to how the cipher is presented. In this article, we’ll break the message into chunks of letters equal to the number of rails, which becomes the “key,” as a default. QI TC IT ER AE TU PI SS HG ET SX indicates this is a two-rail cipher. QTIE TUIS BXIC TEVP SHSW indicates a four-rail cipher.
Now for the code:
procedure MakeRailfenceCipher(PT: String);
var
rg3: Integer;
CT: String;
implementation
procedure MakeRailfenceCipher(PT: String);
var
i,n,r: Integer;
qRail: array[1..4] of String;
begin
for i:=1 to 4 do qRail[i]:='';
(*Number of rails*)
r:=rg3+2;
CT:=AnsiUpperCase(PT);
for i:=1 to length(CT) do
if (CT[i] in ['A'..'Z']) then qRail[1]:=qRail[1]+CT[i];
CT:=qRail[1];
Randomize;
if length(CT) mod r<>0 then
repeat CT:=CT+Chr(Random(25)+65) until length(CT) mod r=0;
(*Encipher*)
qRail[1]:='';
i:=0;
n:=0;
repeat
repeat
inc(n);
if n<=length(CT) then
begin
inc(i);
qRail[i]:=qRail[i]+CT[n]
end
until (i=r) or (n>length(CT));
repeat
inc(n);
if n<=length(CT) then
begin
dec(i);
qRail[i]:=qRail[i]+CT[n]
end
until (i=1) or (n>length(CT))
until n>length(CT);
CT:='';
for i:=1 to r do CT:=CT+qRail[i];
qRail[1]:='';
(*Format output*)
for i:=1 to length(CT) do
begin
qRail[1]:=qRail[1]+CT[i];
if i mod r=0 then qRail[1]:=qRail[1]+' '
end;
CT:=qRail[1]
end;
Playfair Ciphers
Playfair Ciphers are generated from an arbitrary 5×5 matrix using all the letters of the alphabet except the letter Q. This is the standard convention that we have adopted for this article. Any Qs that appear in the plaintext are automatically converted to K. Thus, the word EQUAL will be spelled EKUAL. The word QUALITY will be spelled KUALITY. This should not confuse anybody.
The Key Table is made by filling in the matrix cells, left to right, top to bottom, with all 25 letters of the alphabet (Q is not used). Start with the keyphrase, dropping any duplicate letters. Then add the unused letters of the alphabet, in order. This is the standard Playfair encoding scheme.
Here’s an example Key Table using CONUNDRUM as the keyphrase:
C O N U D
R M A B E
G F H I J
K L P S T
V W X Y Z
To encrypt a message, break the message into groups of 2 letters known as digraphs. If both letters are the same (or only one letter is left), add an “X” after the first letter. Encrypt the new pair and continue. (“CRYPTOGRAM” becomes “CR YP TO GR AM", however “BOTTLE” would be prepped as “BO TX TL EX.") Now encode using the key table and the following rules, in order, for each letter pair.
Basic encrypting is accomplished by replacing each digraph with the letters on the same row respectively but at the other pair of corners of the rectangle defined by the original pair. The order is important. The first encrypted letter of the pair is the one that lies on the same row as the first plaintext letter.
BO is encrypted as MU. RY as BV. AT as EP.
If the letters appear on the same ROW of the table, replace them with the letters to their immediate right, respectively (wrapping around to the left side of the row if a letter in the original pair was on the right side of the row).
CO is encrypted as ON. HI as IJ. BE is ER. ST is TK.
If the letters appear on the same column of the table, replace them with the letters immediately below, respectively (wrapping around to the top side of the column if a letter in the original pair was on the bottom side of the column).
CR is encrypted as RG. HP as PX. SY as YU. LW as WO.
Following these rules, here is how CRYPTOGRAM is encrypted.
CR YP TO GR AM
RG XS LD KG BA
The code is quite a bit more complex, as it involves first generating the key table and then encrypting the message. Here it is:
procedure MakePlayfairCipher(PT: String);
function SwapKeywordRoute(ks: String): String;
var
CT,keystr: String;
implementation
procedure MakePlayfairCipher(PT: String);
var
kt: array[1..5,1..5] of char;
ss: String;
i,x,y,nA,nB: Integer;
twin: Boolean;
function TheKeyString(const ed: String): String;
var
keyS,abc: String;
n: Integer;
begin
(*Create key table*)
abc:='ABCDEFGHIJKLMNOPRSTUVWXYZ';
keyS:=ed+abc;
Result:='';
(*Drop duplicates*)
for n:=1 to length(keyS) do
begin
if pos(keyS[n],abc)<>0 then
begin
Result:=Result+keyS[n];
Delete(abc,pos(keyS[n],abc),1)
end
end
end;
function ArrayX(c: Char): Integer;
var
xx,yy: Integer;
begin
Result:=1;
for yy:=1 to 5 do
for xx:=1 to 5 do
if kt[xx,yy]=c then
Result:=xx
end;
function ArrayY(c: Char): Integer;
var
xx,yy: Integer;
begin
Result:=1;
for yy:=1 to 5 do
for xx:=1 to 5 do
if kt[xx,yy]=c then
Result:=yy
end;
begin
keystr:=PlayKey;
ss:=TheKeyString(keystr);
(*Adjust to offset*)
ss:=AnsiRightStr(ss,StrToInt(PlayOff))
+AnsiLeftStr(ss,length(ss)-StrToInt(PlayOff));
if PKvert then ss:=SwapKeywordRoute(ss);
i:=0;
for y:=1 to 5 do
for x:=1 to 5 do
begin
inc(i);
kt[x,y]:=ss[i]
end;
CT:='';
ss:='';
CT:=AnsiUpperCase(PT);
(*Remove extraneous characters*)
for i:=1 to length(CT) do
if (CT[i] in ['A'..'P','R'..'Z']) then ss:=ss+CT[i]
else if CT[i]='Q' then ss:=ss+'K';
CT:=ss;
ss:='';
(*Prep plaintext*)
repeat
twin:=false;
for i:=1 to length(CT)-1 do
if (Odd(i) and (CT[i]=CT[i+1])) then
begin
insert('X',CT,i+1);
twin:=true;
Break
end
until twin=false;
ss:=CT;
if Odd(length(ss)) then
if ss[length(ss)]='X' then ss:=ss+'J' else ss:=ss+'X';
(*Encode*)
nA:=1;
nB:=2;
CT:='';
repeat
if ArrayY(ss[nA])=ArrayY(ss[nB]) then
begin
y:=ArrayY(ss[nA]);
if ArrayX(ss[nA])=5 then CT:=CT+kt[1,y]
else CT:=CT+kt[ArrayX(ss[nA])+1,y];
if ArrayX(ss[nB])=5 then CT:=CT+kt[1,y]
else CT:=CT+kt[ArrayX(ss[nB])+1,y]
end else begin
if ArrayX(ss[nA])=ArrayX(ss[nB]) then
begin
x:=ArrayX(ss[nA]);
if ArrayY(ss[nA])=5 then CT:=CT+kt[x,1]
else CT:=CT+kt[x,ArrayY(ss[nA])+1];
if ArrayY(ss[nB])=5 then CT:=CT+kt[x,1]
else CT:=CT+kt[x,ArrayY(ss[nB])+1]
end else begin
CT:=CT+kt[ArrayX(ss[nB]),ArrayY(ss[nA])];
CT:=CT+kt[ArrayX(ss[nA]),ArrayY(ss[nB])];
end
end;
inc(nA,2);
inc(nB,2)
until nB=length(ss)+2
end;
function SwapKeywordRoute(ks: String): String;
var
y,x: Integer;
k: String;
begin
k:='';
for y:=1 to 5 do
for x:=0 to 4 do
k:=k+ks[y+x*5];
Result:=k
end;
Quagmire Ciphers
The Quagmire cipher is devilishly complex, based upon a number of cryptographic elements. It is a type of Vigenère cipher. Basically, a Quagmire cipher is made from a ciphertext table that consists of stacked alphabet rows below a key row:
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
The top row is the plaintext alphabet. Beneath is the ciphertext table. Shown above before applying keywords. Quagmires can have up to three different keywords.
The top plaintext row is formed using a keyword much the same way the Playfair matrix is formed. Begin with the keyphrase, dropping any duplicate letters. Then the unused letters of the alphabet, in order. Using TERRY STICKELS as the keyphrase, the key row comes out:
T E R Y S I C K L A B D F G H I M N O P Q U V W X Z
The rows in the ciphertext table are then formed using the same keyphrase as the key row, or another keyphrase altogether.
Let’s use CRYPTOGRAM as the ciphertext table keyphrase. The ciphertext table would look thus:
T E R Y S I C K L A B D F G H I M N O P Q U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
C R Y P T O G A M B D E F H I J K L N Q S U V W X Z
However, Quagmires all use an indicator key. The indicator key establishes the “period”—the number of rows in the table—and the letter with which each alphabet row begins. If the indicator key is SAMUEL, it would direct six rows thus:
T E R Y S I C K L A B D F G H I M N O P Q U V W X Z
S T U V W X Y Z A B C D E F G H I J K L M N O P Q R
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
M N O P Q R S T U V W X Y Z A B C D E F G H I J K L
U V W X Y Z A B C D E F G H I J K L M N O P Q R S T
E F G H I J K L M N O P Q R S T U V W X Y Z A B C D
L M N O P Q R S T U V W X Y Z A B C D E F G H I J K
Combining the indicator key with the ciphertext table key and we get the actual table to use for encoding the plaintext:
T E R Y S I C K L A B D F G H I M N O P Q U V W X Z
S U V W X Z C R Y P T O G A M B D E F H I J K L N Q
A M B D E F H I J K L N Q S U V W X Z C R Y P T O G
M B D E F H I J K L N Q S U V W X Z C R Y P T O G A
U V W X Z C R Y P T O G A M B D E F H I J K L N Q S
E F H I J K L N Q S U V W X Z C R Y P T O G A M B D
L N Q S U V W X Z C R Y P T O G A M B D E F H I J K
The indicator key does not always have to appear down the first column. It can be any of the 26 columns, adjusting the alphabet rows accordingly. The same ciphertext table with the indicator key in column 10:
T E R Y S I C K L A B D F G H I M N O P Q U V W X Z
E F H I J K L N Q S U V W X Z C R Y P T O G A M B D
X Z C R Y P T O G A M B D E F H I J K L N Q S U V W
Z C R Y P T O G A M B D E F H I J K L N Q S U V W X
F H I J K L N Q S U V W X Z C R Y P T O G A M B D E
Y P T O G A M B D E F H I J K L N Q S U V W X Z C R
M B D E F H I J K L N Q S U V W X Z C R Y P T O G A
Encoding is relatively simple. Locate each plaintext letter on the keyed plaintext row (top) and replace it with the corresponding letter of an ciphertext table row, depending upon period order. The first letter of the plaintext is replaced by a letter from the first ciphertext table row, the second letter from the second row, the third letter from the third row, and so on. If the period consists of six rows, after the sixth letter, cycle back to the first row.
So the phrase CRYPTOGRAMS ARE FUN PUZZLES TO SOLVE after encryption becomes LCYOY CXCMY GLHZE AQRGW XSPFE KPTDT F. Quagmires are conventionally broken up into five-character units.
A Quagmire I cipher uses a keyed plaintext row run against a straight cipher table.
A Quagmire II cipher uses an unkeyed plaintext row alphabet run against a keyed cipher table.
A Quagmire III cipher uses both a keyed plaintext row and a keyed cipher table sharing the same keyword.
A Quagmire IV cipher uses a uniquely different key for the plaintext row, the cipher table and the period key.
Following is the code for all types of Quagmire cryptograms:
procedure CreateTheArray;
procedure MakeQuagmireCipher(PT: String);
var
CT,key,ckey,pkey: String;
c: Integer;
QuagArray: Array[0..7] of String;
implementation
function TheKeyString(const ed: String): String;
var
keyS,abc: String;
i: Integer;
begin
(*Create keyed alphabet*)
abc:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
keyS:=ed+abc;
Result:='';
(*Drop duplicates*)
for i:=1 to length(keyS) do
begin
if pos(keyS[i],abc)<>0 then
begin
Result:=Result+keyS[i];
Delete(abc,pos(keyS[i],abc),1)
end
end
end;
procedure CreateTheArray;
var
pp: String;
i,k: Integer;
begin
(*Period key is required*)
if pkey='' then
if (ckey<>'') then pkey:=StringOfChar(ckey[1],7) else pkey:='AAAAAAA';
for i:=0 to 7 do QuagArray[0]:='';
if key<>'' then QuagArray[0]:=TheKeyString(key)
else QuagArray[0]:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
if (QuagCKey=QuagKey) then pp:=QuagArray[0] else
if ckey<>'' then pp:=TheKeyString(ckey) else
pp:='ABCDEFGHIJKLMNOPQRSTUVWXYZ';
k:=0;
i:=0;
while i<length(pkey) do
begin
inc(k);
inc(i);
pp:=copy(pp,pos(pkey[i],pp),80)+copy(pp,1,pos(pkey[i],pp)-1);
(*Indicator key*)
pp:=RightBStr(pp,c-1)+LeftBStr(pp,26-c+1);
QuagArray[k]:=pp
end
end;
procedure MakeQuagmireCipher(PT: String);
var
i,p,y,x: Integer;
ss: String;
begin
key:=QuagKey;
ckey:=QuagCKey;
pkey:=copy(QuagIndi,1,pos('@',QuagIndi)-1);
c:=StrToInt(copy(QuagIndi,pos('@',QuagIndi)+1,3));
CT:='';
ss:='';
(*Remove extraneous characters*)
for i:=1 to length(PT) do
if (PT[i] in ['A'..'Z','a'..'z']) then ss:=ss+PT[i];
(*Clean plaintext*)
PT:=UpperCase(ss);
CreateTheArray;
(*Period*)
p:=length(pkey);
y:=p;
(*Make ciphertext*)
for i:=1 to length(PT) do
begin
if y=p then y:=1 else inc(y);
x:=pos(PT[i],QuagArray[0]);
CT:=CT+QuagArray[y][x]
end;
(*Format output*)
ss:='';
for i:=1 to length (CT) do
begin
ss:=ss+CT[i];
if i mod 5=0 then ss:=ss+' '
end;
CT:=ss
end;
In conclusion, let me add that the primary purpose of this article is to present tested and true code blocks for generating a variety of popular cryptograms. If you are interested in a deeper understanding of the ciphers themselves, the Internet is chock full of information on the subject.
Sam Bellotto Jr. is the Editor of “Perihelion Science Fiction.” He also writes stories, having been published in “Bewildering Stories,” “Twisted Tails” anthologies, “Third Flatiron” anthologies, and elsewhere. And he has published lots of puzzles.