2011-04-17 04:40:23 +00:00
|
|
|
{- common functions for encryptable remotes
|
2011-04-16 17:25:27 +00:00
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
2011-08-17 00:49:54 +00:00
|
|
|
module Remote.Helper.Encryptable where
|
2011-04-16 17:25:27 +00:00
|
|
|
|
|
|
|
import qualified Data.Map as M
|
|
|
|
|
2011-10-05 20:02:51 +00:00
|
|
|
import Common.Annex
|
2011-06-02 01:56:04 +00:00
|
|
|
import Types.Remote
|
2011-04-16 17:25:27 +00:00
|
|
|
import Crypto
|
2013-03-29 16:06:02 +00:00
|
|
|
import Types.Crypto
|
2011-04-16 22:22:52 +00:00
|
|
|
import qualified Annex
|
2013-03-13 20:16:01 +00:00
|
|
|
import Config.Cost
|
2012-04-29 18:31:34 +00:00
|
|
|
import Utility.Base64
|
2013-03-28 21:03:04 +00:00
|
|
|
import Utility.Metered
|
2011-04-16 17:25:27 +00:00
|
|
|
|
|
|
|
{- Encryption setup for a remote. The user must specify whether to use
|
|
|
|
- an encryption key, or not encrypt. An encrypted cipher is created, or is
|
2012-04-29 18:02:18 +00:00
|
|
|
- updated to be accessible to an additional encryption key. Or the user
|
|
|
|
- could opt to use a shared cipher, which is stored unencrypted. -}
|
2011-04-16 17:25:27 +00:00
|
|
|
encryptionSetup :: RemoteConfig -> Annex RemoteConfig
|
2012-04-29 18:02:18 +00:00
|
|
|
encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of
|
|
|
|
(Nothing, Nothing) -> error "Specify encryption=key or encryption=none or encryption=shared"
|
|
|
|
(Just "none", Nothing) -> return c
|
|
|
|
(Nothing, Just _) -> return c
|
|
|
|
(Just "shared", Just (SharedCipher _)) -> return c
|
|
|
|
(Just "none", Just _) -> cannotchange
|
|
|
|
(Just "shared", Just (EncryptedCipher _ _)) -> cannotchange
|
|
|
|
(Just _, Just (SharedCipher _)) -> cannotchange
|
2013-04-05 19:06:16 +00:00
|
|
|
(Just "shared", Nothing) -> use "encryption setup" . genSharedCipher
|
2013-04-06 20:14:57 +00:00
|
|
|
=<< highRandomQuality
|
2013-04-05 19:06:16 +00:00
|
|
|
(Just keyid, Nothing) -> use "encryption setup" . genEncryptedCipher keyid
|
2013-04-06 20:14:57 +00:00
|
|
|
=<< highRandomQuality
|
2012-04-29 18:02:18 +00:00
|
|
|
(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
cannotchange = error "Cannot change encryption type of existing remote."
|
|
|
|
use m a = do
|
|
|
|
cipher <- liftIO a
|
|
|
|
showNote $ m ++ " " ++ describeCipher cipher
|
2013-04-05 19:06:16 +00:00
|
|
|
return $ M.delete "encryption" $ M.delete "highRandomQuality" $
|
|
|
|
storeCipher c cipher
|
2013-04-06 20:14:57 +00:00
|
|
|
highRandomQuality =
|
|
|
|
(&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c)
|
|
|
|
<$> fmap not (Annex.getState Annex.fast)
|
2011-04-16 22:22:52 +00:00
|
|
|
|
2011-04-17 01:41:14 +00:00
|
|
|
{- Modifies a Remote to support encryption.
|
|
|
|
-
|
|
|
|
- Two additional functions must be provided by the remote,
|
|
|
|
- to support storing and retrieving encrypted content. -}
|
2011-04-17 04:40:23 +00:00
|
|
|
encryptableRemote
|
2012-11-30 04:55:59 +00:00
|
|
|
:: RemoteConfig
|
2012-09-21 18:50:14 +00:00
|
|
|
-> ((Cipher, Key) -> Key -> MeterUpdate -> Annex Bool)
|
2013-04-11 21:15:45 +00:00
|
|
|
-> ((Cipher, Key) -> Key -> FilePath -> MeterUpdate -> Annex Bool)
|
2011-12-31 08:11:39 +00:00
|
|
|
-> Remote
|
|
|
|
-> Remote
|
2011-04-17 04:40:23 +00:00
|
|
|
encryptableRemote c storeKeyEncrypted retrieveKeyFileEncrypted r =
|
2011-04-17 01:41:14 +00:00
|
|
|
r {
|
|
|
|
storeKey = store,
|
|
|
|
retrieveKeyFile = retrieve,
|
2012-01-20 17:23:11 +00:00
|
|
|
retrieveKeyFileCheap = retrieveCheap,
|
2011-04-17 01:41:14 +00:00
|
|
|
removeKey = withkey $ removeKey r,
|
2011-04-17 05:13:21 +00:00
|
|
|
hasKey = withkey $ hasKey r,
|
|
|
|
cost = cost r + encryptedRemoteCostAdj
|
2011-04-17 01:41:14 +00:00
|
|
|
}
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
store k f p = cip k >>= maybe
|
|
|
|
(storeKey r k f p)
|
|
|
|
(\enck -> storeKeyEncrypted enck k p)
|
2013-04-11 21:15:45 +00:00
|
|
|
retrieve k f d p = cip k >>= maybe
|
|
|
|
(retrieveKeyFile r k f d p)
|
|
|
|
(\enck -> retrieveKeyFileEncrypted enck k d p)
|
2012-11-11 04:51:07 +00:00
|
|
|
retrieveCheap k d = cip k >>= maybe
|
|
|
|
(retrieveKeyFileCheap r k d)
|
|
|
|
(\_ -> return False)
|
|
|
|
withkey a k = cip k >>= maybe (a k) (a . snd)
|
|
|
|
cip = cipherKey c
|
2011-04-16 22:22:52 +00:00
|
|
|
|
2011-12-08 20:01:46 +00:00
|
|
|
{- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex
|
2011-05-01 18:05:10 +00:00
|
|
|
- state. -}
|
|
|
|
remoteCipher :: RemoteConfig -> Annex (Maybe Cipher)
|
2011-12-08 20:01:46 +00:00
|
|
|
remoteCipher c = go $ extractCipher c
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
go Nothing = return Nothing
|
|
|
|
go (Just encipher) = do
|
|
|
|
cache <- Annex.getState Annex.ciphers
|
|
|
|
case M.lookup encipher cache of
|
|
|
|
Just cipher -> return $ Just cipher
|
2012-11-18 19:27:44 +00:00
|
|
|
Nothing -> do
|
|
|
|
showNote "gpg"
|
|
|
|
cipher <- liftIO $ decryptCipher encipher
|
|
|
|
Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })
|
|
|
|
return $ Just cipher
|
2011-05-01 18:05:10 +00:00
|
|
|
|
2012-11-19 21:32:58 +00:00
|
|
|
{- Checks if the remote's config allows storing creds in the remote's config.
|
|
|
|
-
|
|
|
|
- embedcreds=yes allows this, and embedcreds=no prevents it.
|
|
|
|
-
|
|
|
|
- If not set, the default is to only store creds when it's surely safe:
|
|
|
|
- When gpg encryption is used, in which case the creds will be encrypted
|
|
|
|
- using it. Not when a shared cipher is used.
|
|
|
|
-}
|
|
|
|
embedCreds :: RemoteConfig -> Bool
|
|
|
|
embedCreds c
|
|
|
|
| M.lookup "embedcreds" c == Just "yes" = True
|
|
|
|
| M.lookup "embedcreds" c == Just "no" = False
|
|
|
|
| isJust (M.lookup "cipherkeys" c) && isJust (M.lookup "cipher" c) = True
|
|
|
|
| otherwise = False
|
2012-09-26 16:06:44 +00:00
|
|
|
|
2011-05-01 18:05:10 +00:00
|
|
|
{- Gets encryption Cipher, and encrypted version of Key. -}
|
2012-11-30 04:55:59 +00:00
|
|
|
cipherKey :: RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
|
|
|
|
cipherKey c k = maybe Nothing make <$> remoteCipher c
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
2013-03-29 16:06:02 +00:00
|
|
|
make ciphertext = Just (ciphertext, encryptKey mac ciphertext k)
|
|
|
|
mac = fromMaybe defaultMac $ M.lookup "mac" c >>= readMac
|
2012-04-29 18:31:34 +00:00
|
|
|
|
|
|
|
{- Stores an StorableCipher in a remote's configuration. -}
|
|
|
|
storeCipher :: RemoteConfig -> StorableCipher -> RemoteConfig
|
|
|
|
storeCipher c (SharedCipher t) = M.insert "cipher" (toB64 t) c
|
|
|
|
storeCipher c (EncryptedCipher t ks) =
|
|
|
|
M.insert "cipher" (toB64 t) $ M.insert "cipherkeys" (showkeys ks) c
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
showkeys (KeyIds l) = join "," l
|
2012-04-29 18:31:34 +00:00
|
|
|
|
|
|
|
{- Extracts an StorableCipher from a remote's configuration. -}
|
|
|
|
extractCipher :: RemoteConfig -> Maybe StorableCipher
|
|
|
|
extractCipher c =
|
|
|
|
case (M.lookup "cipher" c, M.lookup "cipherkeys" c) of
|
|
|
|
(Just t, Just ks) -> Just $ EncryptedCipher (fromB64 t) (readkeys ks)
|
|
|
|
(Just t, Nothing) -> Just $ SharedCipher (fromB64 t)
|
|
|
|
_ -> Nothing
|
2012-11-11 04:51:07 +00:00
|
|
|
where
|
|
|
|
readkeys = KeyIds . split ","
|