mirror of
https://github.com/Smaug123/ClassicalCiphers.jl
synced 2025-10-08 19:08:41 +00:00
Merged substitution.jl with monoalphabetic.jl, updating documentation accordingly
This commit is contained in:
24
README.md
24
README.md
@@ -26,7 +26,6 @@ The Solitaire cipher is included for completeness, though it is perhaps not stri
|
|||||||
* [Enigma (M3 Army)][Enigma]
|
* [Enigma (M3 Army)][Enigma]
|
||||||
* [Solitaire]
|
* [Solitaire]
|
||||||
* [Rail Fence]
|
* [Rail Fence]
|
||||||
* [Substitution]
|
|
||||||
* [Atbash]
|
* [Atbash]
|
||||||
|
|
||||||
## Gotchas
|
## Gotchas
|
||||||
@@ -384,28 +383,6 @@ julia> decrypt_railfence("WECRFACERDSOEE.LETNEAIVDEO", 3)
|
|||||||
"wearediscovered.fleeatonce"
|
"wearediscovered.fleeatonce"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Substitution cipher
|
|
||||||
|
|
||||||
```julia
|
|
||||||
julia> encrypt_substitution("Hello, this is plaintext", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm")
|
|
||||||
"ITSSG, ZIOL OL HSQOFZTBZ"
|
|
||||||
|
|
||||||
julia> encrypt_substitution("Hello, this is plaintext", "qwertyuiopasdfghjklzxcvbnm")
|
|
||||||
"ITSSG, ZIOL OL HSQOFZTBZ"
|
|
||||||
|
|
||||||
julia> encrypt_substitution("xyz", Dict('x' => 'd', 'y' => 'e', 'z' => 't'))
|
|
||||||
"DET"
|
|
||||||
|
|
||||||
julia> decrypt_substitution("ITSSG, ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm", reverse_dict = true)
|
|
||||||
"hello, this is plaintext"
|
|
||||||
|
|
||||||
julia> encrypt_atbash("some text", "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
"HLNV GVCG"
|
|
||||||
|
|
||||||
julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
"some text"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Atbash
|
### Atbash
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
@@ -422,5 +399,4 @@ encrypt_atbash("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz") == encry
|
|||||||
[Playfair]: https://en.wikipedia.org/wiki/Playfair_cipher
|
[Playfair]: https://en.wikipedia.org/wiki/Playfair_cipher
|
||||||
[Enigma]: https://en.wikipedia.org/wiki/Enigma_machine
|
[Enigma]: https://en.wikipedia.org/wiki/Enigma_machine
|
||||||
[Rail Fence]: https://en.wikipedia.org/wiki/Rail_fence_cipher
|
[Rail Fence]: https://en.wikipedia.org/wiki/Rail_fence_cipher
|
||||||
[Substitution]: https://en.wikipedia.org/wiki/Substitution_cipher
|
|
||||||
[Atbash]: https://en.wikipedia.org/wiki/Atbash
|
[Atbash]: https://en.wikipedia.org/wiki/Atbash
|
||||||
|
@@ -10,7 +10,7 @@ Documenter.makedocs(
|
|||||||
sitename = "ClassicalCiphers Documentation",
|
sitename = "ClassicalCiphers Documentation",
|
||||||
expandfirst = [],
|
expandfirst = [],
|
||||||
pages = [
|
pages = [
|
||||||
"Index" => "index.md",
|
"Home" => "index.md",
|
||||||
"Usage" => "usage.md"
|
"Usage" => "usage.md"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@@ -15,7 +15,6 @@ Currently implemented ciphers:
|
|||||||
- [Enigma (M3 Army)](https://en.wikipedia.org/wiki/Enigma_machine)
|
- [Enigma (M3 Army)](https://en.wikipedia.org/wiki/Enigma_machine)
|
||||||
- [Solitaire](https://en.wikipedia.org/wiki/Solitaire_(cipher))
|
- [Solitaire](https://en.wikipedia.org/wiki/Solitaire_(cipher))
|
||||||
- [Rail Fence](https://en.wikipedia.org/wiki/Rail_fence_cipher)
|
- [Rail Fence](https://en.wikipedia.org/wiki/Rail_fence_cipher)
|
||||||
- [Substitution](https://en.wikipedia.org/wiki/Substitution_cipher)
|
|
||||||
- [Atbash](https://en.wikipedia.org/wiki/Atbash)
|
- [Atbash](https://en.wikipedia.org/wiki/Atbash)
|
||||||
|
|
||||||
|
|
||||||
|
@@ -13,9 +13,8 @@ include("hill.jl")
|
|||||||
include("playfair.jl")
|
include("playfair.jl")
|
||||||
include("enigma.jl")
|
include("enigma.jl")
|
||||||
include("railfence.jl")
|
include("railfence.jl")
|
||||||
include("substitution.jl")
|
|
||||||
|
|
||||||
export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic,
|
export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic, encrypt_atbash, decrypt_atbash,
|
||||||
encrypt_caesar, decrypt_caesar, crack_caesar,
|
encrypt_caesar, decrypt_caesar, crack_caesar,
|
||||||
encrypt_affine, decrypt_affine, crack_affine,
|
encrypt_affine, decrypt_affine, crack_affine,
|
||||||
encrypt_vigenere, decrypt_vigenere, crack_vigenere,
|
encrypt_vigenere, decrypt_vigenere, crack_vigenere,
|
||||||
@@ -25,7 +24,6 @@ export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic,
|
|||||||
encrypt_playfair, decrypt_playfair,
|
encrypt_playfair, decrypt_playfair,
|
||||||
encrypt_enigma, decrypt_enigma,
|
encrypt_enigma, decrypt_enigma,
|
||||||
string_fitness, index_of_coincidence,
|
string_fitness, index_of_coincidence,
|
||||||
construct_railfence, encrypt_railfence, decrypt_railfence,
|
construct_railfence, encrypt_railfence, decrypt_railfence
|
||||||
encrypt_substitution, decrypt_substitution, encrypt_atbash, decrypt_atbash
|
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
@@ -149,7 +149,7 @@ function encrypt_enigma(plaintext,
|
|||||||
end
|
end
|
||||||
|
|
||||||
# validate reflector settings
|
# validate reflector settings
|
||||||
reflector = keystr_to_dict(parse_reflector(reflector_id))
|
reflector = _keystr_to_dict(parse_reflector(reflector_id))
|
||||||
|
|
||||||
# sanitise plaintext
|
# sanitise plaintext
|
||||||
plaintext = uppercase(letters_only(plaintext))
|
plaintext = uppercase(letters_only(plaintext))
|
||||||
@@ -162,11 +162,11 @@ function encrypt_enigma(plaintext,
|
|||||||
"VZBRGITYUPSDNHLXAWMJQOFECK"]
|
"VZBRGITYUPSDNHLXAWMJQOFECK"]
|
||||||
notches = Int[17,5,22,10,26]
|
notches = Int[17,5,22,10,26]
|
||||||
|
|
||||||
rotor1 = keystr_to_dict(rotor_layouts[rotors[1]])
|
rotor1 = _keystr_to_dict(rotor_layouts[rotors[1]])
|
||||||
notch1 = notches[rotors[1]]
|
notch1 = notches[rotors[1]]
|
||||||
rotor2 = keystr_to_dict(rotor_layouts[rotors[2]])
|
rotor2 = _keystr_to_dict(rotor_layouts[rotors[2]])
|
||||||
notch2 = notches[rotors[2]]
|
notch2 = notches[rotors[2]]
|
||||||
rotor3 = keystr_to_dict(rotor_layouts[rotors[3]])
|
rotor3 = _keystr_to_dict(rotor_layouts[rotors[3]])
|
||||||
notch3 = notches[rotors[3]]
|
notch3 = notches[rotors[3]]
|
||||||
|
|
||||||
rotor1_inv = Dict{Char, Char}(Pair{Char, Char}[reverse(a) for a in rotor1])
|
rotor1_inv = Dict{Char, Char}(Pair{Char, Char}[reverse(a) for a in rotor1])
|
||||||
|
@@ -1,21 +1,45 @@
|
|||||||
function keystr_to_dict(keystr::AbstractString)
|
#=
|
||||||
return Dict{Char, Char}(map(x -> (Char(x[1] + 64), x[2]), enumerate(uppercase(keystr))))
|
julia> @btime encrypt_monoalphabetic("THIS CODE WAS INVENTED BY JULIUS CAESAR", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
||||||
end
|
2.746 μs (21 allocations: 2.31 KiB)
|
||||||
|
"WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU"
|
||||||
|
|
||||||
"""
|
julia> @btime decrypt_monoalphabetic("WKLV FRGH ZDV LQYHQWHG EB MXOLXV FDHVDU", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
||||||
|
3.817 μs (31 allocations: 3.81 KiB)
|
||||||
|
"this code was invented by julius caesar"
|
||||||
|
=#
|
||||||
|
|
||||||
|
_keystr_to_dict(keystr::AbstractString) =
|
||||||
|
Dict{Char, Char}(Char(i + 64) => c for (i, c) in enumerate(uppercase(keystr)))
|
||||||
|
_keystr_to_dict(A::AbstractString, B::AbstractString) =
|
||||||
|
Dict{eltype(A), eltype(B)}(a => b for (a, b) in zip(uppercase(A), uppercase(B))) # policy decision: all dictionaries are uppercase
|
||||||
|
Base.reverse(D::Dict{T, S}) where {T, S} =
|
||||||
|
Dict{S, T}(reverse(p) for p in D)
|
||||||
|
_monoalphabetic_substitution(text, sub_dict::Dict{T, S}) where {T, S} =
|
||||||
|
S[get(sub_dict, c, c) for c in text]
|
||||||
|
|
||||||
|
# if using sub_dict directly, do not convert case
|
||||||
|
@doc raw"""
|
||||||
|
Arguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
|
||||||
```julia
|
```julia
|
||||||
encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
'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:
|
||||||
|
```julia
|
||||||
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Encrypts the given plaintext according to the monoalphabetic substitution cipher.
|
*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.
|
||||||
The key may be given as a Dict of replacements `Dict('a' => 'b', 'c' => 'd')`, etc,
|
|
||||||
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
|
||||||
`Dict('a' => 'k', 'e' => 'b', ...)`
|
|
||||||
|
|
||||||
If the key is given as a string, it is assumed that each character occurs only
|
As per convention, the output will always be uppercase.
|
||||||
once, and the string is converted to lowercase.
|
|
||||||
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
For more information, see [`https://en.wikipedia.org/wiki/Substitution_cipher`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
||||||
in particular, the string is not converted to lowercase automatically.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -27,58 +51,70 @@ julia> encrypt_monoalphabetic("Hello, World!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
|||||||
|
|
||||||
julia> encrypt_monoalphabetic("aBcbDd", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o'))
|
julia> encrypt_monoalphabetic("aBcbDd", Dict{Char, Char}('a' => '5', 'B' => '@', 'b' => 'o'))
|
||||||
"5@coDd"
|
"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"
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function encrypt_monoalphabetic(plaintext, key::Dict{Char, Char})
|
encrypt_monoalphabetic(plaintext, sub_dict::Dict{T, S}) where {T, S} =
|
||||||
# plaintext: string; key: dictionary of {'a' => 'b'}, etc, for replacing 'a' with 'b'
|
join(_monoalphabetic_substitution(plaintext, sub_dict))
|
||||||
return join(eltype(keys(key))[get(key, i, i) for i in plaintext])
|
encrypt_monoalphabetic(plaintext, A, B::AbstractString) =
|
||||||
end
|
encrypt_monoalphabetic(uppercase(plaintext), _keystr_to_dict(A, B))
|
||||||
|
# Fall back on using english alphabet as keys
|
||||||
|
encrypt_monoalphabetic(plaintext, B::AbstractString) =
|
||||||
|
encrypt_monoalphabetic(uppercase(plaintext), _keystr_to_dict(B))
|
||||||
|
|
||||||
"""
|
@doc raw"""
|
||||||
|
Arguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
|
||||||
```julia
|
```julia
|
||||||
decrypt_monoalphabetic(ciphertext, key::Dict{Char, Char})
|
'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:
|
||||||
|
```julia
|
||||||
|
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
|
||||||
```
|
```
|
||||||
|
|
||||||
Decrypts the given ciphertext according to the monoalphabetic substitution cipher.
|
*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.
|
||||||
The key may be given as a Dict of replacements `Dict('a' => 'b', 'c' => 'd')`, etc,
|
|
||||||
or as a 26-length string "keystringbcdfhjlmopqruvwxz", which is shorthand for
|
|
||||||
`Dict('a' => 'k', 'e' => 'b', ...)`
|
|
||||||
|
|
||||||
If the key is given as a string, it is assumed that each character occurs only
|
*If `reverse_dict` is set to true (as it is by default), the input dictionary is assumed to be the same used to _en_crypt, meaning it is reversed in order to _decrypt_ the ciphertext.*
|
||||||
once, and the string is converted to lowercase.
|
|
||||||
If the key is given as a Dict, the only substitutions made are those in the Dict;
|
As per convention, the output will always be lowercase.
|
||||||
in particular, the string is not converted to lowercase automatically.
|
|
||||||
|
For more information, see [`https://en.wikipedia.org/wiki/Substitution_cipher`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### Examples
|
### Examples
|
||||||
|
|
||||||
```julia
|
```julia
|
||||||
|
julia> decrypt_monoalphabetic("ITSSG, ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm", reverse_dict = true)
|
||||||
|
"hello, this is plaintext"
|
||||||
|
|
||||||
julia> decrypt_monoalphabetic("Khoor, Zruog!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
julia> decrypt_monoalphabetic("Khoor, Zruog!", "DEFGHIJKLMNOPQRSTUVWXYZABC")
|
||||||
"hello, world!"
|
"hello, world!"
|
||||||
```
|
```
|
||||||
"""
|
"""
|
||||||
function decrypt_monoalphabetic(ciphertext, key::Dict{Char, Char})
|
function decrypt_monoalphabetic(ciphertext, sub_dict::Dict{T, S}; reverse_dict::Bool = true) where {T, S}
|
||||||
# ciphertext: string; key: dictionary of {'a' => 'b'}, etc, where the plaintext 'a' was
|
sub_dict = reverse_dict ? reverse(sub_dict) : sub_dict
|
||||||
# replaced by ciphertext 'b'. No character should appear more than once
|
return join(_monoalphabetic_substitution(ciphertext, sub_dict))
|
||||||
# as a value in {key}.
|
|
||||||
return encrypt_monoalphabetic(ciphertext, Dict{Char, Char}(reverse(a) for a in key))
|
|
||||||
end
|
|
||||||
|
|
||||||
function encrypt_monoalphabetic(plaintext, key::AbstractString)
|
|
||||||
# plaintext: string; key: string of length 26, first character is the image of 'a', etc
|
|
||||||
# working in lowercase; key is assumed only to have each element appearing once
|
|
||||||
return encrypt_monoalphabetic(uppercase(plaintext), keystr_to_dict(key))
|
|
||||||
end
|
|
||||||
|
|
||||||
function decrypt_monoalphabetic(ciphertext, key::AbstractString)
|
|
||||||
# ciphertext: string; key: string of length 26, first character is the image of 'a', etc
|
|
||||||
# working in lowercase; key is assumed only to have each element appearing once
|
|
||||||
# and to be in lowercase
|
|
||||||
# so decrypt_monoalphabetic("cb", "cbade…") is "ab"
|
|
||||||
dict = Dict{Char, Char}(a => Char(96 + findfirst(==(a), lowercase(key))) for a in lowercase(key))
|
|
||||||
return encrypt_monoalphabetic(lowercase(ciphertext), dict)
|
|
||||||
end
|
end
|
||||||
|
decrypt_monoalphabetic(ciphertext, A, B; reverse_dict::Bool = true) =
|
||||||
|
lowercase(decrypt_monoalphabetic(uppercase(ciphertext), _keystr_to_dict(A, B); reverse_dict = reverse_dict))
|
||||||
|
decrypt_monoalphabetic(ciphertext, B; reverse_dict::Bool = true) =
|
||||||
|
lowercase(decrypt_monoalphabetic(uppercase(ciphertext), _keystr_to_dict(B); reverse_dict = reverse_dict))
|
||||||
|
|
||||||
# Cracking
|
# Cracking
|
||||||
|
|
||||||
@@ -231,3 +267,50 @@ function crack_monoalphabetic(
|
|||||||
|
|
||||||
return decrypt_monoalphabetic(ciphertext, key), key
|
return decrypt_monoalphabetic(ciphertext, key), key
|
||||||
end
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
```julia
|
||||||
|
encrypt_atbash(plaintext, alphabet)
|
||||||
|
```
|
||||||
|
|
||||||
|
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
|
||||||
|
```julia
|
||||||
|
encrypt_atbash(plaintext, "abcdefghijklmnopqrstuvwxyz") == encrypt_substitution(plaintext, "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba")
|
||||||
|
```
|
||||||
|
|
||||||
|
*Omitting the alphabet, it will assume you are using the English alphabet.*
|
||||||
|
"""
|
||||||
|
encrypt_atbash(plaintext, alphabet) =
|
||||||
|
encrypt_substitution(plaintext, alphabet, reverse(alphabet))
|
||||||
|
encrypt_atbash(plaintext) =
|
||||||
|
encrypt_atbash(plaintext, "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
|
||||||
|
"""
|
||||||
|
```julia
|
||||||
|
decrypt_atbash(ciphertext, alphabet)
|
||||||
|
```
|
||||||
|
|
||||||
|
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
|
||||||
|
```julia
|
||||||
|
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
|
||||||
|
julia> encrypt_atbash("some text", "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
"HLNV GVCG"
|
||||||
|
|
||||||
|
julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
"some text"
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
decrypt_atbash(ciphertext, alphabet) =
|
||||||
|
decrypt_substitution(ciphertext, reverse(alphabet), alphabet)
|
||||||
|
decrypt_atbash(ciphertext) =
|
||||||
|
decrypt_atbash(ciphertext, "abcdefghijklmnopqrstuvwxyz")
|
||||||
|
@@ -1,135 +0,0 @@
|
|||||||
construct_sub_dict(A, B) =
|
|
||||||
Dict{eltype(A), eltype(B)}(a => b for (a, b) in zip(lowercase(A), lowercase(B))) # policy decision: all dictionaries are lowercase
|
|
||||||
Base.reverse(D::Dict{T, S}) where {T, S} =
|
|
||||||
Dict{S, T}(reverse(p) for p in D)
|
|
||||||
something_crypt_substitution(text, sub_dict::Dict{T, S}) where {T, S} =
|
|
||||||
S[get(sub_dict, c, c) for c in text]
|
|
||||||
|
|
||||||
@doc raw"""
|
|
||||||
Arguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
|
|
||||||
```julia
|
|
||||||
'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:
|
|
||||||
```julia
|
|
||||||
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 and spaces.*
|
|
||||||
|
|
||||||
As per convention, the output will always be uppercase.
|
|
||||||
|
|
||||||
For more information, see [`https://en.wikipedia.org/wiki/Substitution_cipher`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```julia
|
|
||||||
julia> encrypt_substitution("Hello, this is plaintext", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm")
|
|
||||||
"ITSSG, ZIOL OL HSQOFZTBZ"
|
|
||||||
|
|
||||||
julia> encrypt_substitution("Hello, this is plaintext", "qwertyuiopasdfghjklzxcvbnm")
|
|
||||||
"ITSSG, ZIOL OL HSQOFZTBZ"
|
|
||||||
|
|
||||||
julia> encrypt_substitution("xyz", Dict('x' => 'd', 'y' => 'e', 'z' => 't'))
|
|
||||||
"DET"
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
encrypt_substitution(plaintext, sub_dict::Dict{T, S}) where {T, S} =
|
|
||||||
uppercase(join(something_crypt_substitution(lowercase(plaintext), sub_dict)))
|
|
||||||
encrypt_substitution(plaintext, A, B) =
|
|
||||||
encrypt_substitution(plaintext, construct_sub_dict(A, B))
|
|
||||||
encrypt_substitution(plaintext, B) =
|
|
||||||
encrypt_substitution(plaintext, "abcdefghijklmnopqrstuvwxyz", B)
|
|
||||||
|
|
||||||
@doc raw"""
|
|
||||||
Arguably the most simple of the classical ciphers, the substitution cipher works by creating an arbitrary substitution dictionary; e.g.,
|
|
||||||
```julia
|
|
||||||
'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:
|
|
||||||
```julia
|
|
||||||
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 and spaces.*
|
|
||||||
|
|
||||||
*If `reverse_dict` is set to true (as it is by default), the input dictionary is assumed to be the same used to _en_crypt, 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`](https://en.wikipedia.org/wiki/Substitution_cipher).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### Examples
|
|
||||||
|
|
||||||
```julia
|
|
||||||
julia> decrypt_substitution("ITSSG, ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm", reverse_dict = true)
|
|
||||||
"hello, this is plaintext"
|
|
||||||
|
|
||||||
julia> encrypt_atbash("some text", "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
"HLNV GVCG"
|
|
||||||
|
|
||||||
julia> decrypt_atbash("HLNV GVCG", "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
"some text"
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
function decrypt_substitution(ciphertext, sub_dict::Dict{T, S}; reverse_dict::Bool = true) where {T, S}
|
|
||||||
sub_dict = reverse_dict ? reverse(sub_dict) : sub_dict
|
|
||||||
return lowercase(join(something_crypt_substitution(lowercase(ciphertext), sub_dict)))
|
|
||||||
end
|
|
||||||
decrypt_substitution(ciphertext, A, B; reverse_dict::Bool = true) =
|
|
||||||
decrypt_substitution(ciphertext, construct_sub_dict(A, B); reverse_dict = reverse_dict)
|
|
||||||
decrypt_substitution(ciphertext, B; reverse_dict::Bool = true) =
|
|
||||||
decrypt_substitution(ciphertext, "abcdefghijklmnopqrstuvwxyz", B; reverse_dict = reverse_dict)
|
|
||||||
|
|
||||||
"""
|
|
||||||
```julia
|
|
||||||
encrypt_atbash(plaintext, alphabet)
|
|
||||||
```
|
|
||||||
|
|
||||||
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
|
|
||||||
```julia
|
|
||||||
encrypt_atbash(plaintext, "abcdefghijklmnopqrstuvwxyz") == encrypt_substitution(plaintext, "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba")
|
|
||||||
```
|
|
||||||
|
|
||||||
*Omitting the alphabet, it will assume you are using the English alphabet.*
|
|
||||||
"""
|
|
||||||
encrypt_atbash(plaintext, alphabet) =
|
|
||||||
encrypt_substitution(plaintext, alphabet, reverse(alphabet))
|
|
||||||
encrypt_atbash(plaintext) =
|
|
||||||
encrypt_atbash(plaintext, "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
|
|
||||||
"""
|
|
||||||
```julia
|
|
||||||
decrypt_atbash(ciphertext, alphabet)
|
|
||||||
```
|
|
||||||
|
|
||||||
A special case of the substitution cipher, the Atbash cipher substitutes a given alphabet with its reverse:
|
|
||||||
```julia
|
|
||||||
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.*
|
|
||||||
"""
|
|
||||||
decrypt_atbash(ciphertext, alphabet) =
|
|
||||||
decrypt_substitution(ciphertext, reverse(alphabet), alphabet)
|
|
||||||
decrypt_atbash(ciphertext) =
|
|
||||||
decrypt_atbash(ciphertext, "abcdefghijklmnopqrstuvwxyz")
|
|
@@ -1,33 +0,0 @@
|
|||||||
using ClassicalCiphers
|
|
||||||
using Test
|
|
||||||
|
|
||||||
@test encrypt_substitution("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz") == "HELLO THIS IS PLAINTEXT"
|
|
||||||
@test encrypt_substitution("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz") == "HELLO THIS IS PLAINTEXT"
|
|
||||||
@test encrypt_substitution("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm") == "ITSSG ZIOL OL HSQOFZTBZ"
|
|
||||||
@test encrypt_substitution("hello this is plaintext", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz") == "PCSSI EPHL HL JSKHYECUE"
|
|
||||||
@test encrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz") == "HELLO THIS IS PLAINTEXT"
|
|
||||||
@test encrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz") != encrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm")
|
|
||||||
@test encrypt_substitution("hello this is plaintext", Dict('n' => 'y','f' => 'n','w' => 'b','d' => 'm','e' => 'c','o' => 'i','h' => 'p','y' => 'f','i' => 'h','r' => 'd','t' => 'e','s' => 'l','j' => 'q','q' => 'a','k' => 'r','a' => 'k','c' => 'v','p' => 'j','m' => 'z','z' => 't','g' => 'o','x' => 'u','u' => 'g','l' => 's','v' => 'w','b' => 'x')) == "PCSSI EPHL HL JSKHYECUE"
|
|
||||||
@test encrypt_substitution("hello this is plaintext", Dict('n' => 'f','f' => 'y','w' => 'v','d' => 'r','e' => 't','o' => 'g','h' => 'i','j' => 'p','i' => 'o','k' => 'a','r' => 'k','s' => 'l','t' => 'z','q' => 'j','y' => 'n','a' => 'q','c' => 'e','p' => 'h','m' => 'd','z' => 'm','g' => 'u','v' => 'c','l' => 's','u' => 'x','x' => 'b','b' => 'w')) == "ITSSG ZIOL OL HSQOFZTBZ"
|
|
||||||
|
|
||||||
@test decrypt_substitution("hello this is ciphertext", "abcdefghijklmnopqrstuvwxyz", "abcdefghijklmnopqrstuvwxyz") == "hello this is ciphertext"
|
|
||||||
@test decrypt_substitution("hello this is ciphertext", "abcdefghijklmnopqrstuvwxyz") == "hello this is ciphertext"
|
|
||||||
@test decrypt_substitution("hello this is ciphertext", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = true) == "hello this is ciphertext"
|
|
||||||
@test decrypt_substitution("hello this is ciphertext", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz") == "itssg ziol ol eohitkztbz"
|
|
||||||
@test decrypt_substitution("hello this is ciphertext", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = false) == "pcssi ephl hl vhjpcdecue"
|
|
||||||
@test decrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = false) == "ozllu mogs gs iljgymzwm"
|
|
||||||
@test decrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = true) == "hello this is plaintext"
|
|
||||||
@test decrypt_substitution("PCSSI EPHL HL JSKHYECUE", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = false) == "jvllh cjps ps qlrpfcvgc"
|
|
||||||
@test decrypt_substitution("PCSSI EPHL HL JSKHYECUE", "qwertyuiopasdfghjklzxcvbnm", "abcdefghijklmnopqrstuvwxyz"; reverse_dict = true) == "hello this is plaintext"
|
|
||||||
@test decrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = false) != decrypt_substitution("ITSSG ZIOL OL HSQOFZTBZ", "abcdefghijklmnopqrstuvwxyz", "qwertyuiopasdfghjklzxcvbnm"; reverse_dict = true)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@test encrypt_atbash("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz") == "SVOOL GSRH RH KOZRMGVCG"
|
|
||||||
@test encrypt_atbash("hello this is plaintext") == "SVOOL GSRH RH KOZRMGVCG"
|
|
||||||
@test decrypt_atbash("SVOOL GSRH RH KOZRMGVCG", "abcdefghijklmnopqrstuvwxyz") == "hello this is plaintext"
|
|
||||||
@test decrypt_atbash("SVOOL GSRH RH KOZRMGVCG") == "hello this is plaintext"
|
|
||||||
@test encrypt_atbash("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz") == encrypt_substitution("hello this is plaintext", "abcdefghijklmnopqrstuvwxyz", "zyxwvutsrqponmlkjihgfedcba")
|
|
||||||
@test decrypt_atbash("SVOOL GSRH RH XRKSVIGVCG", "abcdefghijklmnopqrstuvwxyz") == decrypt_substitution("SVOOL GSRH RH XRKSVIGVCG", "zyxwvutsrqponmlkjihgfedcba", "abcdefghijklmnopqrstuvwxyz")
|
|
||||||
@test decrypt_atbash("SVOOL GSRH RH XRKSVIGVCG", "abcdefghijklmnopqrstuvwxyz") == decrypt_substitution("SVOOL GSRH RH XRKSVIGVCG", "zyxwvutsrqponmlkjihgfedcba"; reverse_dict = true)
|
|
Reference in New Issue
Block a user