{-# LANGUAGE OverloadedStrings #-} {- Copyright 2016 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} module ExpensiveHash where import Types import Tunables import Cost import Serialization () import qualified Data.Text as T import qualified Data.ByteString as B import qualified Crypto.Argon2 as Argon2 import Raaz.Core.Encode import Data.Time.Clock import Control.DeepSeq import Data.Monoid -- | 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 CreationOp) T.Text deriving (Show) data Salt t = Salt t -- | Would rather use haskell argon2 library, but it doesn't build -- from source, and is buggy. https://github.com/ocharles/argon2/issues/3 expensiveHash :: Encodable t => Tunables -> Salt t -> B.ByteString -> ExpensiveHash expensiveHash tunables (Salt s) b = case expensiveHashTunable tunables of UseArgon2 opts cost -> ExpensiveHash cost $ -- Using hashEncoded here and not hash, -- because of this bug: -- https://github.com/ocharles/argon2/issues/3 Argon2.hashEncoded opts b argonsalt where -- argon salt cannot be shorter than 8 bytes, so pad with spaces. argonsalt = let sb = toByteString s in sb <> B.replicate (8 - B.length sb ) 32 benchmarkExpensiveHash :: Tunables -> IO (Benchmark (Cost CreationOp)) benchmarkExpensiveHash tunables = do start <- getCurrentTime let ExpensiveHash expected t = expensiveHash tunables (Salt (KeyId gpgKey ("benchmark" :: B.ByteString))) ("himom" :: B.ByteString) end <- t `deepseq` getCurrentTime let diff = floor $ end `diffUTCTime` start let actual = CPUCost $ Seconds diff return $ Benchmark { expectedBenchmark = expected , actualBenchmark = actual }