{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-} {- Copyright 2016 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} 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 -- (must be a multiple of AES block size 16) , 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 -- Setting this to eg, Seconds 60 only makes each password -- guess 60 seconds longer on a GPU. But, on a CPU, keysafe -- has to work for quite a long time to solve such a puzzle. -- So, currently disabling the puzzle with Seconds 0. , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 0)) } 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 0)) } where UseArgon2 argonoptions argoncost = expensiveHashTunable defaultTunables weakargonoptions = argonoptions { Argon2.hashIterations = 1 }