keysafe 0.20170122 → 0.20170303
raw patch · 9 files changed
+70/−40 lines, 9 filesdep ~raaz
Dependency ranges changed: raaz
Files
- ByteStrings.hs +4/−9
- CHANGELOG +6/−0
- Encryption.hs +8/−10
- HTTP/ProofOfWork.hs +12/−5
- HTTP/Server.hs +0/−1
- Share.hs +2/−1
- Storage.hs +5/−7
- TODO +31/−5
- keysafe.cabal +2/−2
ByteStrings.hs view
@@ -1,5 +1,3 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}- {- Copyright 2016 Joey Hess <id@joeyh.name> - - Licensed under the GNU AGPL version 3 or higher.@@ -9,8 +7,6 @@ import qualified Data.ByteString as B import qualified Raaz-import Control.Monad-import Data.Word allByteStringsOfLength :: Int -> [B.ByteString] allByteStringsOfLength = go []@@ -34,9 +30,8 @@ let (h, t) = B.splitAt n b in go (h:cs) t -instance Raaz.Random Word8--randomByteStringOfLength :: Int -> Raaz.SystemPRG -> IO B.ByteString-randomByteStringOfLength n prg = B.pack <$> replicateM n randbyte+randomByteStringOfLength :: Int -> IO B.ByteString+randomByteStringOfLength n = Raaz.securely gen where- randbyte = Raaz.random prg :: IO Word8+ gen :: Raaz.RandM B.ByteString+ gen = Raaz.randomByteString (Raaz.BYTES n)
CHANGELOG view
@@ -1,3 +1,9 @@+keysafe (0.20170303) unstable; urgency=medium++ * Updated to use raaz-0.1.1.++ -- Joey Hess <id@joeyh.name> Fri, 03 Mar 2017 16:15:47 -0400+ keysafe (0.20170122) unstable; urgency=medium * Adjust cabal bounds to allow building with ghc 8.0.
Encryption.hs view
@@ -32,10 +32,9 @@ encrypt tunables kek (SecretKey secret) = EncryptedSecretKey (chunkByteString (objectSize tunables) b) (keyBruteForceCalc kek) where- -- Raaz does not seem to provide a high-level interface- -- for AES encryption, so use unsafeEncrypt. The use of - -- EncryptableBytes makes sure it's provided with a - -- multiple of the AES block size.+ -- Raaz does not provide a high-level interface for AES encryption,+ -- so we use unsafeEncrypt. The use of EncryptableBytes makes+ -- sure it's provided with a multiple of the AES block size. b = Raaz.unsafeEncrypt cipher (keyEncryptionKey kek, keyEncryptionIV kek) $ getEncryptableBytes $ encodeEncryptableBytes tunables secret @@ -104,8 +103,7 @@ -- 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+ saltprefix <- genRandomSaltPrefix tunables return $ head $ genKeyEncryptionKeys [saltprefix] tunables name password @@ -144,12 +142,12 @@ Raaz.fromByteString $ B.take ivlen $ Raaz.toByteString $ Raaz.sha256 name where- ivlen = fromIntegral $ Raaz.byteSize (undefined :: Raaz.IV)+ ivlen = fromIntegral $ Raaz.sizeOf (undefined :: Raaz.IV) type SaltPrefix = B.ByteString -genRandomSaltPrefix :: Raaz.SystemPRG -> Tunables -> IO SaltPrefix-genRandomSaltPrefix prg tunables = randomByteStringOfLength n prg+genRandomSaltPrefix :: Tunables -> IO SaltPrefix+genRandomSaltPrefix tunables = randomByteStringOfLength n where n = randomSaltBytes $ keyEncryptionKeyTunable tunables @@ -164,7 +162,7 @@ fromMaybe (error "hashToAESKey fromByteString failed") $ Raaz.fromByteString b where- b = B.take (fromIntegral $ Raaz.byteSize (undefined :: AesKey)) $+ b = B.take (fromIntegral $ Raaz.sizeOf (undefined :: AesKey)) $ Raaz.toByteString $ Raaz.sha256 (E.encodeUtf8 t) -- | A bytestring that can be AES encrypted.
HTTP/ProofOfWork.hs view
@@ -95,10 +95,13 @@ newtype RequestIDSecret = RequestIDSecret (Raaz.Key (Raaz.HMAC Raaz.SHA256)) +-- | Random data is generated insecurely, eg not locked in memory because+-- this is a transient secret. newRequestIDSecret :: IO RequestIDSecret-newRequestIDSecret = do- prg <- Raaz.newPRG () :: IO Raaz.SystemPRG- RequestIDSecret <$> Raaz.random prg+newRequestIDSecret = RequestIDSecret <$> Raaz.insecurely gen+ where+ gen :: Raaz.RandM (Raaz.Key (Raaz.HMAC Raaz.SHA256))+ gen = Raaz.random mkRequestID :: RequestIDSecret -> IO RequestID mkRequestID secret = mkRequeestID' secret <$> mkRandomSalt@@ -113,11 +116,15 @@ let rid' = mkRequeestID' secret (randomSalt rid) in requestHMAC rid == requestHMAC rid' +-- | Random data is generated insecurely, eg not locked in memory because+-- this is a transient secret. mkRandomSalt :: IO RandomSalt mkRandomSalt = do- prg <- Raaz.newPRG () :: IO Raaz.SystemPRG- rs <- replicateM 16 (Raaz.random prg :: IO Word8)+ rs <- Raaz.insecurely $ replicateM 16 gen return $ RandomSalt $ T.pack $ concatMap show rs+ where+ gen :: Raaz.RandM Word8+ gen = Raaz.random class POWIdent p where getPOWIdent :: p -> B.ByteString
HTTP/Server.hs view
@@ -18,7 +18,6 @@ import Storage.Local import Serialization () import Servant-import Network.Wai import Network.Wai.Handler.Warp import Control.Monad.IO.Class import Control.Concurrent
@@ -94,7 +94,8 @@ combineShares :: Tunables -> [S.Set Share] -> Either String EncryptedSecretKey combineShares tunables shares | null shares || any null shares || any (\l -> length l < sharesneeded) shares = - Left "Not enough shares are currently available to reconstruct your data."+ Left $ "Not enough shares are currently available to reconstruct your data. " +++ concatMap (\l -> "(Got " ++ show (length l) ++ "/" ++ show sharesneeded ++ ") ") shares | otherwise = Right $ mk $ map (BL.toStrict . SS.decode . map decodeshare . S.toList) shares where
Storage.hs view
@@ -26,7 +26,6 @@ import qualified Data.Set as S import System.Random import System.Random.Shuffle-import qualified Raaz networkStorageLocations :: Maybe LocalStorageDirectory -> StorageLocations networkStorageLocations = StorageLocations . serverList@@ -171,25 +170,24 @@ storeChaff hn port delayseconds = forever $ do say $ "Sending chaff to " ++ hn ++ " (press ctrl-c to stop)" say "Legend: + = successful upload, ! = upload failure"- prg <- Raaz.newPRG () :: IO Raaz.SystemPRG- randomname <- randomByteStringOfLength 128 prg+ randomname <- randomByteStringOfLength 128 -- It's ok the use the testModeTunables here because -- the randomname is not something that can be feasibly guessed. -- Prefix "random chaff" to the name to avoid ever using a name -- that a real user might want to use. let sis = shareIdents testModeTunables (Name $ "random chaff:" <> randomname) AnyGpgKey- mapConcurrently (go sis prg)+ mapConcurrently (go sis) [1..totalObjects (shareParams testModeTunables)] where server = networkStorage Untrusted Nothing $ Server (ServerName hn) [ServerAddress hn port] "chaff server" objsize = objectSize defaultTunables * shareOverhead defaultTunables maxmsdelay = ceiling $ 1000000 * fromMaybe 0 delayseconds- go sis prg n = do+ go sis n = do msdelay <- getStdRandom (randomR (0, maxmsdelay)) delay msdelay - b <- randomByteStringOfLength objsize prg+ b <- randomByteStringOfLength objsize let share = Share 0 (StorableObject b) let (is, sis') = nextShareIdents sis let i = S.toList is !! (n - 1)@@ -197,7 +195,7 @@ case r of StoreSuccess -> progress "+" _ -> progress "!"- go sis' prg n+ go sis' n -- | Shuffles the list, keeping Recommended first, then -- Alternate, and finally Untrusted.
TODO view
@@ -2,6 +2,20 @@ * Finish vetting 2 servers to Recommended. * Set up --check-servers in a cron job, so I know when servers are down.+* Remove gpg key passohrase from gpg keys that keysafe backs up.+ The reason for this is that the user may well forget their gpg key+ passphrase, and it's *weird* to restore a key with keysafe's password+ and then have it passphrase protected.+ The gpg key passphrase is intended only to keep a key from being used+ for a short period of time (a week or so) when the device holding it + is known to have been compromised, so the key can be revoked.+ This doesn't really apply to keys backed up with keysafe -- if they get+ compromised somehow, the user won't know, and cracking the gpg passphrase+ should be almost trivial to an attacker who was able to break keysafe's+ password.+ paperkey can remove gpg key passphrases. Is there any better way?+ It might make sense for keysafe to prompt for a new gpg passphrase+ when restoring. Later: @@ -25,11 +39,6 @@ * Add some random padding to http requests and responses, to make it harder for traffic analysis to tell that given TOR traffic is keysafe traffic.-* Argon2d is more resistent to GPU/ASIC attack optimisation.- Switching from Argon2i would require new tunables, and delay restores- (of keys backed up using the old tunables, and when the user provides the- wrong name) by ~10 minutes, so deferred for now- until there's some other reason to change the tunables. Wishlist: @@ -72,3 +81,20 @@ restore from working. (It also makes a malicious data attack (as described in https://keysafe.branchable.com/details/) possible by attackers who do not control the servers.++Encryption tunables changes:++* Argon2d is more resistent to GPU/ASIC attack optimisation.+ Switching from Argon2i would require new tunables, and delay restores+ (of keys backed up using the old tunables, and when the user provides the+ wrong name) by ~10 minutes, so deferred for now+ until there's some other reason to change the tunables.+* The ShareIdents derivation currently appends a number and sha256 hashes+ to generate a stream of values. Ben M points out that HMAC is a more+ typical way to do such a thing. Even better, a HKDF-Expand+ (RFC5869) can generate a stream which can then be chunked up into values. + Either of these would avoid a full pre-image attack on SHA-2 breaking+ keysafe. Of course, such an SHA-2 attack would be a general security+ disaster. HKDF may prove more robust in the face of partial SHA-2 breaks.+ Deferred for now until tthere's some other reason to change keysafe's+ tunables.
keysafe.cabal view
@@ -1,5 +1,5 @@ Name: keysafe-Version: 0.20170122+Version: 0.20170303 Cabal-Version: >= 1.8 Maintainer: Joey Hess <joey@kitenet.net> Author: Joey Hess@@ -38,7 +38,7 @@ -- the version ranges, it's important to run keysafe --test secret-sharing == 1.0.* , argon2 == 1.2.*- , raaz == 0.0.2+ , raaz == 0.1.1 , base (>= 4.5 && < 5.0) , bytestring == 0.10.* , text == 1.2.*