summaryrefslogtreecommitdiffhomepage
path: root/Tunables.hs
diff options
context:
space:
mode:
authorJoey Hess <joeyh@joeyh.name>2016-08-11 15:52:50 -0400
committerJoey Hess <joeyh@joeyh.name>2016-08-11 15:52:50 -0400
commit5decbad3eb779b1bbe11245cbde84701909e9c68 (patch)
tree79e28c04d76ee8e225ee344b9d8c07a922728002 /Tunables.hs
parent90b7c385f4e2f293502f9aca38aaa041b7b2f486 (diff)
downloadkeysafe-5decbad3eb779b1bbe11245cbde84701909e9c68.tar.gz
nearly able to generate shards now
Diffstat (limited to 'Tunables.hs')
-rw-r--r--Tunables.hs97
1 files changed, 97 insertions, 0 deletions
diff --git a/Tunables.hs b/Tunables.hs
new file mode 100644
index 0000000..7a646d3
--- /dev/null
+++ b/Tunables.hs
@@ -0,0 +1,97 @@
+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}
+
+module Tunables where
+
+import Cost
+import qualified Crypto.Argon2 as Argon2
+
+-- | To determine the tunables used for a key name the expensive hash of the
+-- name is calculated, using a particular configuration, and if the
+-- object names it generates are available, we know the tunables.
+--
+-- Since this process is expensive, it's important that the most commonly
+-- used tunables come first, so that the expensive hash does not have to be
+-- calculated repatedly.
+--
+-- The reason for using this expensive method of encoding the tunables
+-- is that it prevents attacks where related objects are correlated based
+-- on their tunables.
+knownTunings :: [(ExpensiveHashTunable, Tunables)]
+knownTunings = map (\t -> (expensiveHashTunable t, t))
+ [ defaultTunables
+ ]
+
+-- | keysafe stores data for a long time, and needs to be able to process
+-- data from a long time ago when restoring a key. We don't want to be
+-- locked into old choices of crypto primitives etc forever.
+--
+-- So, every parameter that can be tuned is configured in this data
+-- structure.
+data Tunables = Tunables
+ { shardParams :: [ShardParams]
+ -- ^ multiple ShardParams may be supported, with the user
+ -- allowed to choose between them
+ , objectSize :: Int
+ -- ^ a StorableObject is exactly this many bytes in size
+ , expensiveHashTunable :: ExpensiveHashTunable
+ , encryptionTunable :: EncryptionTunable
+ , decryptionPuzzleTunable :: DecryptionPuzzleTunable
+ }
+
+-- | Parameters for sharding. The secret is split into
+-- N objects, such that only M are needed to reconstruct it.
+data ShardParams = ShardParams
+ { totalObjects :: Int -- ^ N
+ , neededObjects :: Int -- ^ M
+ }
+
+-- | An expensive hash, used to make it hard to crack an encrypted secret key.
+data ExpensiveHashTunable = UseArgon2 Argon2.HashOptions (Cost CreationOp)
+ deriving (Show)
+
+-- | What encryption to use.
+data EncryptionTunable = UseAES256
+ deriving (Show)
+
+-- | An additional puzzle that makes decryption more expensive.
+data DecryptionPuzzleTunable = KeyBlindingLeftSide (Cost DecryptionOp)
+ deriving (Show)
+
+defaultTunables :: Tunables
+defaultTunables = Tunables
+ { shardParams = [ShardParams { totalObjects = 3, neededObjects = 2 }]
+ , objectSize = 1024*64 -- 64 kb
+ , expensiveHashTunable = UseArgon2 argonoptions argoncost
+ , encryptionTunable = UseAES256
+ -- AES can be calculated more efficiently by a GPU, so the
+ -- cost is a GPU cost.
+ -- This is set to only 1 minute because GPUs are quite a lot
+ -- faster than CPUs at AES, and so setting it higher would make
+ -- clients too slow at key recovery.
+ , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 60))
+ }
+ where
+ argonoptions = Argon2.HashOptions
+ { Argon2.hashIterations = 10000
+ , Argon2.hashMemory = 131072 -- 128 mebibtyes per thread
+ , Argon2.hashParallelism = 4 -- 4 threads
+ , Argon2.hashVariant = Argon2.Argon2i
+ }
+ -- argon2 is GPU and ASIC resistent, so it uses CPU time.
+ -- The above HashOptions were benchmarked at 661 seconds CPU time
+ -- on a 2 core Intel(R) Core(TM) i5-4210Y CPU @ 1.50GHz.
+ -- Since cost is measured per core, we double that.
+ argoncost = CPUCost (Seconds (2*600))
+
+-- | Dials back cryptographic difficulty, not for production use.
+testModeTunables :: Tunables
+testModeTunables = Tunables
+ { shardParams = [ShardParams { totalObjects = 3, neededObjects = 2 }]
+ , objectSize = 1024*64
+ , expensiveHashTunable = UseArgon2 weakargonoptions argoncost
+ , encryptionTunable = UseAES256
+ , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 60))
+ }
+ where
+ UseArgon2 argonoptions argoncost = expensiveHashTunable defaultTunables
+ weakargonoptions = argonoptions { Argon2.hashIterations = 1 }