Cipher: Polybius' checkerboard
The Polybius Checkerboard, named after Greek historian Polybius, is an example of a substitution cipher from around 205-123 B.C. .
Polybius was the first to propose a method of substituting different two-digit numbers for a letter in an alphabet. Hence, the Polybius’ Checkerboard is an early example of a substitution cipher.
Polybius’ Checkerboard
A Polybius’ checkerboard looks almost like a like a checkerboard. Each square holds a letter and we can locate a letter on the checkerboard by crossreferencing a unique row and column number.
Thus, each letter is represented by a number pair $(n,m)$, where the first number in the pair represents the row, and the second number in the pair represents the column on the checkerboard. If we does this for the Roman alphabet we get something like this:
In the checkerboard above, the letter $S$ is located on row 4 and column 3, so its representation is $(4,3)$.
Please not the exception in row-column (2,4) which holds two letters I/J.
The Polybius Ciphering Process
Using a Polybius’ Checkerboard, we can encipher a plaintext message by substituting each letter in the message with a number pair representing the letter’s position on the board.
For example, enciphering the plaintext message: $$“SENDMORETROOPS”$$
results in the ciphertext:
$$4315331432344215444234343543$$
The deciphering process would mean reverting the ciphering process, translating each number pair to its location o the checkerboard.
Russian Nihilist political prisoners are believed to have adapted a Polybius Checkerboard system to communicate secretly across prison cell walls.
A Simple Code Example
Let’s try to write a simple Polybius’ Checkerboard cipher program.
The following program makes a lot of assumptions on the input provided to the ciphering function. The program is only meant to illustrate the fundamental idea of the cipher.
1pub struct Checkerboard {
2 rows: [[char; 5]; 5],
3}
4
5impl Checkerboard {
6 /// Creates a new Polybius Checkerboard, preloaded with the Roman alphabet.
7 pub fn new() -> Self {
8 Checkerboard {
9 rows:
10 [['A','B','C','D','E'],
11 ['F','G','H','I','K'],
12 ['L','M','N','O','P'],
13 ['Q','R','S','T','U'],
14 ['V','W','X','Y','Z']]}
15 }
16
17 pub fn get(&self, row: usize, col: usize) -> Option<char> {
18 let val = self.rows.get(row)?.get(col)?;
19 Some(*val)
20 }
21
22 /// OBS! We increment the ith and jth value to adjust for
23 /// the common practice of starting a checkerboard index by
24 /// the values 1 (instead of 0, which is common in computer
25 /// science). Thus, the letter 'A' has the adjusted index of
26 /// (1,1) instead of (0,0).
27 pub fn get_position(&self, searchletter:char) -> Option<String> {
28 let mut res = String::new();
29 for (i, row) in self.rows.iter().enumerate() {
30 for (j, letter) in row.iter().enumerate() {
31 if letter == &searchletter {
32 res = (i+1).to_string();
33 res.push_str(&(j+1).to_string());
34 }
35 }
36 }
37 Some(res)
38 }
39
40 pub fn encipher(&self, plaintext: &str) -> String {
41 let mut ciphertext = String::new();
42 for c in plaintext.chars() {
43 ciphertext.push_str(&self.get_position(c).unwrap());
44 }
45
46 ciphertext
47 }
48}
49
50fn main() {
51 let checkerboard = Checkerboard::new();
52 println!("SENDMORETROOPS = {}", checkerboard.encipher("SENDMORETROOPS"));
53}
54
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 #[test]
61 fn encipher_test() {
62 let roman_checkerboard= Checkerboard::new();
63 assert_eq!(roman_checkerboard.encipher("SENDMORETROOPS"),
64 "4315331432344215444234343543");
65 }
66}