keysafe 0.20170303 → 0.20170811
raw patch · 10 files changed
+32/−135 lines, 10 filesdep ~MonadRandomdep ~aesondep ~directory
Dependency ranges changed: MonadRandom, aeson, directory, http-client, optparse-applicative, servant, servant-client, servant-server
Files
- CHANGELOG +6/−0
- CmdLine.hs +1/−0
- Encryption.hs +4/−2
- HTTP/Client.hs +8/−17
- HTTP/ProofOfWork.hs +2/−2
- HTTP/Server.hs +1/−0
- Makefile +1/−1
- Storage.hs +0/−3
- TODO +0/−100
- keysafe.cabal +9/−10
CHANGELOG view
@@ -1,3 +1,9 @@+keysafe (0.20170811) unstable; urgency=medium++ * Updated to http-client 0.5.3, servant 0.11, and stackage lts-9.0.++ -- Joey Hess <id@joeyh.name> Fri, 11 Aug 2017 18:57:15 -0400+ keysafe (0.20170303) unstable; urgency=medium * Updated to use raaz-0.1.1.
CmdLine.hs view
@@ -14,6 +14,7 @@ import Tunables import qualified Gpg import Options.Applicative+import Data.Monoid import qualified Data.ByteString.UTF8 as BU8 import qualified Data.Text as T import System.Directory
Encryption.hs view
@@ -20,7 +20,6 @@ import qualified Data.Text.Encoding as E import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as B8-import qualified Data.ByteString.UTF8 as BU8 import Text.Read type AesKey = Raaz.KEY256@@ -202,7 +201,10 @@ -- | Encoded, so that it does not contain any NULs. sha :: B.ByteString -> B.ByteString-sha = BU8.fromString . Raaz.showBase16 . Raaz.sha256+sha = Raaz.toByteString . enc . Raaz.sha256+ where+ enc :: Raaz.Encodable a => a -> Raaz.Base16+ enc = Raaz.encode padBytes :: Int -> B.ByteString -> B.ByteString padBytes n b = b <> padding
HTTP/Client.hs view
@@ -15,11 +15,8 @@ import Servant.Client import Data.Proxy import Network.HTTP.Client hiding (port, host, Proxy)-import Network.HTTP.Client.Internal (Connection, makeConnection)-import Control.Monad.Trans.Except (ExceptT, runExceptT)+import Network.HTTP.Client.Internal (Connection) import Control.Exception-import qualified Network.Socket-import Network.Socket.ByteString (sendAll, recv) import Network.Socks5 import qualified Data.ByteString.UTF8 as BU8 import Data.List@@ -28,10 +25,10 @@ httpAPI :: Proxy HttpAPI httpAPI = Proxy -motd :: Manager -> BaseUrl -> ClientM Motd-getObject :: StorableObjectIdent -> Maybe ProofOfWork -> Manager -> BaseUrl -> ClientM (POWGuarded StorableObject)-putObject :: StorableObjectIdent -> Maybe ProofOfWork -> StorableObject -> Manager -> BaseUrl -> ClientM (POWGuarded StoreResult)-countObjects :: Maybe ProofOfWork -> Manager -> BaseUrl -> ClientM (POWGuarded CountResult)+motd :: ClientM Motd+getObject :: StorableObjectIdent -> Maybe ProofOfWork -> ClientM (POWGuarded StorableObject)+putObject :: StorableObjectIdent -> Maybe ProofOfWork -> StorableObject -> ClientM (POWGuarded StoreResult)+countObjects :: Maybe ProofOfWork -> ClientM (POWGuarded CountResult) motd :<|> getObject :<|> putObject :<|> countObjects = client httpAPI tryA :: IO a -> IO (Either SomeException a)@@ -43,7 +40,7 @@ -> (String -> a) -> (r -> a) -> p- -> (Maybe ProofOfWork -> Manager -> BaseUrl -> ExceptT ServantError IO (POWGuarded r))+ -> (Maybe ProofOfWork -> ClientM (POWGuarded r)) -> IO a serverRequest srv onerr onsuccess p a = do r <- tryA $ go Nothing maxProofOfWork@@ -69,7 +66,7 @@ -- comparing IP addresses (which are masked somewhat by using tor). serverRequest' :: Server- -> (Manager -> BaseUrl -> ExceptT ServantError IO r)+ -> (ClientM r) -> IO (Either String r) serverRequest' srv a = go Nothing (serverUrls srv) where@@ -77,7 +74,7 @@ maybe "no known address" (\err -> "server failure: " ++ show err) lasterr go _ (url:urls) = do manager <- torableManager- res <- runExceptT $ a manager url+ res <- runClientM a (ClientEnv manager url) case res of Left err -> go (Just err) urls Right r -> return (Right r)@@ -103,12 +100,6 @@ torsockconf = defaultSocksConf "127.0.0.1" torsocksport socksdomain = SocksAddrDomainName (BU8.fromString onionaddress) socksaddr = SocksAddress socksdomain (fromIntegral p)--socketConnection :: Network.Socket.Socket -> Int -> IO Connection-socketConnection socket chunksize = makeConnection- (recv socket chunksize)- (sendAll socket)- (Network.Socket.close socket) serverUrls :: Server -> [BaseUrl] serverUrls srv = map go (serverAddress srv)
HTTP/ProofOfWork.hs view
@@ -16,7 +16,7 @@ import GHC.Generics import qualified Data.Text as T import qualified Data.ByteString as B-import Data.Text.Encoding (encodeUtf8)+import Data.Text.Encoding (decodeUtf8, encodeUtf8) import Raaz.Core.Encode import qualified Raaz import Data.BloomFilter.Hash@@ -109,7 +109,7 @@ mkRequeestID' :: RequestIDSecret -> RandomSalt -> RequestID mkRequeestID' (RequestIDSecret key) salt = let hmac = Raaz.hmacSha256 key (encodeUtf8 $ fromRandomSalt salt)- in RequestID salt (T.pack (showBase16 hmac))+ in RequestID salt $ decodeUtf8 $ Raaz.toByteString (Raaz.encode hmac :: Base16) validRequestID :: RequestIDSecret -> RequestID -> Bool validRequestID secret rid =
HTTP/Server.hs view
@@ -18,6 +18,7 @@ import Storage.Local import Serialization () import Servant+import Network.Wai (Application) import Network.Wai.Handler.Warp import Control.Monad.IO.Class import Control.Concurrent
Makefile view
@@ -34,7 +34,7 @@ $(BUILDER) build $(BUILDEROPTIONS); \ fi if [ "$(BUILDER)" = stack ]; then \- ln -sf $$(find .stack-work/ -name keysafe -type f | grep build/keysafe/keysafe | tail -n 1) keysafe; \+ ln -sf $$(stack path --dist-dir)/build/keysafe/keysafe keysafe; \ else \ ln -sf dist/build/keysafe/keysafe keysafe; \ fi
Storage.hs view
@@ -73,9 +73,6 @@ -- -- If a server is not currently accessible, it will be queued locally. -- If any uploads are queued, returns True.------ TODO: Add shuffling and queueing/chaffing to prevent--- correlation of related shares. storeShares :: StorageLocations -> ShareIdents -> [S.Set Share] -> UpdateProgress -> IO (StoreResult, Bool, [Storage]) storeShares (StorageLocations locs) allsis shares updateprogress = do ((r, anyqueued), usedlocs) <- go allsis shares [] False
− TODO
@@ -1,100 +0,0 @@-Soon:--* 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:--* The attack cost display can lead to a false sense of security if the user- takes it as gospel. It needs to be clear that it's an estimate. This and- other parts of the keysafe UI need usability testing.-* Make --gui password entry fields longer, so user does not feel they need- to make password short. (zenity may not allow configuring this)-* improve restore progress bar points (update after every hash try)-* If we retrieved enough shares successfully, but decrypt failed, must- be a wrong password, so prompt for re-entry and retry with those shares.-* --no-jargon which makes the UI avoid terms like "secret key" and "crack- password". Do usability testing!-* --key-value=$N which eliminates the question about password value,- and rejects passwords that would cost less than $N to crack at current- rates. This should add a combo box to the password entry form in the- GUI to let the user adjust the $N there.-* In backup, only upload to N-1 servers immediately, and delay the rest- for up to several days, with some uploads of chaff, to prevent- collaborating evil servers from correlating related shards.-* 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.--Wishlist:--* Custom GUI, instead of zenity. Allows:- - Fewer screens by consolidating multiple prompts.- - Check same password entered second time and don't allow continuing- if not.- - Password strengh display, and don't allow continuing if password is too- weak.-* Keep secret keys in locked memory until they're encrypted.- (Raaz makes this possible to do.)- Would be nice, but not super-important, since gpg secret keys- are passphrase protected anyway..-* Don't require --totalshares and --neededshares on restore when unusual- values were used for backup. -- The difficulty is that the number of needed shares cannot be determined by- looking at shares, and guessing it wrong will result in combining- too few shares yielding garbage, which it will take up to an hour to- try to decrypt, before it can tell that more shares are needed.-- This could be dealt with by including the number of needed shares in the- serialization of Share, but then an attacker could use it to partition- shares from servers. If only one person uses --neededshares=5,- the attacker can guess that all their shares go together.-- What about including the number of needed shares in the name? Since that's- hashed, it's not visible to an attacker. Keysafe would need to try names- with 2 shares, then 3, etc, and once it found shares, it would know the- number needed. It should also be possible to avoid breaking backwards- compatability, by only including the number of shares in the name when- it's not the standard number. To avoid needing to re-run argon2 for each- try, the argon2 hash of the name could be calculated first, and then the- number of needed shares appended before the final sha256 hash is- generated.- - If an attacker is able to guess the name, and a nonstandard number of- shares was used, the attacker could upload other objects where they would- be found before the real objects. This could be used to prevent- 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.20170303+Version: 0.20170811 Cabal-Version: >= 1.8 Maintainer: Joey Hess <joey@kitenet.net> Author: Joey Hess@@ -19,7 +19,6 @@ License-File: AGPL Extra-Source-Files: CHANGELOG- TODO INSTALL keysafe.1 keysafe.service@@ -52,18 +51,18 @@ , unix == 2.7.* , filepath == 1.4.* , split == 0.2.*- , directory == 1.2.*+ , directory (>= 1.2 && < 1.4) , process (>= 1.2 && < 1.5)- , optparse-applicative == 0.12.*+ , optparse-applicative (>= 0.12 && < 0.14) , readline == 1.0.* , zxcvbn-c == 1.0.*- , servant (>= 0.7 && < 0.9)- , servant-server (>= 0.7 && < 0.9)- , servant-client (>= 0.7 && < 0.9)- , aeson == 0.11.*+ , servant (>= 0.7 && < 0.12)+ , servant-server (>= 0.7 && < 0.12)+ , servant-client (>= 0.7 && < 0.12)+ , aeson (>= 0.11 && < 1.2) , wai == 3.2.* , warp == 3.2.*- , http-client == 0.4.*+ , http-client (>= 0.5.3 && < 0.6) , transformers (>= 0.4 && < 0.6) , stm == 2.4.* , socks == 0.5.*@@ -79,7 +78,7 @@ , unix-compat == 0.4.* , exceptions == 0.8.* , random-shuffle == 0.0.*- , MonadRandom == 0.4.*+ , MonadRandom (>= 0.4 && < 0.6) Other-Modules: AutoStart BackupLog