git-annex/Remote/Helper/Encryptable.hs

115 lines
4 KiB
Haskell
Raw Normal View History

2011-04-17 04:40:23 +00:00
{- common functions for encryptable remotes
-
- 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
import qualified Data.Map as M
2011-10-05 20:02:51 +00:00
import Common.Annex
import Types.Remote
import Crypto
import qualified Annex
import Config
2012-04-29 18:31:34 +00:00
import Utility.Base64
{- 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
- updated to be accessible to an additional encryption key. Or the user
- could opt to use a shared cipher, which is stored unencrypted. -}
encryptionSetup :: RemoteConfig -> Annex RemoteConfig
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
(Just "shared", Nothing) -> use "encryption setup" $ genSharedCipher
(Just keyid, Nothing) -> use "encryption setup" $ genEncryptedCipher keyid
(Just keyid, Just v) -> use "encryption updated" $ updateEncryptedCipher keyid v
where
cannotchange = error "Cannot change encryption type of existing remote."
2011-04-17 22:18:27 +00:00
use m a = do
cipher <- liftIO a
2011-04-17 22:18:27 +00:00
showNote $ m ++ " " ++ describeCipher cipher
return $ M.delete "encryption" $ storeCipher c cipher
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
2011-04-17 01:41:14 +00:00
:: Maybe RemoteConfig
-> ((Cipher, Key) -> Key -> Annex Bool)
-> ((Cipher, Key) -> Key -> FilePath -> 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,
retrieveKeyFileCheap = retrieveCheap,
2011-04-17 01:41:14 +00:00
removeKey = withkey $ removeKey r,
hasKey = withkey $ hasKey r,
cost = cost r + encryptedRemoteCostAdj
2011-04-17 01:41:14 +00:00
}
where
2011-05-15 16:25:58 +00:00
store k = cip k >>= maybe
(storeKey r k)
2011-07-15 16:47:14 +00:00
(`storeKeyEncrypted` k)
retrieve k f = cip k >>= maybe
(retrieveKeyFile r k f)
(\enck -> retrieveKeyFileEncrypted enck k f)
retrieveCheap k f = cip k >>= maybe
(retrieveKeyFileCheap r k f)
(\_ -> return False)
2011-05-15 16:25:58 +00:00
withkey a k = cip k >>= maybe (a k) (a . snd)
cip = cipherKey c
{- Gets encryption Cipher. The decrypted Ciphers are cached in the Annex
- state. -}
remoteCipher :: RemoteConfig -> Annex (Maybe Cipher)
remoteCipher c = go $ extractCipher c
2011-05-15 06:49:43 +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
Nothing -> decrypt encipher cache
decrypt encipher cache = do
showNote "gpg"
cipher <- liftIO $ decryptCipher encipher
Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache })
return $ Just cipher
{- Gets encryption Cipher, and encrypted version of Key. -}
cipherKey :: Maybe RemoteConfig -> Key -> Annex (Maybe (Cipher, Key))
cipherKey Nothing _ = return Nothing
2011-10-11 18:43:45 +00:00
cipherKey (Just c) k = maybe Nothing encrypt <$> remoteCipher c
2011-05-15 16:25:58 +00:00
where
2011-10-11 18:43:45 +00:00
encrypt ciphertext = Just (ciphertext, encryptKey ciphertext k)
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
where
showkeys (KeyIds l) = join "," l
{- 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
where
readkeys = KeyIds . split ","