diff options
-rw-r--r-- | Cost.hs | 9 | ||||
-rw-r--r-- | Encryption.hs | 202 | ||||
-rw-r--r-- | ExpensiveHash.hs | 46 | ||||
-rw-r--r-- | Shard.hs | 3 | ||||
-rw-r--r-- | Tunables.hs | 82 | ||||
-rw-r--r-- | Types/Cost.hs | 8 | ||||
-rw-r--r-- | keysafe.cabal | 5 | ||||
-rw-r--r-- | keysafe.hs | 6 |
8 files changed, 175 insertions, 186 deletions
@@ -15,24 +15,15 @@ import Types.Cost -- | Cost in seconds, with the type of hardware needed. totalCost :: Cost op -> (Seconds, [UsingHardware]) totalCost (CPUCost s) = (s, [UsingCPU]) -totalCost (GPUCost s) = (s, [UsingGPU]) -totalCost (CombinedCost a b) = - let (s1, h1) = totalCost a - (s2, h2) = totalCost b - in (s1+s2, h1++h2) raiseCostPower :: Cost c -> Entropy e -> Cost c raiseCostPower c (Entropy e) = adjustCost c (* 2^e) adjustCost :: Cost c -> (Seconds -> Seconds) -> Cost c adjustCost (CPUCost s) f = CPUCost (f s) -adjustCost (GPUCost s) f = GPUCost (f s) -adjustCost (CombinedCost a b) f = CombinedCost (adjustCost a f) (adjustCost b f) castCost :: Cost a -> Cost b castCost (CPUCost s) = CPUCost s -castCost (GPUCost s) = GPUCost s -castCost (CombinedCost a b) = CombinedCost (castCost a) (castCost b) -- | CostCalc for a brute force linear search through an entropy space -- in which each step entails paying a cost. diff --git a/Encryption.hs b/Encryption.hs index bf2370c..8d508d8 100644 --- a/Encryption.hs +++ b/Encryption.hs @@ -1,4 +1,5 @@ {-# LANGUAGE OverloadedStrings, MultiParamTypeClasses, DataKinds #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} {- Copyright 2016 Joey Hess <id@joeyh.name> - @@ -11,9 +12,9 @@ import Types import Tunables import Cost import ExpensiveHash -import Data.Bits import Data.Monoid import Data.Maybe +import Data.Word import qualified Raaz import qualified Raaz.Cipher.AES as Raaz import qualified Raaz.Cipher.Internal as Raaz @@ -24,49 +25,6 @@ import Text.Read type AesKey = Raaz.KEY256 --- | An AES key, which is used to encrypt the key that is stored --- in keysafe. -data KeyEncryptionKey = KeyEncryptionKey - { keyEncryptionKey :: AesKey - , keyEncryptionIV :: Raaz.IV - , keyDecryptionCost :: Cost DecryptionOp - , keyBruteForceCalc :: CostCalc BruteForceOp UnknownPassword - } - -instance Bruteforceable KeyEncryptionKey UnknownPassword where - getBruteCostCalc = keyBruteForceCalc - --- | The ExpensiveHash of the Password used as the AES key. --- Name is used as a salt, to prevent rainbow table attacks. -genKeyEncryptionKey :: Tunables -> Name -> Password -> IO KeyEncryptionKey -genKeyEncryptionKey tunables name (Password password) = case decryptionPuzzleTunable tunables of - KeyBlindingLeftSide puzzlecost -> do - prg <- Raaz.newPRG () :: IO Raaz.SystemPRG - ob <- genRandomObstacle tunables - randomkey <- Raaz.random prg :: IO AesKey - let k = hashToAESKey randomkey hash - let strongk = mixinRandomObstacle ob k - let decryptcost = CombinedCost puzzlecost (castCost hashcost) - iv <- genIV (Password password) - -- To brute force data encrypted with this key, - -- an attacker needs to pay the decryptcost for - -- each password checked. - let bruteforcecalc = bruteForceLinearSearch decryptcost - return $ KeyEncryptionKey strongk iv decryptcost bruteforcecalc - where - hash@(ExpensiveHash hashcost _) = expensiveHash tunables salt password - salt = Salt name - --- Use the sha256 of the password (truncated) as the IV. -genIV :: Password -> IO Raaz.IV -genIV (Password password) = do - prg <- Raaz.newPRG () :: IO Raaz.SystemPRG - exampleiv <- Raaz.random prg :: IO Raaz.IV - let ivlen = B.length (Raaz.toByteString exampleiv) - return $ fromMaybe (error "genIV fromByteString failed") $ - Raaz.fromByteString $ B.take ivlen $ - Raaz.toByteString $ Raaz.sha256 password - cipher :: Raaz.AES 256 'Raaz.CBC cipher = Raaz.aes256cbc @@ -85,21 +43,57 @@ decrypt kek (EncryptedSecretKey b _) = SecretKey <$> fromEncryptableBytes pbs where pbs = EncryptableBytes $ Raaz.unsafeDecrypt cipher (keyEncryptionKey kek, keyEncryptionIV kek) b --- | A stream of all the key encryption keys that need to be tried to --- decrypt. +-- | An AES key, which is used to encrypt the secret key that is stored +-- in keysafe. +data KeyEncryptionKey = KeyEncryptionKey + { keyEncryptionKey :: AesKey + , keyEncryptionIV :: Raaz.IV + , keyDecryptionCost :: Cost DecryptionOp + , keyBruteForceCalc :: CostCalc BruteForceOp UnknownPassword + } + +instance Bruteforceable KeyEncryptionKey UnknownPassword where + getBruteCostCalc = keyBruteForceCalc + +-- | The ExpensiveHash of the Password used as the KeyEncryptionKey +-- +-- Name is used as a salt, to prevent rainbow table attacks. -- --- The DecryptionPuzzleTunable is used to alter the AES key --- in some way; this stream includes every possible solution to the puzzle. -candidateKeyEncryptionKeys :: Tunables -> KeyEncryptionKey -> [KeyEncryptionKey] -candidateKeyEncryptionKeys tunables basekek = - case decryptionPuzzleTunable tunables of - KeyBlindingLeftSide _ -> - let k = keyEncryptionKey basekek - nrand = sizeRandomObstacle tunables - mkcandidate b = - let ob = mkRandomObstacle' k b - in basekek { keyEncryptionKey = mixinRandomObstacle ob k } - in map mkcandidate (allByteStringsOfLength nrand) +-- A random prefix is added to the salt, to force an attacker to +-- run the hash repeatedly. +genKeyEncryptionKey :: Tunables -> Name -> Password -> IO KeyEncryptionKey +genKeyEncryptionKey tunables name password = do + prg <- Raaz.newPRG () :: IO Raaz.SystemPRG + saltprefix <- genRandomSaltPrefix prg tunables + return $ head $ + genKeyEncryptionKeys [saltprefix] tunables name password + +-- | A stream of KeyEncryptionKeys, using the specified salt prefixes. +genKeyEncryptionKeys :: [SaltPrefix] -> Tunables -> Name -> Password -> [KeyEncryptionKey] +genKeyEncryptionKeys saltprefixes tunables (Name name) (Password password) = + map mk saltprefixes + where + iv = genIV (Password password) + -- To brute force data encrypted with a key, + -- an attacker needs to pay the decryptcost for + -- each password checked. + bruteforcecalc = bruteForceLinearSearch decryptcost + decryptcost = castCost $ randomSaltBytesBruteForceCost kektunables + kektunables = keyEncryptionKeyTunable tunables + + mk saltprefix = KeyEncryptionKey (hashToAESKey hash) iv decryptcost bruteforcecalc + where + salt = Salt (saltprefix <> name) + hash = expensiveHash (keyEncryptionKeyHash kektunables) salt password + +-- | A stream of all the key encryption keys that need to be tried to +-- decrypt. +candidateKeyEncryptionKeys :: Tunables -> Name -> Password -> [KeyEncryptionKey] +candidateKeyEncryptionKeys tunables name password = + genKeyEncryptionKeys saltprefixes tunables name password + where + saltprefixes = allByteStringsOfLength $ + randomSaltBytes $ keyEncryptionKeyTunable tunables allByteStringsOfLength :: Int -> [B.ByteString] allByteStringsOfLength = go [] @@ -110,84 +104,42 @@ allByteStringsOfLength = go [] w <- [0..255] go (w:ws) (n-1) +-- Use the sha256 of the password (truncated) as the IV. +genIV :: Password -> Raaz.IV +genIV (Password password) = + fromMaybe (error "genIV fromByteString failed") $ + Raaz.fromByteString $ B.take ivlen $ + Raaz.toByteString $ Raaz.sha256 password + where + ivlen = fromIntegral $ Raaz.byteSize (undefined :: Raaz.IV) + +type SaltPrefix = B.ByteString + +genRandomSaltPrefix :: Raaz.SystemPRG -> Tunables -> IO SaltPrefix +genRandomSaltPrefix prg tunables = go [] + (randomSaltBytes $ keyEncryptionKeyTunable tunables) + where + go ws 0 = return (B.pack ws) + go ws n = do + b <- Raaz.random prg :: IO Word8 + go (b:ws) (n-1) + +instance Raaz.Random Word8 + -- | Make an AES key out of a hash value. -- -- Since the ExpensiveHash value is ascii encoded, and has a common prefix, -- it does not have a high entropy in every byte, and its length is longer -- than the AES key length. To deal with this, use the SHA256 of -- the ExpensiveHash, as a bytestring. -hashToAESKey :: AesKey -> ExpensiveHash -> AesKey -hashToAESKey samplekey (ExpensiveHash _ t) = +hashToAESKey :: ExpensiveHash -> AesKey +hashToAESKey (ExpensiveHash _ t) = fromMaybe (error "hashToAESKey fromByteString failed") $ Raaz.fromByteString b where - b = B.take (B.length (Raaz.toByteString samplekey)) $ + b = B.take (fromIntegral $ Raaz.byteSize (undefined :: AesKey)) $ Raaz.toByteString $ Raaz.sha256 (E.encodeUtf8 t) --- | A random value which can be mixed into an AES key to --- require decrypting it to perform some brute-force work. --- --- The random value is left-padded with NULL bytes, so ORing it with an AES --- key varies the initial bytes of the key. --- --- The AesKey also includes a random IV. -data RandomObstacle = RandomObstacle AesKey - --- | Length of the random obstacle, in bytes, that will satisfy the --- decryptionPuzzleCost. --- --- AES can be calculated more efficiently by a GPU, so the cost must be --- a GPU cost. --- --- This depends on the objectSize, because to brute force the --- RandomObstable, AES decryptions must be done repeatedly, and the --- time needed for an AES decryption depends on the amount of data. -sizeRandomObstacle :: Tunables -> Int -sizeRandomObstacle tunables = ceiling $ nbits / 8 - where - -- in 2016, a GPU can run AES at 10 GB/s. - bytespersecond = 10*1024*1024*1024 - triespersecond = bytespersecond `div` fromIntegral (objectSize tunables) - targetseconds = case decryptionPuzzleTunable tunables of - KeyBlindingLeftSide cost -> case cost of - GPUCost (Seconds n) -> n - _ -> error "decryptionPuzzleCost must be a GPUCost" - -- Add one bit of entropy, because a brute-force attack will - -- on average succeed half-way through the search space. - nbits :: Double - nbits - | targetseconds < 1 = 0 - | otherwise = logBase 2 (fromIntegral $ targetseconds * triespersecond) + 1 - -mkRandomObstacle :: AesKey -> Int -> RandomObstacle -mkRandomObstacle k nbytes = mkRandomObstacle' k $ - B.take nbytes $ Raaz.toByteString k - -mkRandomObstacle' :: AesKey -> B.ByteString -> RandomObstacle -mkRandomObstacle' examplek b = RandomObstacle $ - fromMaybe (error "mkRandomObstacle' fromByteString failed") $ - Raaz.fromByteString (b <> padding) - where - klen = B.length (Raaz.toByteString examplek) - padding = B.replicate (klen - B.length b) 0 - -genRandomObstacle :: Tunables -> IO RandomObstacle -genRandomObstacle tunables = do - prg <- Raaz.newPRG () :: IO Raaz.SystemPRG - randomkey <- Raaz.random prg :: IO AesKey - let size = sizeRandomObstacle tunables - return $ mkRandomObstacle randomkey size - -mixinRandomObstacle :: RandomObstacle -> AesKey -> AesKey -mixinRandomObstacle (RandomObstacle r) k = k' - where - k' = fromMaybe (error "mixinRandomObstacle fromByteString failed") $ - Raaz.fromByteString $ - Raaz.toByteString r `orBytes` Raaz.toByteString k - -orBytes :: B.ByteString -> B.ByteString -> B.ByteString -orBytes a b = B.pack $ map (uncurry (.|.)) $ zip (B.unpack a) (B.unpack b) - -- | A bytestring that can be AES enctypted. It includes a checksum, -- and size, and is padded to the objectSize with NULs. -- diff --git a/ExpensiveHash.hs b/ExpensiveHash.hs index 226fac7..3d832fb 100644 --- a/ExpensiveHash.hs +++ b/ExpensiveHash.hs @@ -17,6 +17,7 @@ import qualified Crypto.Argon2 as Argon2 import Raaz.Core.Encode import Data.Time.Clock import Control.DeepSeq +import Control.Monad import Data.Monoid -- | A hash that is expensive to calculate. @@ -29,32 +30,45 @@ data ExpensiveHash = ExpensiveHash (Cost CreationOp) T.Text data Salt t = Salt t --- | Would rather use haskell argon2 library, but it doesn't build --- from source, and is buggy. https://github.com/ocharles/argon2/issues/3 -expensiveHash :: Encodable t => Tunables -> Salt t -> B.ByteString -> ExpensiveHash -expensiveHash tunables (Salt s) b = - case expensiveHashTunable tunables of - UseArgon2 opts cost -> ExpensiveHash cost $ - -- Using hashEncoded here and not hash, - -- because of this bug: - -- https://github.com/ocharles/argon2/issues/3 - Argon2.hashEncoded opts b argonsalt +expensiveHash :: Encodable t => ExpensiveHashTunable -> Salt t -> B.ByteString -> ExpensiveHash +expensiveHash (UseArgon2 cost opts) (Salt s) b = ExpensiveHash cost $ + -- Using hashEncoded here and not hash, + -- because of this bug: + -- https://github.com/ocharles/argon2/issues/3 + Argon2.hashEncoded opts b argonsalt where -- argon salt cannot be shorter than 8 bytes, so pad with spaces. argonsalt = let sb = toByteString s in sb <> B.replicate (8 - B.length sb ) 32 -benchmarkExpensiveHash :: Tunables -> IO (Benchmark (Cost CreationOp)) -benchmarkExpensiveHash tunables = do +benchmarkExpensiveHash :: Int -> ExpensiveHashTunable -> Cost op -> IO (Benchmark (Cost op)) +benchmarkExpensiveHash rounds tunables expected = do start <- getCurrentTime - let ExpensiveHash expected t = expensiveHash tunables - (Salt (KeyId gpgKey ("benchmark" :: B.ByteString))) - ("himom" :: B.ByteString) - end <- t `deepseq` getCurrentTime + forM_ [1..rounds] $ \_ -> do + let ExpensiveHash _ t = expensiveHash tunables + (Salt (KeyId gpgKey ("benchmark" :: B.ByteString))) + ("himom" :: B.ByteString) + t `deepseq` return () + end <- getCurrentTime let diff = floor $ end `diffUTCTime` start let actual = CPUCost $ Seconds diff return $ Benchmark { expectedBenchmark = expected , actualBenchmark = actual } + +benchmarkTunables :: Tunables -> IO () +benchmarkTunables tunables = do + putStrLn "Note that expected times are for 1 core machine." + putStrLn "If this machine has 2 cores, the actual times should be half the expected times." + putStrLn "Benchmarking 16 rounds of key generation hash..." + print =<< benchmarkExpensiveHash 16 + (keyEncryptionKeyHash $ keyEncryptionKeyTunable tunables) + (mapCost (`div` 16) $ randomSaltBytesBruteForceCost $ keyEncryptionKeyTunable tunables) + putStrLn "Benchmarking 1 round of name generation hash..." + print =<< benchmarkExpensiveHash 1 + (nameGenerationHash $ nameGenerationTunable tunables) + (getexpected $ nameGenerationHash $ nameGenerationTunable tunables) + where + getexpected (UseArgon2 cost _) = cost @@ -41,13 +41,14 @@ shardIdents tunables (Name name) keyid = ShardIdents idents creationcost bruteforcecalc where (ExpensiveHash creationcost basename) = - expensiveHash tunables (Salt keyid) name + expensiveHash hashtunables (Salt keyid) name mk n = StorableObjectIdent $ Raaz.toByteString $ mksha $ E.encodeUtf8 $ basename <> T.pack (show n) mksha :: B.ByteString -> Raaz.Base16 mksha = Raaz.encode . Raaz.sha256 idents = map mk [1..totalObjects (head (shardParams tunables))] bruteforcecalc = bruteForceLinearSearch creationcost + hashtunables = nameGenerationHash $ nameGenerationTunable tunables genShards :: EncryptedSecretKey -> Tunables -> IO [Shard] genShards (EncryptedSecretKey esk _) tunables = do 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 diff --git a/Types/Cost.hs b/Types/Cost.hs index a6a1160..290fa25 100644 --- a/Types/Cost.hs +++ b/Types/Cost.hs @@ -12,8 +12,6 @@ import Utility.HumanTime -- | An estimated cost to perform an operation. data Cost op = CPUCost Seconds -- ^ using 1 CPU core - | GPUCost Seconds - | CombinedCost (Cost op) (Cost op) deriving (Show) unknownCost :: Cost op @@ -32,9 +30,9 @@ instance Monoid (Cost t) where mempty = CPUCost (Seconds 0) CPUCost (Seconds a) `mappend` CPUCost (Seconds b) = CPUCost (Seconds (a+b)) - GPUCost (Seconds a) `mappend` GPUCost (Seconds b) = - GPUCost (Seconds (a+b)) - a `mappend` b = CombinedCost a b + +mapCost :: (Integer -> Integer) -> Cost op -> Cost op +mapCost f (CPUCost (Seconds n)) = CPUCost (Seconds (f n)) -- | Operations whose cost can be measured. data DecryptionOp diff --git a/keysafe.cabal b/keysafe.cabal index fc80725..3eee7c6 100644 --- a/keysafe.cabal +++ b/keysafe.cabal @@ -37,17 +37,22 @@ Executable keysafe , readline == 1.0.* , zxcvbn-c == 1.0.* + -- Inlined to change the finite field size to 256 + -- for efficient serialization. -- secret-sharing == 1.0.* , dice-entropy-conduit >= 1.0.0.0 , binary >=0.5.1.1 , vector >=0.10.11.0 , finite-field >=0.8.0 , polynomial >= 0.7.1 + -- Temporarily inlined due to https://github.com/ocharles/argon2/issues/3 -- argon2 == 1.1.* Extra-Libraries: argon2 Other-Modules: Crypto.Argon2.FFI Crypto.Argon2 + ExpensiveHash + Encryption source-repository head type: git @@ -12,6 +12,7 @@ import Tunables import qualified CmdLine import UI import Encryption +import Entropy import Cost import Shard import Storage @@ -45,7 +46,7 @@ storedemo ui keyid tunables = do kek <- genKeyEncryptionKey tunables name password putStrLn "Very rough estimate of cost to brute-force the password:" print $ estimateAttack spotAWS $ estimateBruteforceOf kek - (passwordEntropy password) + (passwordEntropy password []) let esk = encrypt tunables kek secretkey let sis = shardIdents tunables name keyid shards <- genShards esk tunables @@ -77,8 +78,7 @@ retrievedemo ui keyid = do <$> mapM (uncurry (retrieveShard localFiles)) l _ <- obscureShards localFiles let esk = combineShards tunables shards - basekek <- genKeyEncryptionKey tunables name password - go esk (candidateKeyEncryptionKeys tunables basekek) + go esk (candidateKeyEncryptionKeys tunables name password) where go _ [] = error "decryption failed" go esk (kek:rest) = case decrypt kek esk of |