I wrote the following that tries to crack a message that was encrypted via a simple Caesar cipher through brute force. I'm fairly new to Haskell, so any and all input is appreciated.
Because there are infinite ways that something could have been encrypted, it assumes very simple "offset" encryption; like what decShift
uses. It also doesn't assume any "wrapping" if an offset character goes out of range.
It works fine (from my testing), I'd just like any input on how it could be made more readable, idiomatic, or generally better.
Use:
simpDec
"Encrypted Message", which returns a list of tuples where the first element is the key used to obtain the decryption, and the second is the decrypted message (it returns a list because it's possible for it to return more then one possible decryption).
decUpper = 127 --Upperbound of normal characters
decLower = 32 --Lowerbound of normal characters
decRange = decUpper - decLower --Total range
type Message = String
--Checks if the char is in range
isInRange :: Char -> Bool
isInRange char = ichar <= decUpper && ichar >= decLower
where ichar = ord char
--Makes sure all chars in the Message are in range
isPossible :: Message -> Bool
isPossible [] = False
isPossible m = null [letter | letter <- m, not $ isInRange letter]
--Returns a list > 0 if it finds consecutive symbols
conSymList :: Message -> Char -> [Char]
conSymList [] _ = []
conSymList (x:xs) lastChar
| isSym x && isSym lastChar = x : conSymList xs x
| otherwise = conSymList xs x
where isSym c = isSymbol c || isPunctuation c
--Checks if a string has 2 symbols beside each other (meaning its improbable)
isProbable :: Message -> Bool
isProbable [] = False
isProbable m = null $ conSymList m 'a'
--Shifts all characters by n
decShift :: Message -> Int -> Message
decShift [] _ = []
decShift (x:xs) n
| iNewChar > 0 = chr iNewChar : decShift xs n
| otherwise = x : decShift xs n
where iNewChar = ord x + n
--Returns a list of tuples of the offset used, and the shifted Message
getCombos :: Message -> [(Int,Message)]
getCombos m = [(off,decShift m off) | off <- [decRange * (-1)..decRange]]
--Returns a list of tuples of possible decryptions (KeyUsed,Message)
simpDec :: Message -> [(Int, Message)]
simpDec m = [(key,try) | (key,try) <- getCombos m, isPossible try && isProbable try]