A substitution cipher replaces each letter in a message with the corresponding letter from a second alphabet. The ordinary alphabet supplies the letters to look up; the substitution alphabet supplies their replacements and acts as the secret key. Unlike a Caesar cipher, the key is not a rotation: it is the entire replacement alphabet. To exchange messages, both parties need the same mapping. section 15.4
In Julia, `join('A':'Z')` makes the ordinary alphabet, while `join(shuffle('A':'Z'))` makes a rearranged one. `zip` pairs their letters in order. For example, pairing `ABCDEF` with `ACFBDE` maps `B` to `C` and `C` to `F`. Collecting those pairs gives encryption and decryption a shared mapping. section 15.4.2
The same pairs work in both directions:
- To encrypt, find the pair whose first letter matches the input and return its second letter. section 15.4.2
- To decrypt, find the pair whose second letter matches the input and return its first letter. section 15.4.2
- If a character is absent from the alphabet, leave it unchanged. section 15.4.2
Listing 15.8 Substitution cipher encryption based on an array lookup
using Random
alphabet = join('A':'Z')
substitute = join(shuffle('A':'Z'))
mapping = collect(zip(alphabet, substitute))
function substitution_encrypt(ch::Char, mapping::Vector)
i = findfirst(row->first(row) == ch, mapping) #1
if isnothing(i) #2
ch
else
mapping[i][2] #3
end
end
function substitution_decrypt(ch::Char, mapping::Vector)
i = findfirst(row->last(row) == ch, mapping)
if isnothing(i)
ch
else
mapping[i][1]
end
end
The Julia listing puts forward and reverse lookup side by side, using `findfirst` on one vector of letter pairs. section 15.4.2
That mapping can be packaged in two ways. A `SubstitutionCipher` object can hold it and provide `encrypt` and `decrypt` methods. Alternatively, partially applied functions can capture it and return message-encryption and message-decryption functions. Both approaches let a password-keeping example exchange a substitution cipher for a Caesar cipher without rewriting the keeper. section 15.6 section 15.7.3
Encrypt and recover a message
With a generated mapping, `"HELLO" |> substitution_encrypt(mapping)` produces `"NEPPR"` in the shown run. Piping that result through `substitution_decrypt(mapping)` returns `"HELLO"`. The output depends on the shuffled alphabet; successful recovery depends on reusing the mapping. section 15.7.2
