{-# LANGUAGE OverloadedStrings, GeneralizedNewtypeDeriving, MultiParamTypeClasses, FlexibleInstances #-} module Types where import Types.Cost import Entropy import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Crypto.Argon2 as Argon2 import Data.String -- | keysafe stores secret keys. newtype SecretKey = SecretKey B.ByteString -- | Objects stored on a keysafe server are (probably) a shard of an -- encrypted secret key. newtype StorableObject = StorableObject { fromStorableObject :: BL.ByteString } -- | 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 } -- | 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. Carefully chosen parts of this are exposed at various points -- in the data stored for a key, to allow future versions of keysafe to -- make the right decisions when processing it. data Tunables = Tunables { shardParams :: ShardParams , objectSize :: Int -- ^ a StorableObject is exactly this many bytes in size , expensiveHashTunable :: ExpensiveHashTunable , encryptionTunable :: EncryptionTunable , decryptionPuzzleTunable :: DecryptionPuzzleTunable } -- | An expensive hash, used to make it hard to crack an encrypted secret key. data ExpensiveHashTunable = UseArgon2 Argon2.HashOptions (Cost CreationOp) -- | What encryption to use. data EncryptionTunable = UseAES256 -- | An additional puzzle that makes decryption more expensive. data DecryptionPuzzleTunable = KeyBlindingLeftSide (Cost DecryptionOp) 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 Argon2.defaultHashOptions (CPUCost (Seconds (2*600))) , encryptionTunable = UseAES256 , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 60)) } -- | The secret key, encrypted with a password. data EncryptedSecretKey = EncryptedSecretKey B.ByteString (CostCalc BruteForceOp UnknownPassword) instance Bruteforceable EncryptedSecretKey UnknownPassword where getBruteCostCalc (EncryptedSecretKey _ cc) = cc -- | A password used to encrypt a key stored in keysafe. newtype Password = Password B.ByteString deriving (IsString) -- | Naive calculation of the entropy of a password. -- Does not take common passwords and password generation patterns into -- account, so this is an overestimation of how hard a password -- is to crack. passwordEntropy :: Password -> Entropy UnknownPassword passwordEntropy (Password p) = Entropy $ floor $ totalEntropy p -- | A name associated with a key stored in keysafe. newtype Name = Name B.ByteString deriving (Show) -- | The type of the key that is stored in keysafe. newtype KeyType = KeyType B.ByteString deriving (Show) gpgKey :: KeyType gpgKey = KeyType "gpg" -- | Enough information to uniquely identify a key stored in keysafe. data KeyIdent = KeyIdent KeyType Name deriving (Show) newtype ShardNum = ShardNum Int deriving (Show) -- | Enough information to uniquely identify an object stored on a keysafe -- server for a key. data ObjectIdent = ObjectIdent ShardNum KeyIdent deriving (Show) data Benchmark t = Benchmark { expectedBenchmark :: t, actualBenchmark :: t } deriving (Show)