diff options
author | Joey Hess <joeyh@joeyh.name> | 2016-08-11 22:13:05 -0400 |
---|---|---|
committer | Joey Hess <joeyh@joeyh.name> | 2016-08-11 22:13:05 -0400 |
commit | 7db612b232a0f8a98fa695da6f14853284def681 (patch) | |
tree | 4c7c7b641f313c0bcf0106e19b4c42c73f41356f | |
parent | 73d1b3d2dfb0f225132ab31a701e6378422e2a37 (diff) | |
download | keysafe-7db612b232a0f8a98fa695da6f14853284def681.tar.gz |
simplify by using the IV as the puzzle
-rw-r--r-- | Encryption.hs | 77 | ||||
-rw-r--r-- | Tunables.hs | 6 |
2 files changed, 35 insertions, 48 deletions
diff --git a/Encryption.hs b/Encryption.hs index 29935ac..c712d98 100644 --- a/Encryption.hs +++ b/Encryption.hs @@ -11,7 +11,6 @@ import Types import Tunables import Cost import ExpensiveHash -import Data.Bits import Data.Monoid import Data.Maybe import qualified Raaz @@ -56,16 +55,20 @@ decrypt kek (EncryptedSecretKey b _) = SecretKey <$> fromPaddedBytes pbs where pbs = PaddedBytes $ Raaz.unsafeDecrypt cipher (keyEncryptionKey kek) b --- | The ExpensiveHash of the Password is combined with a --- RandomObstacle to form the AES key. Combination method is logical OR. --- +-- | The ExpensiveHash of the Password used as the AES key. -- Name is used as a salt, to prevent rainbow table attacks. +-- +-- The DecryptionPuzzleTunable is used to alter the AES key +-- in some way that requires significant work to determine at +-- decryption time. genKeyEncryptionKey :: Tunables -> Name -> Password -> IO KeyEncryptionKey genKeyEncryptionKey tunables name (Password password) = case decryptionPuzzleTunable tunables of - KeyBlindingLeftSide puzzlecost -> do - ob@(RandomObstacle ok) <- genRandomObstacle tunables - let k = hashToAESKey ok hash - let strongk = mixinRandomObstacle ob k + UsePuzzleIV puzzlecost -> do + prg <- Raaz.newPRG () :: IO Raaz.SystemPRG + samplekey <- Raaz.random prg :: IO AesKey + let basek = hashToAESKey samplekey hash + (PuzzleIV iv) <- genPuzzleIV tunables + let strongk = (basek, iv) let decryptcost = CombinedCost puzzlecost (castCost hashcost) -- To brute force data encrypted with this key, -- an attacker needs to pay the decryptcost for @@ -90,64 +93,48 @@ hashToAESKey (samplekey, _iv) (ExpensiveHash _ t) = b = B.take (B.length (Raaz.toByteString samplekey)) $ 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 +-- | A puzzle IV starts with some number of random bytes of IV data, +-- and is right-padded with NULL bytes. +data PuzzleIV = PuzzleIV Raaz.IV --- | Length of the random obstacle, in bytes, that will satisfy the +-- | Length of the puzzle IV, 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 +-- RandomIV, 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 +sizePuzzleIV :: Tunables -> Int +sizePuzzleIV 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 + UsePuzzleIV cost -> case cost of GPUCost (Seconds n) -> n - _ -> error "decryptionPuzzleCost must be a GPUCost" + _ -> error "decryptionPuzzleTunable 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 = logBase 2 (fromIntegral $ targetseconds * triespersecond) + 1 -mkRandomObstacle :: AesKey -> Int -> AesKey -mkRandomObstacle (k, iv) nbytes = (k', iv) +mkPuzzleIV :: Raaz.IV -> Int -> PuzzleIV +mkPuzzleIV randomiv nbytes = PuzzleIV $ + fromMaybe (error "mkRandomIV fromByteString failed") $ + Raaz.fromByteString $ B.take nbytes b <> padding where - k' = fromMaybe (error "mkRandomObstacle fromByteString failed") $ - Raaz.fromByteString ob - kb = Raaz.toByteString k - padding = B.replicate (B.length kb - nbytes) 0 - ob = padding <> B.take nbytes kb - -genRandomObstacle :: Tunables -> IO RandomObstacle -genRandomObstacle tunables = do - prg <- Raaz.newPRG () :: IO Raaz.SystemPRG - randomkey <- Raaz.random prg :: IO AesKey - let size = sizeRandomObstacle tunables - return $ RandomObstacle $ mkRandomObstacle randomkey size - -mixinRandomObstacle :: RandomObstacle -> Raaz.KEY256 -> AesKey -mixinRandomObstacle (RandomObstacle (r, iv)) k = (k', iv) - where - k' = fromMaybe (error "mixinRandomObstacle fromByteString failed") $ - Raaz.fromByteString $ - Raaz.toByteString r `orBytes` Raaz.toByteString k + b = Raaz.toByteString randomiv + padding = B.replicate (B.length b - nbytes) 0 -orBytes :: B.ByteString -> B.ByteString -> B.ByteString -orBytes a b = B.pack $ map (uncurry (.|.)) $ zip (B.unpack a) (B.unpack b) +genPuzzleIV :: Tunables -> IO PuzzleIV +genPuzzleIV tunables = do + prg <- Raaz.newPRG () :: IO Raaz.SystemPRG + randomiv <- Raaz.random prg :: IO Raaz.IV + let size = sizePuzzleIV tunables + return $ mkPuzzleIV randomiv size newtype PaddedBytes = PaddedBytes { getPaddedBytes :: B.ByteString } deriving (Show) diff --git a/Tunables.hs b/Tunables.hs index 763f0bf..a2c5f52 100644 --- a/Tunables.hs +++ b/Tunables.hs @@ -59,7 +59,7 @@ data EncryptionTunable = UseAES256 deriving (Show) -- | An additional puzzle that makes decryption more expensive. -data DecryptionPuzzleTunable = KeyBlindingLeftSide (Cost DecryptionOp) +data DecryptionPuzzleTunable = UsePuzzleIV (Cost DecryptionOp) deriving (Show) defaultTunables :: Tunables @@ -73,7 +73,7 @@ defaultTunables = Tunables -- 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)) + , decryptionPuzzleTunable = UsePuzzleIV (GPUCost (Seconds 60)) } where argonoptions = Argon2.HashOptions @@ -95,7 +95,7 @@ testModeTunables = Tunables , objectSize = 1024*64 , expensiveHashTunable = UseArgon2 weakargonoptions argoncost , encryptionTunable = UseAES256 - , decryptionPuzzleTunable = KeyBlindingLeftSide (GPUCost (Seconds 60)) + , decryptionPuzzleTunable = UsePuzzleIV (GPUCost (Seconds 1)) } where UseArgon2 argonoptions argoncost = expensiveHashTunable defaultTunables |