diff options
author | Joey Hess <joeyh@joeyh.name> | 2016-09-12 22:35:47 -0400 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2016-09-12 22:39:21 -0400 |
commit | 13c408d2295597540f0b2dfb6f7b86e739876c90 (patch) | |
tree | cac72a6d5a75fb15d71d5e86395543829fe2f2df /Benchmark.hs | |
parent | 483cc9e1fe40899c7f045d71d75aaa5ca99db3fb (diff) | |
download | keysafe-13c408d2295597540f0b2dfb6f7b86e739876c90.tar.gz |
implement client-server Proof Of Work
Mashed up a argon2-based PoW with token buckets and bloom filters.
This is intended to prevent a few abuses including:
* Using a keysafe server for general file storage, by storing a whole
lot of chunks.
* An attacker guessing names that people will use, and uploading junk
to keysafe servers under those names, to make it harder for others to use
keysafe later.
* An attacker trying to guess the names used for objects on keysafe
servers in order to download them and start password cracking.
(As a second level of defense, since the name generation hash
is expensive already.)
Completely untested, but it builds!
This commit was sponsored by Andreas on Patreon.
Diffstat (limited to 'Benchmark.hs')
-rw-r--r-- | Benchmark.hs | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/Benchmark.hs b/Benchmark.hs new file mode 100644 index 0000000..21b7ce3 --- /dev/null +++ b/Benchmark.hs @@ -0,0 +1,82 @@ +{-# LANGUAGE OverloadedStrings #-} + +{- Copyright 2016 Joey Hess <id@joeyh.name> + - + - Licensed under the GNU AGPL version 3 or higher. + -} + +module Benchmark where + +import Types +import Tunables +import ExpensiveHash +import Cost +import Serialization () +import qualified Data.ByteString.UTF8 as BU8 +import qualified Crypto.Argon2 as Argon2 +import Data.Time.Clock +import Control.DeepSeq +import Control.Monad +import Data.Monoid +import Data.Maybe + +benchmarkExpensiveHash :: Int -> ExpensiveHashTunable -> IO (BenchmarkResult (Cost CreationOp)) +benchmarkExpensiveHash rounds tunables = + benchmarkExpensiveHash' rounds tunables (getexpected tunables) + where + getexpected (UseArgon2 cost _) = mapCost (* fromIntegral rounds) cost + +benchmarkExpensiveHash' :: Int -> ExpensiveHashTunable -> Cost op -> IO (BenchmarkResult (Cost op)) +benchmarkExpensiveHash' rounds tunables@(UseArgon2 _ hashopts) expected = do + numcores <- fromIntegral . fromMaybe (error "Unknown number of physical cores.") + <$> getNumCores + start <- getCurrentTime + forM_ [1..rounds] $ \n -> do + -- Must vary the data being hashed to avoid laziness + -- caching hash results. + let base = BU8.fromString (show n) + let ExpensiveHash _ t = expensiveHash tunables + (Salt (GpgKey (KeyId (base <> "dummy")))) + (base <> "himom") + t `deepseq` return () + end <- getCurrentTime + let diff = floor $ end `diffUTCTime` start + let maxthreads = Argon2.hashParallelism hashopts + let actual = CPUCost (Seconds diff) (Divisibility $ fromIntegral maxthreads) + -- The expected cost is for a single core, so adjust it + -- based on the number of cores, up to a maximum of the number + -- of threads that the hash is configred to use. + let usedcores = min maxthreads numcores + let adjustedexpected = mapCost (`div` fromIntegral usedcores) expected + return $ BenchmarkResult + { expectedBenchmark = adjustedexpected + , actualBenchmark = actual + } + +benchmarkTunables :: Tunables -> IO () +benchmarkTunables tunables = do + putStrLn "/proc/cpuinfo:" + putStrLn =<< readFile "/proc/cpuinfo" + + putStrLn $ "Benchmarking 16 rounds of proof of work hash..." + print =<< benchmarkExpensiveHash 16 (proofOfWorkHashTunable 0) + + -- Rather than run all 256 rounds of this hash, which would + -- probably take on the order of 1 hour, run only 16, and scale + -- the expected cost accordingly. + let normalrounds = fromIntegral $ + 256 * randomSaltBytes (keyEncryptionKeyTunable tunables) + putStrLn $ "Benchmarking 16/" ++ show normalrounds ++ " rounds of key encryption key hash..." + r <- benchmarkExpensiveHash' 16 + (keyEncryptionKeyHash $ keyEncryptionKeyTunable tunables) + (mapCost (`div` (normalrounds `div` 16)) $ randomSaltBytesBruteForceCost $ keyEncryptionKeyTunable tunables) + print r + putStrLn $ "Estimated time for " ++ show normalrounds ++ " rounds of key encryption key hash..." + print $ BenchmarkResult + { expectedBenchmark = mapCost (* 16) (expectedBenchmark r) + , actualBenchmark = mapCost (* 16) (actualBenchmark r) + } + + putStrLn "Benchmarking 1 round of name generation hash..." + print =<< benchmarkExpensiveHash 1 + (nameGenerationHash $ nameGenerationTunable tunables) |