{-# LANGUAGE OverloadedStrings #-} {- Copyright 2016 Joey Hess - - Licensed under the GNU AGPL version 3 or higher. -} module Main where import Types import Tunables import qualified CmdLine import UI import Encryption import Entropy import Cost import Shard import Storage import Storage.LocalFiles import Data.Maybe import qualified Data.ByteString as B import qualified Data.ByteString.UTF8 as BU8 import System.Posix.User (userGecos, getUserEntryForID, getEffectiveUserID) main :: IO () main = do cmdline <- CmdLine.get ui <- selectUI (CmdLine.gui cmdline) let keytype = CmdLine.keytype cmdline -- TODO determine gpg key id by examining secret key, -- or retrieving public key from keyserver and examining it. let keyid = KeyId keytype "dummy key id" case CmdLine.mode cmdline of CmdLine.Backup -> storedemo ui keyid $ if CmdLine.testMode cmdline then testModeTunables else defaultTunables CmdLine.Restore -> retrievedemo ui keyid storedemo :: UI -> KeyId -> Tunables -> IO () storedemo ui keyid tunables = do username <- userName name <- fromMaybe (error "Aborting on no name") <$> promptName ui "Enter a name" namedesc username validateName kek <- genKeyEncryptionKey tunables name password putStrLn "Very rough estimate of cost to brute-force the password:" print $ estimateAttack spotAWS $ estimateBruteforceOf kek (passwordEntropy password []) let esk = encrypt tunables kek secretkey let sis = shardIdents tunables name keyid shards <- genShards esk tunables print =<< mapM (uncurry (storeShard localFiles)) (zip (getIdents sis) shards) print =<< obscureShards localFiles where password = Password "correct horse battery staple" secretkey = SecretKey "this is a gpg private key" namedesc = unlines [ "To back up your key, you will need to enter a name and a password." , "" , "Make sure to pick a name you will remember at some point in the future," , "perhaps years from now, when you will need to enter it with the same" , "spelling and capitalization in order to restore the key." , "" , "(Your own full name is a pretty good choice for the name to enter here.)" ] retrievedemo :: UI -> KeyId -> IO () retrievedemo ui keyid = do username <- userName name <- fromMaybe (error "Aborting on no name") <$> promptName ui "Enter the name of the key to restore" namedesc username validateName let sis = shardIdents tunables name keyid -- we drop 1 to simulate not getting all shards from the servers let l = drop 1 $ zip [1..] (getIdents sis) shards <- map (\(RetrieveSuccess s) -> s) <$> mapM (uncurry (retrieveShard localFiles)) l _ <- obscureShards localFiles let esk = combineShards tunables shards go esk (candidateKeyEncryptionKeys tunables name password) where go _ [] = error "decryption failed" go esk (kek:rest) = case decrypt kek esk of Just (SecretKey sk) -> print sk Nothing -> go esk rest password = Password "correct horse battery staple" -- TODO: derive by probing to find objects tunables = testModeTunables -- defaultTunables namedesc = unlines [ "When you backed up the key, you entered a name and a password." , "Now it's time to remember what you entered back then." , "" , "(If you can't remember the name you used, your own full name is the best guess.)" ] validateName :: Name -> Maybe Problem validateName (Name n) | B.length n < 6 = Just "The name should be at least 6 letters long." | otherwise = Nothing userName :: IO Name userName = do u <- getUserEntryForID =<< getEffectiveUserID return $ Name $ BU8.fromString $ takeWhile (/= ',') (userGecos u)