{-# LANGUAGE OverloadedStrings #-} module ExpensiveHash where import Types import qualified Data.ByteString as B import Raaz.Core.Encode import qualified Crypto.Argon2 as Argon2 import Data.Time.Clock import Control.DeepSeq -- | A hash that is expensive to calculate. -- -- This is a lynchpin of keysafe's security, because using this hash -- as an encryption key forces brute force attackers to generate -- hashes over and over again, taking a very long time. data ExpensiveHash = ExpensiveHash Cost B.ByteString deriving (Show) data Salt t = Salt t expensiveHash :: Encodable t => RunMode -> Salt t -> Password -> ExpensiveHash expensiveHash runmode (Salt s) (Password password) = ExpensiveHash cost $ Argon2.hash o password (toByteString s) where HashParams o cost = hashParams runmode data HashParams = HashParams Argon2.HashOptions Cost hashParams :: RunMode -> HashParams hashParams SecureMode = HashParams o cost where -- argon2 is GPU and ASIC resistent, so it uses CPU time. -- The selected HashOptions were benchmarked at 661 seconds CPU time -- on a 2 core Intel(R) Core(TM) i5-4210Y CPU @ 1.50GHz. cost = CPUCost (Seconds 600) o = Argon2.HashOptions { Argon2.hashIterations = 10000 , Argon2.hashMemory = 131072 -- 128 mebibtyes per thread , Argon2.hashParallelism = 4 -- 4 threads , Argon2.hashVariant = Argon2.Argon2i } hashParams TestingMode = HashParams Argon2.defaultHashOptions $ CPUCost (Seconds 0) benchmarkExpensiveHash :: IO (Benchmark Cost) benchmarkExpensiveHash = do start <- getCurrentTime let ExpensiveHash expected b = expensiveHash SecureMode (Salt (KeyIdent gpgKey (Name ("benchmark" :: B.ByteString)))) (Password ("himom" :: B.ByteString)) end <- b `deepseq` getCurrentTime let actual = (CPUCost $ Seconds $ end `diffUTCTime` start) return $ Benchmark { expectedBenchmark = expected, actualBenchmark = actual }