diff --git a/README.md b/README.md index 023676e..246261f 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The Solitaire cipher is included for completeness, though it is perhaps not stri * [Caesar] * [Monoalphabetic substitution] * [Vigenère] +* [Portas] * [Solitaire] ## Gotchas @@ -131,6 +132,28 @@ It returns (key, decrypted text). If the keylength is known, specifying it as `crack_vigenere(str, keylength=6)` may aid decryption. +### Portas cipher + +Encrypt the text "Hello, World!" with a Portas cipher of key "ab": + +```julia +encrypt_portas("Hello, World!", "ab") +# outputs "URYYB, JBEYQ!" +``` + +Note that the input has been made uppercase, but symbols have been preserved. +The key is expected to be letters only; it is converted to uppercase and symbols +are stripped out before use. + +Decrypt the same text: + +```julia +decrypt_portas("URYYB, JBEYQ!", "ab") +# outputs "hello, world!" +``` + +Notice that the input has been made lowercase. + ### Solitaire cipher Encrypt the text "Hello, World!" with the Solitaire cipher, key "crypto": @@ -147,8 +170,8 @@ decrypt_solitaire("EXKYI ZSGEH UNTIQ", collect(1:54)) # outputs "aaaaaaaaaaaaaaa", as per https://www.schneier.com/code/sol-test.txt ``` - [Caesar]: https://en.wikipedia.org/wiki/Caesar_cipher [Vigenère]: https://en.wikipedia.org/wiki/Vigen%C3%A8re_cipher [Monoalphabetic substitution]: https://en.wikipedia.org/wiki/Substitution_cipher [Solitaire]: https://en.wikipedia.org/wiki/Solitaire_(cipher) +[Portas]: http://practicalcryptography.com/ciphers/porta-cipher/ diff --git a/src/ClassicalCiphers.jl b/src/ClassicalCiphers.jl index c96ec40..afdafc8 100644 --- a/src/ClassicalCiphers.jl +++ b/src/ClassicalCiphers.jl @@ -7,10 +7,12 @@ include("monoalphabetic.jl") include("caesar.jl") include("vigenere.jl") include("solitaire.jl") +include("portas.jl") export encrypt_monoalphabetic, decrypt_monoalphabetic, crack_monoalphabetic, encrypt_caesar, decrypt_caesar, crack_caesar, encrypt_vigenere, decrypt_vigenere, crack_vigenere, + encrypt_portas, decrypt_portas, encrypt_solitaire, decrypt_solitaire, string_fitness, index_of_coincidence diff --git a/src/portas.jl b/src/portas.jl new file mode 100644 index 0000000..051481d --- /dev/null +++ b/src/portas.jl @@ -0,0 +1,47 @@ +""" +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. +""" +function encrypt_portas(plaintext, key_in::AbstractString) + key = uppercase(letters_only(key_in)) + plaintext = uppercase(plaintext) + keyarr = [div(Int(ch) - 65, 2) for ch in key] + + keycounter = 1 + ans = IOBuffer() + + for i in 1:length(plaintext) + if ('A' <= plaintext[i] <= 'Z') + plainch = Int(plaintext[i]) # 68 + keych = keyarr[keycounter] # 4 + if 'Z' >= plaintext[i] >= 'M' + print(ans, Char(((plainch - 65 - keych + 13) % 13) + 65)) + else + print(ans, Char(((plainch - 65 + keych) % 13) + 65+13)) + end + + keycounter += 1 + if keycounter == length(key) + 1 + keycounter = 1 + end + else + print(ans, plaintext[i]) + end + end + + takebuf_string(ans) +end + +""" +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. +""" +function decrypt_portas(ciphertext, key::AbstractString) + lowercase(encrypt_portas(ciphertext, key)) +end \ No newline at end of file diff --git a/test/portas.jl b/test/portas.jl new file mode 100644 index 0000000..4a60897 --- /dev/null +++ b/test/portas.jl @@ -0,0 +1,12 @@ +using ClassicalCiphers +using Base.Test + +@test encrypt_portas("DEFENDTHEEASTWALLOFTHECASTLE", "FORTIFICATION") == uppercase("synnjscvrnrlahutukucvryrlany") +@test decrypt_portas("synnjscvrnrlahutukucvryrlany", "FORTIFICATION") == lowercase("DEFENDTHEEASTWALLOFTHECASTLE") + +@test decrypt_portas("synnjs cvr nrla hutu ku cvr yrlany!", "FORTIFICATION") == lowercase("DEFEND THE EAST WALL OF THE CASTLE!") + +# doc tests + +@test decrypt_portas("URYYB, JBEYQ!", "ab") == "hello, world!" +@test encrypt_portas("Hello, World!", "ab") == "URYYB, JBEYQ!" \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index fb789da..4c3879d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,6 +1,6 @@ using ClassicalCiphers -tests = ["vigenere", "monoalphabetic", "solitaire", "caesar"] +tests = ["vigenere", "monoalphabetic", "solitaire", "caesar", "portas"] println("Running tests:")