Usage
Main.ClassicalCiphers.AbstractPair
— TypeAbstractPair{F, S} = Union{Tuple{F, S}, Pair{F, S}}
A simple wrapper for a Pair
, or a Tuple
representing a pair of objects.
Main.ClassicalCiphers.adjugate
— Methodadjugate(mat::AbstractArray{Integer, 2})
Computes the adjugate matrix for given matrix.
Main.ClassicalCiphers.construct_railfence
— Methodconstruct_railfence(input::AbstractString, n_rails::Integer)
construct_railfence(input::AbstractArray{T}, n_rails::Integer) where {T <: Number}
See https://en.wikipedia.org/wiki/Rail_fence_cipher
.
Examples
julia> construct_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3)
3×26 Array{Char,2}:
'W' '□' '□' '□' 'E' '□' '□' '□' 'C' '□' '□' '□' 'R' … '□' '□' 'F' '□' '□' '□' 'A' '□' '□' '□' 'C' '□'
'□' 'E' '□' 'R' '□' 'D' '□' 'S' '□' 'O' '□' 'E' '□' '□' '.' '□' 'L' '□' 'E' '□' 'T' '□' 'N' '□' 'E'
'□' '□' 'A' '□' '□' '□' 'I' '□' '□' '□' 'V' '□' '□' 'D' '□' '□' '□' 'E' '□' '□' '□' 'O' '□' '□' '□'
Main.ClassicalCiphers.crack_affine
— Methodcrack_affine(ciphertext; mult::Integer = 0, add::Integer = -1)
Cracks the given ciphertext according to the Affine cipher. Returns ((multiplier, additive constant), decrypted string)
.
Converts the input to lowercase, but retains symbols.
Optional arguments: mult=0
, which specifies the multiplier if known; add=-1
, which specifies the additive constant if known.
Examples
julia> crack_affine("ZQLLU, SUDLN!")
("hello, world!", (3, 4))
Main.ClassicalCiphers.crack_caesar
— Methodcrack_caesar(ciphertext; cleverness::Integer = 1)
Cracks the given ciphertext according to the Caesar cipher. Returns (plaintext, key::Integer)
, such that encrypt_caesar(plaintext, key)
would return ciphertext.
With cleverness=0
, simply does the shift that maximises e's frequency. With cleverness=1
, maximises the string's total fitness.
Converts the input to lowercase.
Examples
julia> crack_caesar("Khoor, Zruog!")
("hello, world!", 3)
Main.ClassicalCiphers.crack_monoalphabetic
— Methodcrack_monoalphabetic(
ciphertext;
starting_key::AbstractString = "",
min_temp::AbstractFloat = 0.0001,
temp_factor::AbstractFloat = 0.97,
acceptance_prob::AbstractFloat = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
chatty::Integer = 0,
rounds::Integer = 1
)
crack_monoalphabetic cracks the given ciphertext which was encrypted by the monoalphabetic substitution cipher.
Returns (the derived key, decrypted plaintext)
.
The various optional arguments to crack_monoalphabetic
are:
starting_key=""
, which when specified (for example, as "ABCDEFGHIJKLMNOPQRSTUVWXYZ"), starts the simulation at the given key. The default causes it to start with the most common characters being decrypted to the most common English characters.min_temp=0.0001
, which is the temperature at which we stop the simulation.temp_factor=0.97
, which is the factor by which the temperature decreases each step.chatty=0
, which can be set to 1 to print whenever the key is updated, or 2 to print whenever any new key is considered (prints tostdout
).rounds=1
, which sets the number of repetitions we perform. Each round starts with the best key we've found so far.acceptance_prob=((e, ep, t) -> ep>e ? 1 : exp(-(e-ep)/t))
, which is the probability with which we accept new key of fitness ep, given that the current key has fitness e, at temperature t.
Examples
julia> crack_monoalphabetic(str, chatty=0, rounds=10)
(decrypted_string, key)
Main.ClassicalCiphers.crack_monoalphabetic
— Methodcrack_monoalphabetic(
io::IO,
ciphertext;
starting_key::AbstractString = "",
min_temp::AbstractFloat = 0.0001,
temp_factor::AbstractFloat = 0.97,
acceptance_prob::AbstractFloat = ((e,ep,t) -> ep > e ? 1. : exp(-(e-ep)/t)),
chatty::Integer = 0,
rounds::Integer = 1
)
crack_monoalphabetic cracks the given ciphertext which was encrypted by the monoalphabetic substitution cipher.
Returns (the derived key, decrypted plaintext)
.
The various optional arguments to crack_monoalphabetic
are:
starting_key=""
, which when specified (for example, as "ABCDEFGHIJKLMNOPQRSTUVWXYZ"), starts the simulation at the given key. The default causes it to start with the most common characters being decrypted to the most common English characters.min_temp=0.0001
, which is the temperature at which we stop the simulation.temp_factor=0.97
, which is the factor by which the temperature decreases each step.chatty=0
, which can be set to 1 to print whenever the key is updated, or 2 to print whenever any new key is considered.rounds=1
, which sets the number of repetitions we perform. Each round starts with the best key we've found so far.acceptance_prob=((e, ep, t) -> ep>e ? 1 : exp(-(e-ep)/t))
, which is the probability with which we accept new key of fitness ep, given that the current key has fitness e, at temperature t.
Examples
julia> crack_monoalphabetic(str, chatty=0, rounds=10)
(decrypted_string, key)
Main.ClassicalCiphers.crack_vigenere
— Methodcrack_vigenere(plaintext; keylength::Integer = 0)
Cracks the given text encrypted with the Vigenere cipher.
Returns (derived key, decrypted plaintext)
.
Optional parameters: keylength=0
: if the key length is known, specifying it may help the solver. If 0, the solver will attempt to derive the key length using the index of coincidence.
Examples
julia> crack_vigenere(str)
Main.ClassicalCiphers.decrypt_affine
— Methoddecrypt_affine(ciphertext, mult::Integer, add::Integer; offset::Integer=0)
Decrypts the given ciphertext according to the Affine cipher. The key is given as a pair of integers: first the multiplier, then the additive constant.
The multiplier must be coprime to 26. If it is not, an error is thrown.
Converts the input to lowercase, but retains symbols.
Optional argument: offset=0
, which specifies what number 'a' should be considered as.
Examples
julia> decrypt_affine("ZQLLU, SUDLN!", 3, 4)
"hello, world!"
Main.ClassicalCiphers.decrypt_atbash
— Methoddecrypt_atbash(ciphertext, alphabet)
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
decrypt_atbash(ciphertext, "abcdefghijklmnopqrstuvwxyz") == decrypt_substitution(ciphertext, "zyxwvutsrqponmlkjihgfedcba", "abcdefghijklmnopqrstuvwxyz")
decrypt_atbash(ciphertext, "abcdefghijklmnopqrstuvwxyz") == decrypt_substitution(ciphertext, "zyxwvutsrqponmlkjihgfedcba"; reverse_dict = true)
Omitting the alphabet, it will assume you are using the English alphabet.
Examples
julia> encrypt_atbash("some text", "abcdefghijklmnopqrstuvwxyz")
"HLNV GVCG"
julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
"some text"
Main.ClassicalCiphers.decrypt_caesar
— Methoddecrypt_caesar(ciphertext, key::Integer)
decrypt_caesar(ciphertext)
Decrypts the given ciphertext according to the Caesar cipher. The key is given as an integer, being the offset of each character; so decrypt_caesar("abcd", 1) == "zabc"
.
Converts the input to lowercase, but retains symbols.
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will fall back to if only given plaintext.
Examples
julia> decrypt_caesar("Khoor, Zruog!", 3)
"hello, world!"
Main.ClassicalCiphers.decrypt_enigma
— MethodSee encrypt_enigma
as this function uses identical arguments.
Main.ClassicalCiphers.decrypt_hill
— Methoddecrypt_hill(ciphertext, key::AbstractArray{T, 2}) where {T <: Integer}
Examples
julia> decrypt_hill("PLHCGQWHRY", [1 2; 5 7]) # Decrypt the text "PLHCGQWHRY" with key of `[1 2; 5 7]`
"helloworld"
julia> decrypt_hill("PLHCGQWHRY", "bcfh")
"helloworld"
julia> decrypt_hill("PLHCIX", "bcfh") # If the plaintext-length is not a multiple of the dimension of the key matrix, it is padded with X
"hellox"
Main.ClassicalCiphers.decrypt_monoalphabetic
— MethodArguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
'a' => 'x'
'b' => 'g'
'c' => 'l'
...
This dictionary then replaces every corresponding letter in the plaintext input with a different letter (as specified by the dictionary input.)
The function decrypt_substitution
will either take this dictionary as its second parameter, or it can construct the dictionary itself:
decrypt_substitution(ciphertext, Dict(...); reverse_dict = true)
decrypt_substitution(ciphertext, "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba"; reverse_dict = true) # this will create the dictionary 'a' => 'z', 'b' => 'y', ..., 'z' => 'a'
decrypt_substitution(ciphertext, "zyxwvutsrqponmlkjihgfedcba"; reverse_dict = true) # this will create the dictionary 'a' => 'z', 'b' => 'y', ..., 'z' => 'a' by assuming the keys in the substitution dictionary
All characters undefined in the dictionary are preserved by default; this includes punctionation, spaces, and cases. This means that, when using a dictionary, strings are not automatically converted into lowercase.
If reverse_dict
is set to true (as it is by default), the input dictionary is assumed to be the same used to encrypt, meaning it is reversed in order to decrypt the ciphertext.
As per convention, the output will always be lowercase.
For more information, see https://en.wikipedia.org/wiki/Substitution_cipher
.
Examples
julia> decrypt_monoalphabetic("ITSSG, ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm", reverse_dict = true)
"hello, this is plaintext"
julia> decrypt_monoalphabetic("Khoor, Zruog!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
"hello, world!"
Main.ClassicalCiphers.decrypt_playfair
— Methoddecrypt_playfair(ciphertext, key::Array{Char, 2}; combined::AbstractPair{Char, Char} = ('I', 'J'))
Decrypts the given ciphertext according to the Playfair cipher.
Does not attempt to delete X's inserted as padding for double letters.
Examples
julia> decrypt_playfair("RMRMFWYE", "playfair example")
"ixixyzax"
Main.ClassicalCiphers.decrypt_portas
— Methoddecrypt_portas(ciphertext, key::AbstractString)
Decrypts the given ciphertext with the Portas cipher.
The key must be given as a string, whose characters are letters.
Converts the text to lowercase.
Examples
julia> decrypt_portas("URYYB, JBEYQ!", "ab")
"hello, world!"
Main.ClassicalCiphers.decrypt_railfence
— Methodencrypt_railfence(input::AbstractString, n_rails::Integer)
See https://en.wikipedia.org/wiki/Rail_fence_cipher
.
Examples
julia> decrypt_railfence("WECRFACERDSOEE.LETNEAIVDEO", 3)
"wearediscovered.fleeatonce"
Main.ClassicalCiphers.decrypt_solitaire
— Methoddecrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
Decrypts the given ciphertext according to the Solitaire cipher. The key may be given either as a vector initial deck, where the cards are 1 through 54 (the two jokers being 53, 54), or as a string. Schneier's keying algorithm is used to key the deck if the key is a string.
Examples
julia> decrypt_solitaire("EXKYI ZSGEH UNTIQ", collect(1:54)) # as per https://www.schneier.com/code/sol-test.txt
"aaaaaaaaaaaaaaa"
Main.ClassicalCiphers.decrypt_vigenere
— Methoddecrypt_vigenere(ciphertext, key::Array)
decrypt_vigenere(plaintext, key::AbstractString)
Decrypts the given string using the Vigenere cipher according to the given vector of offsets. For example, decrypt_vigenere("ac", [0, 1])
returns "ab"
.
Examples
julia> decrypt_vigenere("HFLMOXOSLE", [0, 1]) # Notice that the offset `0` corresponds to the key `a`.
"helloworld"
Main.ClassicalCiphers.encrypt_affine
— Methodencrypt_affine(plaintext, mult::Integer, add::Integer; offset::Integer = 0)
Encrypts the given plaintext according to the Affine cipher. The key is given as a pair of integers: first the multiplier, then the additive constant.
The multiplier must be coprime to 26. If it is not, an error is thrown.
Converts the input to uppercase, but retains symbols.
Optional argument: offset=0
, which specifies what number 'a' should be considered as.
Examples
julia> encrypt_affine("Hello, World!", 3, 4)
"ZQLLU, SUDLN!"
Main.ClassicalCiphers.encrypt_atbash
— Methodencrypt_atbash(plaintext, alphabet)
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
encrypt_atbash(plaintext, "abcdefghijklmnopqrstuvwxyz") == encrypt_substitution(plaintext, "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba")
Omitting the alphabet, it will assume you are using the English alphabet.
Main.ClassicalCiphers.encrypt_caesar
— Methodencrypt_caesar(plaintext, key::Integer)
encrypt_caesar(plaintext)
Encrypts the given plaintext according to the Caesar cipher. The key is given as an integer, being the offset of each character; so encrypt_caesar("abc", 1) == "BCD"
.
Converts the input to uppercase, but retains symbols.
Traditionally, the Caesar cipher was used with a shift of 3, so this is the method it will fall back to if only given plaintext.
Examples
julia> encrypt_caesar("Hello, World!", 3)
"KHOOR, ZRUOG!"
Main.ClassicalCiphers.encrypt_enigma
— Methodfunction encrypt_enigma(plaintext,
rotors::Array{Integer, 1}, key::AbstractString;
reflector_id='B', ring::AbstractString = "AAA",
stecker = Tuple{Char, Char}[],
skip_stecker_check = false)
Encrypts the given plaintext according to the Enigma (M3, army version).
Arguments are in the order: plaintext, stecker, rotors, ring, key.
Plaintext is a string; punctuation is stripped out and it is made lowercase. Rotors is an array - for example, [1,2,3]
- being the order of the rotors. Each entry should be a distinct integer between 1 and 5 inclusive. Key is a string of three letters, indicating the starting positions of the rotors.
Optional:
reflector_id='B'
, which sets whether to use reflector A, B or C.
Can also be specified as a 26-char string.
- Stecker is either an array - for example,
[('A','B'), ('D', 'E')]
specifying
that A, B are swapped and D, E are swapped - or a string ("ABDE" accomplishing the same thing). No letter may appear more than once.
- Ring is a string - for example, "AAA" - being the offset applied to each rotor.
"AAA", for example, signifies no offset. The string must be three letters.
skip_stecker_check=false
, which whentrue
skips validation of stecker settings.
Examples
julia> encrypt_enigma("AAA", [1,2,3], "ABC")
"CXT"
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id='B', stecker="") # synonymous with above
"CXT"
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id="YRUHQSLDPXNGOKMIEBFZCWVJAT", stecker="") # synonymous with above
"CXT"
julia> encrypt_enigma("AAA", [1,2,3], "ABC", ring="AAA", reflector_id='B', stecker=Tuple{Char, Char}[]) # synonymous with above
"CXT"
Main.ClassicalCiphers.encrypt_hill
— Methodencrypt_hill(plaintext::AbstractString, key::AbstractArray{Integer, 2})
Encrypts the given plaintext according to the Hill cipher. The key may be given as a matrix (that is, two-dimensional Array{Int}
) or as a string.
If the key is given as a string, the string is converted to uppercase before use, and symbols are removed. It is assumed to be of square integer length, and the matrix entries are filled top-left to top-right, then next-top left to next-top right, and so on down to bottom-left to bottom-right. If the string is not of square integer length, an error is thrown.
The matrix must be invertible modulo 26. If it is not, an error is thrown.
Examples
julia> encrypt_hill("Hello, World!", [1 2; 5 7]) # Encrypt the text "Hello, World!" with a Hill key of matrix `[1 2; 5 7]`
"PHHRGUWQRV"
julia> encrypt_hill("Hello, World!", "bcfh")
"PLHCGQWHRY"
julia> encrypt_hill("Hello", "bcfh") # If the plaintext-length is not a multiple of the dimension of the key matrix, it is padded with X
"PLHCIX"
Main.ClassicalCiphers.encrypt_monoalphabetic
— MethodArguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
'a' => 'x'
'b' => 'g'
'c' => 'l'
...
This dictionary then replaces every corresponding letter in the plaintext input with a different letter (as specified by the dictionary input.)
The function encrypt_substitution
will either take this dictionary as its second parameter, or it can construct the dictionary itself:
encrypt_substitution(plaintext, Dict(...))
encrypt_substitution(plaintext, "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba") # this will create the dictionary 'a' => 'z', 'b' => 'y', ..., 'z' => 'a'
encrypt_substitution(plaintext, "zyxwvutsrqponmlkjihgfedcba") # this will create the dictionary 'a' => 'z', 'b' => 'y', ..., 'z' => 'a' by assuming the keys in the substitution dictionary
All characters undefined in the dictionary are preserved by default; this includes punctionation, spaces, and cases. This means that, when using a dictionary, strings are not automatically converted into uppercase.
As per convention, the output will always be uppercase.
For more information, see https://en.wikipedia.org/wiki/Substitution_cipher
.
Examples
julia> encrypt_monoalphabetic("Hello, World!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
"KHOOR, ZRUOG!"
julia> encrypt_monoalphabetic("aBcbDd", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o'))
"5@coDd"
julia> encrypt_monoalphabetic("Hello, this is plaintext", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm")
"ITSSG, ZIOL OL HSQOFZTBZ"
julia> encrypt_monoalphabetic("Hello, this is plaintext", "qwertyuiopasdfghjklzxcvbnm")
"ITSSG, ZIOL OL HSQOFZTBZ"
julia> encrypt_monoalphabetic("xyz", Dict('x' => 'd', 'y' => 'e', 'z' => 't'))
"det"
Main.ClassicalCiphers.encrypt_playfair
— Methodencrypt_playfair(plaintext, key::Array{Char, 2}; stripped::Bool = false, combined::AbstractPair{Char, Char} = ('I', 'J'))
Encrypts the given plaintext according to the Playfair cipher. Throws an error if the second entry in the combined
tuple is present in the key.
Optional parameters:
stripped=false
. When set to true, encrypt_playfair
skips converting the plaintext to uppercase, removing punctuation, and combining characters which are to be combined in the key. combined=('I', 'J')
, marks the characters which are to be combined in the text. Only the first of these two may be present in the output of encrypt_playfair
.
Examples
julia> encrypt_playfair("Hello, World!", "playfair example")
"DMYRANVQCRGE"
julia> arr = ['P' 'L' 'A' 'Y' 'F'; 'I' 'R' 'E' 'X' 'M'; 'B' 'C' 'D' 'G' 'H'; 'K' 'N' 'O' 'Q' 'S'; 'T' 'U' 'V' 'W' 'Z'];
julia> encrypt_playfair("Hello, World!", arr) # Encrypt the same text using an explicitly specified keysquare
"DMYRANVQCRGE"
julia> encrypt_playfair("IJXYZA", "PLAYFIREXM", combined=('I', 'J')) # Optionally specify the two letters which are to be combined (default 'I','J')
"RMRMFWYE"
julia> encrypt_playfair("IJXYZA", "PLAYFIREXM", combined=('X', 'Z'))
"BSGXEY"
Main.ClassicalCiphers.encrypt_portas
— Methodencrypt_portas(plaintext, key_in::AbstractString)
Encrypts the given plaintext with the Portas cipher.
The key must be given as a string, whose characters are letters.
Converts the text to uppercase.
Examples
julia> encrypt_portas("Hello, World!", "ab")
"URYYB, JBEYQ!"
Main.ClassicalCiphers.encrypt_railfence
— Methodencrypt_railfence(input::AbstractString, n_rails::Integer)
See https://en.wikipedia.org/wiki/Rail_fence_cipher
.
Examples
julia> encrypt_railfence("WE ARE DISCOVERED. FLEE AT ONCE", 3) # this reads the above matrix row by row
"WECRFACERDSOEE.LETNEAIVDEO"
Main.ClassicalCiphers.encrypt_solitaire
— Methodencrypt_solitaire(string::AbstractString, initialDeck::AbstractVector{T}) where {T <: Integer}
Encrypts the given plaintext according to the Solitaire cipher. The key may be given either as a vector initial deck, where the cards are 1 through 54 (the two jokers being 53, 54), or as a string. Schneier's keying algorithm is used to key the deck if the key is a string.
Examples
julia> encrypt_solitaire("Hello, World!", "crypto")
"GRNNQISRYA"
Main.ClassicalCiphers.encrypt_vigenere
— Methodencrypt_vigenere(plaintext, key::Array)
encrypt_vigenere(ciphertext, key::AbstractString)
Encrypts the given string using the Vigenere cipher according to the given vector of offsets. For example, encrypt_vigenere("ab", [0, 1])
returns "AC"
.
Examples
julia> encrypt_vigenere("Hello, World!", "ab")
"HFLMOXOSLE"
Main.ClassicalCiphers.frequencies
— Methodfrequencies(input::AbstractString)
Finds the frequencies of all characters in the input string, returning a Dict
of 'a' => 4
, for instance. Uppercase characters are considered distinct from lowercase.
Main.ClassicalCiphers.index_of_coincidence
— Methodindex_of_coincidence(input::AbstractString)
Finds the index of coincidence of the input string. Uppercase characters are considered to be equal to their lowercase counterparts.
Main.ClassicalCiphers.playfair_key_to_square
— Methodplayfair_key_to_square(key::AbstractString, replacement::AbstractPair{Char, Char})
Converts the given key-string to a Playfair key square.
Parameter replacement
is a pair, such as ('I', 'J')
or 'I' => 'J'
, containing the two letters which are combined. Only the first of these letters will be present in the keysquare.
Main.ClassicalCiphers.string_fitness
— MethodPerforms a trigram analysis on the input string, to determine how close it is to English. That is, splits the input string into groups of three letters, and assigns a score based on the frequency of the trigrams in true English.
Main.ClassicalCiphers.swap_two
— Methodswap_two(str)
swap_two(string) swaps two of the characters of the input string, at random. The characters are guaranteed to be at different positions, though "aa" would be 'swapped' to "aa".