summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CmdLine.hs8
-rw-r--r--Storage.hs25
-rw-r--r--Storage/Local.hs39
-rw-r--r--Storage/Network.hs41
-rw-r--r--TODO3
-rw-r--r--Types/Storage.hs13
-rw-r--r--keysafe.hs2
7 files changed, 91 insertions, 40 deletions
diff --git a/CmdLine.hs b/CmdLine.hs
index a55e985..0213f28 100644
--- a/CmdLine.hs
+++ b/CmdLine.hs
@@ -21,12 +21,12 @@ data CmdLine = CmdLine
, customShardParams :: Maybe ShardParams
}
-data Mode = Backup | Restore | Benchmark
+data Mode = Backup | Restore | UploadQueued | Benchmark
deriving (Show)
parse :: Parser CmdLine
parse = CmdLine
- <$> optional (backup <|> restore <|> benchmark)
+ <$> optional (backup <|> restore <|> uploadqueued <|> benchmark)
<*> optional (gpgswitch <|> fileswitch)
<*> localstorageswitch
<*> guiswitch
@@ -41,6 +41,10 @@ parse = CmdLine
( long "restore"
<> help "Retrieve a secret key from keysafe."
)
+ uploadqueued = flag' UploadQueued
+ ( long "uploadqueued"
+ <> help "Upload any data to servers that was queued by a previous --backup run."
+ )
benchmark = flag' Benchmark
( long "benchmark"
<> help "Benchmark speed of keysafe's cryptographic primitives."
diff --git a/Storage.hs b/Storage.hs
index 6f39cde..ff96a3d 100644
--- a/Storage.hs
+++ b/Storage.hs
@@ -10,23 +10,16 @@ import Types.Storage
import Shard
import Storage.Local
import Storage.Network
-import System.FilePath
import Data.Monoid
import Data.Maybe
+import System.FilePath
+import Control.Monad
allStorageLocations :: IO StorageLocations
allStorageLocations = do
servers <- networkServers
- return $ servers <> uploadQueueLocations servers
-
--- | Objects queued for upload to servers. There are a number of queues,
--- but no 1:1 mapping from queues to a particular server.
--- It's important that when flushing the upload queue, the objects in each
--- separate queue are sent to a separate server.
-uploadQueueLocations :: StorageLocations -> StorageLocations
-uploadQueueLocations (StorageLocations servers) = StorageLocations $
- map (localStorage . ("uploadqueue" </>) . show)
- [1..length servers]
+ return $ StorageLocations $
+ map networkStorage servers <> map uploadQueue servers
localStorageLocations :: StorageLocations
localStorageLocations = StorageLocations $
@@ -52,7 +45,10 @@ storeShards (StorageLocations locs) sis shards = do
StoreSuccess -> do
_ <- showprogress
go otherlocs (loc:usedlocs) Nothing rest
- _ -> go otherlocs usedlocs (Just r) tostore
+ StoreFailure _ -> go otherlocs usedlocs (Just r) tostore
+ -- Give up if any location complains a shard
+ -- already exists, because we have a name conflict.
+ StoreAlreadyExists -> return (StoreAlreadyExists, usedlocs)
-- | Retrieves shards from among the storage locations, and returns all
-- the shards it can find, which may not be all that were requested.
@@ -84,3 +80,8 @@ retrieveShards (StorageLocations locs) l = do
-- all shards and so learn the idents of
-- all of them.
go (unusedlocs++[loc]) usedlocs' rest shards'
+
+uploadQueued :: IO ()
+uploadQueued = do
+ servers <- networkServers
+ forM_ servers $ \s -> moveShards (uploadQueue s) (networkStorage s)
diff --git a/Storage/Local.hs b/Storage/Local.hs
index a13fcae..bd49116 100644
--- a/Storage/Local.hs
+++ b/Storage/Local.hs
@@ -5,10 +5,11 @@
{-# LANGUAGE OverloadedStrings #-}
-module Storage.Local (localStorage) where
+module Storage.Local (localStorage, uploadQueue) where
import Types
import Types.Storage
+import Storage.Network (Server(..))
import Serialization ()
import qualified Data.ByteString as B
import qualified Data.ByteString.UTF8 as U8
@@ -22,6 +23,7 @@ import System.FilePath
import Raaz.Core.Encode
import Control.DeepSeq
import Control.Exception
+import Control.Monad
newtype Section = Section String
@@ -31,10 +33,14 @@ localStorage n = Storage
, retrieveShard = retrieve section
, obscureShards = obscure section
, countShards = count section
+ , moveShards = move section
}
where
section = Section n
+uploadQueue :: Server -> Storage
+uploadQueue s = localStorage ("uploadqueue" </> serverName s)
+
store :: Section -> StorableObjectIdent -> Shard -> IO StoreResult
store section i s = onError (StoreFailure . show) $ do
dir <- shardDir section
@@ -84,6 +90,26 @@ count section = onError (CountFailure . show) $ do
CountResult . genericLength . filter isShardFile
<$> getDirectoryContents dir
+move :: Section -> Storage -> IO ()
+move section storage = do
+ dir <- shardDir section
+ fs <- getDirectoryContents dir
+ forM_ fs $ \f -> case fromShardFile f of
+ Nothing -> return ()
+ Just i -> do
+ -- Use a dummy shard number of 0; it doesn't
+ -- matter because we're not going to be
+ -- recombining the shard, just sending its contents
+ -- on the the server.
+ r <- retrieve section 0 i
+ case r of
+ RetrieveFailure _ -> return ()
+ RetrieveSuccess shard -> do
+ s <- storeShard storage i shard
+ case s of
+ StoreFailure _ -> return ()
+ _ -> removeFile f
+
onError :: (IOException -> a) -> IO a -> IO a
onError f a = do
v <- try a
@@ -96,14 +122,19 @@ shardDir (Section section) = do
u <- getUserEntryForID =<< getEffectiveUserID
return $ homeDirectory u </> dotdir </> section
-shardFile :: StorableObjectIdent -> String
+shardFile :: StorableObjectIdent -> FilePath
shardFile i = U8.toString (toByteString i) <> ext
-ext :: String
-ext = ".keysafe"
+fromShardFile :: FilePath -> Maybe StorableObjectIdent
+fromShardFile f
+ | isShardFile f = fromByteString $ U8.fromString $ dropExtension f
+ | otherwise = Nothing
isShardFile :: FilePath -> Bool
isShardFile f = ext `isSuffixOf` f
+ext :: String
+ext = ".keysafe"
+
dotdir :: FilePath
dotdir = ".keysafe" </> "objects"
diff --git a/Storage/Network.hs b/Storage/Network.hs
index 7a461c7..2b837dc 100644
--- a/Storage/Network.hs
+++ b/Storage/Network.hs
@@ -5,32 +5,39 @@
{-# LANGUAGE OverloadedStrings #-}
-module Storage.Network (networkServers, networkStorage) where
+module Storage.Network (Server(..), networkServers, networkStorage) where
import Types
import Types.Storage
-networkServers :: IO StorageLocations
-networkServers = return $ StorageLocations [] -- none yet
+newtype Server = Server { serverName :: String }
-networkStorage :: Storage
-networkStorage = Storage
- { storeShard = store
- , retrieveShard = retrieve
- , obscureShards = obscure
- , countShards = count
+networkServers :: IO [Server]
+networkServers = return [] -- none yet
+
+networkStorage :: Server -> Storage
+networkStorage server = Storage
+ { storeShard = store server
+ , retrieveShard = retrieve server
+ , obscureShards = obscure server
+ , countShards = count server
+ , moveShards = move server
}
-store :: StorableObjectIdent -> Shard -> IO StoreResult
-store _i _s = return $ StoreFailure "network storage not implemented yet"
+store :: Server -> StorableObjectIdent -> Shard -> IO StoreResult
+store _server _i _s = return $ StoreFailure "network storage not implemented yet"
-retrieve :: ShardNum -> StorableObjectIdent -> IO RetrieveResult
-retrieve _n _i = return $ RetrieveFailure "network storage not implemented yet"
+retrieve :: Server -> ShardNum -> StorableObjectIdent -> IO RetrieveResult
+retrieve _server _n _i = return $ RetrieveFailure "network storage not implemented yet"
-- | Servers should automatically obscure, so do nothing.
-- (Could upload chaff.)
-obscure :: IO ObscureResult
-obscure = return ObscureSuccess
+obscure :: Server -> IO ObscureResult
+obscure _ = return ObscureSuccess
+
+count :: Server -> IO CountResult
+count _server = return $ CountFailure "network storage not implemented yet"
-count :: IO CountResult
-count = return $ CountFailure "network storage not implemented yet"
+-- | Not needed for servers.
+move :: Server -> Storage -> IO ()
+move _ _ = return ()
diff --git a/TODO b/TODO
index 37088d9..9006c7e 100644
--- a/TODO
+++ b/TODO
@@ -1,8 +1,7 @@
* splitting large secret keys
* tune hashes on more powerful hardware than thermal throttling laptop
* store to servers
-* Run periodically and store any objects in uploadqueue on servers,
- to recover from eg network failure.
+* Run --uploadqueued periodically (systemd timer?)
* improve restore progress bar points (update after every hash try)
* Keep secret keys in locked memory until they're encrypted.
(Raaz makes this possible to do.)
diff --git a/Types/Storage.hs b/Types/Storage.hs
index bc11b55..b3f714a 100644
--- a/Types/Storage.hs
+++ b/Types/Storage.hs
@@ -14,14 +14,21 @@ import Types
newtype StorageLocations = StorageLocations [Storage]
deriving (Monoid)
+-- | Storage interface. This can be used both for local storage,
+-- an upload queue, or a remote server.
+--
+-- Note that there is no interface to enumerate shards.
+-- This is intentional; servers should not allow that.
data Storage = Storage
{ storeShard :: StorableObjectIdent -> Shard -> IO StoreResult
, retrieveShard :: ShardNum -> StorableObjectIdent -> IO RetrieveResult
, obscureShards :: IO ObscureResult
- -- ^ run after making some calls to storeShard/retrieveShard,
- -- to avoid correlation attacks
+ -- ^ Run after making some calls to storeShard/retrieveShard,
+ -- to avoid correlation attacks.
, countShards :: IO CountResult
- } -- Note that there is no interface to enumerate shards.
+ , moveShards :: Storage -> IO ()
+ -- ^ Tries to move all shards from this storage to another one.
+ }
data StoreResult = StoreSuccess | StoreAlreadyExists | StoreFailure String
deriving (Show)
diff --git a/keysafe.hs b/keysafe.hs
index c1ed35a..919150b 100644
--- a/keysafe.hs
+++ b/keysafe.hs
@@ -59,6 +59,8 @@ dispatch cmdline ui storagelocations tunables possibletunables = do
=<< Gpg.getKeyToBackup ui
go CmdLine.Restore Nothing =
restore storagelocations ui possibletunables Gpg.anyKey
+ go CmdLine.UploadQueued _ =
+ uploadQueued
go CmdLine.Benchmark _ =
benchmarkTunables tunables