summaryrefslogtreecommitdiffhomepage
path: root/Tunables.hs
diff options
context:
space:
mode:
authorJoey Hess <joeyh@joeyh.name>2016-08-16 12:57:19 -0400
committerJoey Hess <joeyh@joeyh.name>2016-08-16 13:04:07 -0400
commit3b4a775d536b2b2956269a59f886487efe29ed51 (patch)
treebbd8fd580afad11f822b2929061b034abf376c50 /Tunables.hs
parentd7696832e183cc3e98d094b35ee4392d0c8d3df5 (diff)
downloadkeysafe-3b4a775d536b2b2956269a59f886487efe29ed51.tar.gz
switch to random salt byte to make decryption expensive
Diffstat (limited to 'Tunables.hs')
-rw-r--r--Tunables.hs82
1 files changed, 55 insertions, 27 deletions
diff --git a/Tunables.hs b/Tunables.hs
index 0053668..49e6cd4 100644
--- a/Tunables.hs
+++ b/Tunables.hs
@@ -22,7 +22,7 @@ import qualified Crypto.Argon2 as Argon2
-- is that it prevents attacks where related objects are correlated based
-- on their tunables.
knownTunings :: [(ExpensiveHashTunable, Tunables)]
-knownTunings = map (\t -> (expensiveHashTunable t, t))
+knownTunings = map (\t -> (nameGenerationHash (nameGenerationTunable t), t))
[ defaultTunables
]
@@ -39,10 +39,11 @@ data Tunables = Tunables
, objectSize :: Int
-- ^ a StorableObject is exactly this many bytes in size
-- (must be a multiple of AES block size 16)
- , expensiveHashTunable :: ExpensiveHashTunable
+ , nameGenerationTunable :: NameGenerationTunable
+ , keyEncryptionKeyTunable :: KeyEncryptionKeyTunable
, encryptionTunable :: EncryptionTunable
- , decryptionPuzzleTunable :: DecryptionPuzzleTunable
}
+ deriving (Show)
-- | Parameters for sharding. The secret is split into
-- N objects, such that only M are needed to reconstruct it.
@@ -50,53 +51,80 @@ data ShardParams = ShardParams
{ totalObjects :: Int -- ^ N
, neededObjects :: Int -- ^ M
}
+ deriving (Show)
--- | An expensive hash, used to make it hard to crack an encrypted secret key.
-data ExpensiveHashTunable = UseArgon2 Argon2.HashOptions (Cost CreationOp)
+-- | An expensive hash, which makes brute-forcing hard.
+--
+-- The creation cost estimate must be manually tuned to match the
+-- hash options. Use benchmarkTunables to check this.
+data ExpensiveHashTunable = UseArgon2 (Cost CreationOp) Argon2.HashOptions
deriving (Show)
--- | What encryption to use.
-data EncryptionTunable = UseAES256
+data NameGenerationTunable = NameGenerationTunable
+ { nameGenerationHash :: ExpensiveHashTunable
+ }
deriving (Show)
--- | An additional puzzle that makes decryption more expensive.
-data DecryptionPuzzleTunable = KeyBlindingLeftSide (Cost DecryptionOp)
+-- | How to generate the encryption key used to encrypt the secret key.
+-- This is an expensive hash of the password, but not a super expensive
+-- hash, because a password brute forcing attacker needs to run the hash
+-- 256 times per random salt byte.
+data KeyEncryptionKeyTunable = KeyEncryptionKeyTunable
+ { keyEncryptionKeyHash :: ExpensiveHashTunable
+ , randomSaltBytes :: Int
+ , randomSaltBytesBruteForceCost :: Cost BruteForceOp
+ }
+ deriving (Show)
+
+-- | What encryption to use.
+data EncryptionTunable = UseAES256
deriving (Show)
defaultTunables :: Tunables
defaultTunables = Tunables
{ shardParams = [ShardParams { totalObjects = 3, neededObjects = 2 }]
, objectSize = 1024*64 -- 64 kb
- , expensiveHashTunable = UseArgon2 argonoptions argoncost
+ -- The nameGenerationHash was 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.
+ , nameGenerationTunable = NameGenerationTunable
+ { nameGenerationHash = argon2 10000 (CPUCost (Seconds (2*600)))
+ }
+ , keyEncryptionKeyTunable = KeyEncryptionKeyTunable
+ { keyEncryptionKeyHash = argon2 100 (CPUCost (Seconds 0))
+ , randomSaltBytes = 1
+ -- The keyEncryptionKeyHash is run 256 times per
+ -- random salt byte to brute-force, and its parameters
+ -- were chosen so the total brute forcing time is 50 minutes,
+ -- on a 2 core Intel(R) Core(TM) i5-4210Y CPU @ 1.50GHz.
+ -- Since cost is measured per core, we double that.
+ , randomSaltBytesBruteForceCost = CPUCost (Seconds (2*50*60))
+ }
, 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 i c = UseArgon2 c $ Argon2.HashOptions
+ { Argon2.hashIterations = i
, 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.
+-- | Dials back hash difficulty, lies about costs.
+-- Not for production use!
testModeTunables :: Tunables
testModeTunables = Tunables
{ shardParams = [ShardParams { totalObjects = 3, neededObjects = 2 }]
, objectSize = 1024*64
- , expensiveHashTunable = UseArgon2 weakargonoptions argoncost
+ , nameGenerationTunable = NameGenerationTunable
+ { nameGenerationHash = weakargon2 (CPUCost (Seconds (2*600)))
+ }
+ , keyEncryptionKeyTunable = KeyEncryptionKeyTunable
+ { keyEncryptionKeyHash = weakargon2 (CPUCost (Seconds 0))
+ , randomSaltBytes = 1
+ , randomSaltBytesBruteForceCost = CPUCost (Seconds (2*50*60))
+ }
, encryptionTunable = UseAES256
- , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 0))
}
where
- UseArgon2 argonoptions argoncost = expensiveHashTunable defaultTunables
- weakargonoptions = argonoptions { Argon2.hashIterations = 1 }
+ weakargon2 c = UseArgon2 c Argon2.defaultHashOptions