summaryrefslogtreecommitdiffhomepage
path: root/ExpensiveHash.hs
diff options
context:
space:
mode:
authorJoey Hess <joeyh@joeyh.name>2016-08-06 17:35:10 -0400
committerJoey Hess <joeyh@joeyh.name>2016-08-06 17:35:10 -0400
commit7192abc5d53aa5a6ee609ed30bd05f1575e67b65 (patch)
tree2f1d17f27b483a8deec001a12a55696b0eea5978 /ExpensiveHash.hs
parentfbd0bb3a2b2541e897708fb441ab1c8a2b5ab78e (diff)
downloadkeysafe-7192abc5d53aa5a6ee609ed30bd05f1575e67b65.tar.gz
some basic data types and expensive hashing
Diffstat (limited to 'ExpensiveHash.hs')
-rw-r--r--ExpensiveHash.hs54
1 files changed, 54 insertions, 0 deletions
diff --git a/ExpensiveHash.hs b/ExpensiveHash.hs
new file mode 100644
index 0000000..8bfe004
--- /dev/null
+++ b/ExpensiveHash.hs
@@ -0,0 +1,54 @@
+{-# 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 }