mirror of
https://github.com/Smaug123/ClassicalCiphers.jl
synced 2025-10-05 17:38:48 +00:00
Add Affine functionality - encrypt, decrypt, crack
This commit is contained in:
31
README.md
31
README.md
@@ -50,6 +50,37 @@ crack_caesar("Khoor, Zruog!")
|
||||
# outputs ("hello, world!", 3)
|
||||
```
|
||||
|
||||
### Affine cipher
|
||||
|
||||
Encrypt the text "Hello, World!" with the function `x -> 3x+4`:
|
||||
|
||||
```julia
|
||||
encrypt_affine("Hello, World!", 3, 4)
|
||||
# outputs
|
||||
```
|
||||
|
||||
Notice that `encrypt_affine` turns everything upper-case, but retains symbols.
|
||||
The multiplier is the second argument, and the additive constant is the third.
|
||||
|
||||
The multiplier must be coprime to 26, or an error is thrown.
|
||||
|
||||
Decrypt the same text:
|
||||
|
||||
```julia
|
||||
decrypt_affine("ZQLLU, SUDLN!", 3, 4)
|
||||
# outputs "hello, world!"
|
||||
```
|
||||
|
||||
Crack the same text:
|
||||
|
||||
```julia
|
||||
crack_affine("ZQLLU, SUDLN!")
|
||||
# outputs ((3, 4), "hello, world!")
|
||||
```
|
||||
|
||||
You can provide `mult=` or `add=` options to `crack_affine`, if they are known,
|
||||
to help it out.
|
||||
|
||||
### Monoalphabetic cipher
|
||||
|
||||
Encrypt the text "Hello, World!" with the same Caesar cipher, but
|
||||
|
@@ -8,9 +8,11 @@ include("caesar.jl")
|
||||
include("vigenere.jl")
|
||||
include("solitaire.jl")
|
||||
include("portas.jl")
|
||||
include("affine.jl")
|
||||
|
||||
export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic,
|
||||
encrypt_caesar, decrypt_caesar, crack_caesar,
|
||||
encrypt_affine, decrypt_affine, crack_affine,
|
||||
encrypt_vigenere, decrypt_vigenere, crack_vigenere,
|
||||
encrypt_portas, decrypt_portas,
|
||||
encrypt_solitaire, decrypt_solitaire,
|
||||
|
65
src/affine.jl
Normal file
65
src/affine.jl
Normal file
@@ -0,0 +1,65 @@
|
||||
using Iterators
|
||||
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
function encrypt_affine(plaintext, mult::Integer, add::Integer; offset=0)
|
||||
if mult % 2 == 0 || mult % 13 == 0
|
||||
error("Multiplier must be coprime to 26.")
|
||||
end
|
||||
|
||||
keystr = join([Char(((mult*i + add) % 26) + 97 - offset) for i in offset:25+offset], "")
|
||||
encrypt_monoalphabetic(plaintext, keystr)
|
||||
end
|
||||
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
function decrypt_affine(ciphertext, mult::Integer, add::Integer; offset=0)
|
||||
if mult % 2 == 0 || mult % 13 == 0
|
||||
error("Multiplier must be coprime to 26.")
|
||||
end
|
||||
|
||||
keystr = join([Char(((mult*i + add) % 26) + 97 - offset) for i in offset:25+offset], "")
|
||||
decrypt_monoalphabetic(ciphertext, keystr)
|
||||
end
|
||||
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
function crack_affine(ciphertext; mult=0, add=-1)
|
||||
mults = mult != 0 ? [mult] : [i for i in filter(x -> (x % 2 != 0 && x % 13 != 0), 1:25)]
|
||||
adds = add != -1 ? [add] : (0:25)
|
||||
|
||||
possible_keys = product(mults, adds)
|
||||
|
||||
decrypts = [(i, decrypt_affine(ciphertext, i[1], i[2])) for i in possible_keys]
|
||||
|
||||
sort!(decrypts, by=(x -> string_fitness(x[2])))
|
||||
|
||||
decrypts[end]
|
||||
end
|
22
test/affine.jl
Normal file
22
test/affine.jl
Normal file
@@ -0,0 +1,22 @@
|
||||
using ClassicalCiphers
|
||||
using Base.Test
|
||||
|
||||
# docs examples
|
||||
|
||||
@test encrypt_affine("Hello, World!", 3, 4) == "ZQLLU, SUDLN!"
|
||||
@test decrypt_affine("ZQLLU, SUDLN!", 3, 4) == "hello, world!"
|
||||
@test crack_affine("zqllu, sudln!") == ((3, 4), "hello, world!")
|
||||
|
||||
# Wikipedia examples
|
||||
|
||||
@test encrypt_affine("affine cipher", 5, 8) == "IHHWVC SWFRCP"
|
||||
@test decrypt_affine("IHHWVC SWFRCP", 5, 8) == "affine cipher"
|
||||
|
||||
# Practical Cryptography
|
||||
|
||||
@test encrypt_affine("defend the east wall of the castle", 5, 7) == uppercase("wbgbuw yqb bhty nhkk zg yqb rhtykb")
|
||||
@test decrypt_affine("wbgbuwyqbbhtynhkkzgyqbrhtykb", 5, 7) == "defendtheeastwallofthecastle"
|
||||
@test crack_affine("wbgbuwyqbbhtynhkkzgyqbrhtykb") == ((5, 7), "defendtheeastwallofthecastle")
|
||||
|
||||
@test crack_affine("wbgbuwyqbbhtynhkkzgyqbrhtykb", mult=5) == ((5, 7), "defendtheeastwallofthecastle")
|
||||
@test crack_affine("wbgbuwyqbbhtynhkkzgyqbrhtykb", add=7) == ((5, 7), "defendtheeastwallofthecastle")
|
@@ -1,6 +1,6 @@
|
||||
using ClassicalCiphers
|
||||
|
||||
tests = ["vigenere", "monoalphabetic", "solitaire", "caesar", "portas"]
|
||||
tests = ["vigenere", "monoalphabetic", "solitaire", "caesar", "portas", "affine"]
|
||||
|
||||
println("Running tests:")
|
||||
|
||||
|
Reference in New Issue
Block a user