{- Copyright 2016 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} {-# LANGUAGE DeriveGeneric, BangPatterns #-} module BackupRecord where import Types import Types.Cost import Types.Server import GHC.Generics import Data.Time.Clock.POSIX import Data.Aeson import Data.Maybe import System.FilePath import System.Directory import System.Posix.User import System.Posix.Files import qualified Data.ByteString.Lazy as B -- | Record of a backup. -- -- If an attacker cracks the user's system and finds this stored -- on it, it should not help them recover keys from keysafe. -- -- That's why the Name used is not included; as knowing the name lets -- an attacker download shards and start password cracking. -- -- Including the password entropy does let an attacker avoid trying -- weak passwords and go right to passwords that are strong enough, but -- this should only half the password crack time at worst. data BackupRecord = BackupRecord { backupDate :: POSIXTime , backupServers :: [HostName] , secretKeySource :: String , passwordEntropy :: Int } deriving (Show, Generic) -- BackupRecord is serialized as JSON. instance ToJSON BackupRecord instance FromJSON BackupRecord mkBackupRecord :: [Server] -> SecretKeySource -> Entropy UnknownPassword -> IO BackupRecord mkBackupRecord servers sks (Entropy n) = BackupRecord <$> getPOSIXTime <*> pure (map serverName servers) <*> pure (show sks) <*> pure n backupRecordFile :: IO FilePath backupRecordFile = do u <- getUserEntryForID =<< getEffectiveUserID return $ homeDirectory u ".keysafe/backup.log" readBackupRecords :: IO [BackupRecord] readBackupRecords = do f <- backupRecordFile e <- doesFileExist f if e then fromMaybe [] . decode <$> B.readFile f else return [] storeBackupRecord :: BackupRecord -> IO () storeBackupRecord r = do !rs <- readBackupRecords f <- backupRecordFile let d = takeDirectory f createDirectoryIfMissing True d setFileMode d $ ownerReadMode `unionFileModes` ownerWriteMode `unionFileModes` ownerExecuteMode setPermissions d $ setOwnerReadable True $ setOwnerWritable True $ setOwnerExecutable True emptyPermissions B.writeFile f $ encode (r:rs)