Add Affine functionality - encrypt, decrypt, crack

This commit is contained in:
Smaug123
2016-01-06 19:56:06 +00:00
parent 6eb59cde05
commit 6a6aa3d078
6 changed files with 122 additions and 1 deletions

View File

@@ -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

View File

@@ -1 +1,2 @@
julia 0.4
Iterators

View File

@@ -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
View 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
View 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")

View File

@@ -1,6 +1,6 @@
using ClassicalCiphers
tests = ["vigenere", "monoalphabetic", "solitaire", "caesar", "portas"]
tests = ["vigenere", "monoalphabetic", "solitaire", "caesar", "portas", "affine"]
println("Running tests:")