OfflineDictionaryAttack.hs (2018B)
1 {-# LANGUAGE OverloadedStrings #-} 2 {-# LANGUAGE RecordWildCards #-} 3 4 module Main where 5 6 import Cryptopals.SRP.Simple (Env(..), defaultEnv) 7 import qualified Cryptopals.Digest.Pure.SHA as CS 8 import qualified Cryptopals.DH as DH 9 import qualified Data.Binary as DB 10 import qualified Data.ByteString.Base16 as B16 11 import qualified Data.ByteString.Char8 as B8 12 import qualified Data.ByteString.Lazy as BL 13 import qualified Data.ByteString.Lazy.Char8 as BL8 14 import qualified Data.HashMap.Lazy as HML 15 import qualified Data.Text as T 16 import qualified Data.Text.Encoding as TE 17 import qualified Data.Text.IO as TIO 18 import Numeric.Natural 19 import qualified Options.Applicative as O 20 21 populate :: Natural -> IO (HML.HashMap BL.ByteString BL.ByteString) 22 populate herpub = do 23 let Env {..} = defaultEnv 24 dict <- BL8.readFile "/usr/share/dict/words" 25 let derive x = ((herpub `mod` en) * (DH.modexp eg x en)) `mod` en 26 27 let ls = BL8.lines dict 28 ns = fmap (fromIntegral . CS.integerDigest . CS.sha256) ls :: [Natural] 29 ss = fmap derive ns 30 hs = fmap (CS.bytestringDigest . CS.sha256 . DB.encode) ss 31 ms = fmap (\s -> CS.bytestringDigest (CS.hmacSha256 s mempty)) hs 32 33 pure . HML.fromList $ zip ms ls 34 35 data Args = Args { 36 argsNat :: Natural 37 , argsMAC :: T.Text 38 } 39 40 ops :: O.Parser Args 41 ops = Args 42 <$> O.argument O.auto (O.metavar "PUBLICKEY") 43 <*> O.argument O.str (O.metavar "MAC") 44 45 crack :: Args -> IO () 46 crack Args {..} = do 47 let mac = BL.fromStrict . B16.decodeBase16Lenient $ TE.encodeUtf8 argsMAC 48 dict <- populate argsNat 49 case HML.lookup mac dict of 50 Nothing -> TIO.putStrLn "(cryptopals) couldn't crack password" 51 Just pw -> do 52 let s = BL.toStrict pw 53 B8.putStrLn "(cryptopals) success" 54 B8.putStrLn $ "(cryptopals) password: " <> s 55 56 main :: IO () 57 main = do 58 let pars = O.info (O.helper <*> ops) $ 59 O.fullDesc 60 <> O.progDesc "perform an offline dictionary attack" 61 <> O.header "offline-dictionary-attack" 62 63 args <- O.execParser pars 64 65 crack args 66