diff options
author | Joey Hess <joey@kitenet.net> | 2012-04-29 14:02:18 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2012-04-29 14:02:43 -0400 |
commit | 1c16f616df9a8469d24cefb6007333df3a35a449 (patch) | |
tree | 2198232da7d7594d1a8d952724497c08ee22d2ce | |
parent | d7a4a9a66bd51b18a9e5f4427c3492db1adec40d (diff) |
Added shared cipher mode to encryptable special remotes.
This option avoids gpg key distribution, at the expense of flexability, and
with the requirement that all clones of the git repository be equally
trusted.
-rw-r--r-- | Annex.hs | 2 | ||||
-rw-r--r-- | Crypto.hs | 77 | ||||
-rw-r--r-- | Remote/Helper/Encryptable.hs | 25 | ||||
-rw-r--r-- | Types/Crypto.hs | 6 | ||||
-rw-r--r-- | Utility/Gpg.hs | 13 | ||||
-rw-r--r-- | debian/changelog | 3 | ||||
-rw-r--r-- | doc/encryption.mdwn | 13 |
7 files changed, 80 insertions, 59 deletions
@@ -90,7 +90,7 @@ data AnnexState = AnnexState , shared :: Maybe SharedRepository , forcetrust :: TrustMap , trustmap :: Maybe TrustMap - , ciphers :: M.Map EncryptedCipher Cipher + , ciphers :: M.Map StorableCipher Cipher , lockpool :: M.Map FilePath Fd , flags :: M.Map String Bool , fields :: M.Map String String @@ -3,16 +3,17 @@ - Currently using gpg; could later be modified to support different - crypto backends if neccessary. - - - Copyright 2011 Joey Hess <joey@kitenet.net> + - Copyright 2011-2012 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -} module Crypto ( Cipher, - EncryptedCipher, - genCipher, - updateCipher, + StorableCipher(..), + genEncryptedCipher, + genSharedCipher, + updateEncryptedCipher, describeCipher, storeCipher, extractCipher, @@ -60,59 +61,55 @@ cipherPassphrase (Cipher c) = drop cipherHalf c cipherHmac :: Cipher -> String cipherHmac (Cipher c) = take cipherHalf c -{- Creates a new Cipher, encrypted as specified in the remote's configuration -} -genCipher :: RemoteConfig -> IO EncryptedCipher -genCipher c = do - ks <- configKeyIds c - random <- genrandom +{- Creates a new Cipher, encrypted to the specificed key id. -} +genEncryptedCipher :: String -> IO StorableCipher +genEncryptedCipher keyid = do + ks <- Gpg.findPubKeys keyid + random <- Gpg.genRandom cipherSize encryptCipher (Cipher random) ks - where - genrandom = Gpg.readStrict - -- Armor the random data, to avoid newlines, - -- since gpg only reads ciphers up to the first - -- newline. - [ Params "--gen-random --armor" - , Param $ show randomquality - , Param $ show cipherSize - ] - -- 1 is /dev/urandom; 2 is /dev/random - randomquality = 1 :: Int - -{- Updates an existing Cipher, re-encrypting it to add KeyIds specified in - - the remote's configuration. -} -updateCipher :: RemoteConfig -> EncryptedCipher -> IO EncryptedCipher -updateCipher c encipher@(EncryptedCipher _ ks) = do - ks' <- configKeyIds c - cipher <- decryptCipher c encipher + +{- Creates a new, shared Cipher. -} +genSharedCipher :: IO StorableCipher +genSharedCipher = SharedCipher <$> Gpg.genRandom cipherSize + +{- Updates an existing Cipher, re-encrypting it to add a keyid. -} +updateEncryptedCipher :: String -> StorableCipher -> IO StorableCipher +updateEncryptedCipher _ (SharedCipher _) = undefined +updateEncryptedCipher keyid encipher@(EncryptedCipher _ ks) = do + ks' <- Gpg.findPubKeys keyid + cipher <- decryptCipher encipher encryptCipher cipher (merge ks ks') where merge (KeyIds a) (KeyIds b) = KeyIds $ a ++ b -describeCipher :: EncryptedCipher -> String +describeCipher :: StorableCipher -> String +describeCipher (SharedCipher _) = "shared cipher" describeCipher (EncryptedCipher _ (KeyIds ks)) = "with gpg " ++ keys ks ++ " " ++ unwords ks where keys [_] = "key" keys _ = "keys" -{- Stores an EncryptedCipher in a remote's configuration. -} -storeCipher :: RemoteConfig -> EncryptedCipher -> RemoteConfig +{- 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 EncryptedCipher from a remote's configuration. -} -extractCipher :: RemoteConfig -> Maybe EncryptedCipher +{- 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 "," {- Encrypts a Cipher to the specified KeyIds. -} -encryptCipher :: Cipher -> KeyIds -> IO EncryptedCipher +encryptCipher :: Cipher -> KeyIds -> IO StorableCipher encryptCipher (Cipher c) (KeyIds ks) = do let ks' = nub $ sort ks -- gpg complains about duplicate recipient keyids encipher <- Gpg.pipeStrict (encrypt++recipients ks') c @@ -126,9 +123,9 @@ encryptCipher (Cipher c) (KeyIds ks) = do force_recipients = Params "--no-encrypt-to --no-default-recipient" {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} -decryptCipher :: RemoteConfig -> EncryptedCipher -> IO Cipher -decryptCipher _ (EncryptedCipher encipher _) = - Cipher <$> Gpg.pipeStrict decrypt encipher +decryptCipher :: StorableCipher -> IO Cipher +decryptCipher (SharedCipher t) = return $ Cipher t +decryptCipher (EncryptedCipher t _) = Cipher <$> Gpg.pipeStrict decrypt t where decrypt = [ Param "--decrypt" ] @@ -165,14 +162,6 @@ pass :: (Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a) -> Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a pass to c i a = to c i $ \h -> a =<< L.hGetContents h -configKeyIds :: RemoteConfig -> IO KeyIds -configKeyIds c = Gpg.findPubKeys $ configGet c "encryption" - -configGet :: RemoteConfig -> String -> String -configGet c key = fromMaybe missing $ M.lookup key c - where - missing = error $ "missing " ++ key ++ " in remote config" - hmacWithCipher :: Cipher -> String -> String hmacWithCipher c = hmacWithCipher' (cipherHmac c) hmacWithCipher' :: String -> String -> String diff --git a/Remote/Helper/Encryptable.hs b/Remote/Helper/Encryptable.hs index bcecb30cc..a44e6e453 100644 --- a/Remote/Helper/Encryptable.hs +++ b/Remote/Helper/Encryptable.hs @@ -17,17 +17,22 @@ import Config {- 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. -} + - 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" - (Just "none", Nothing) -> return c - (Just "none", Just _) -> error "Cannot change encryption type of existing remote." - (Nothing, Just _) -> return c - (Just _, Nothing) -> use "encryption setup" $ genCipher c - (Just _, Just v) -> use "encryption updated" $ updateCipher c v +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." use m a = do cipher <- liftIO a showNote $ m ++ " " ++ describeCipher cipher @@ -78,7 +83,7 @@ remoteCipher c = go $ extractCipher c Nothing -> decrypt encipher cache decrypt encipher cache = do showNote "gpg" - cipher <- liftIO $ decryptCipher c encipher + cipher <- liftIO $ decryptCipher encipher Annex.changeState (\s -> s { Annex.ciphers = M.insert encipher cipher cache }) return $ Just cipher diff --git a/Types/Crypto.hs b/Types/Crypto.hs index 686bf5c1a..135522ba1 100644 --- a/Types/Crypto.hs +++ b/Types/Crypto.hs @@ -1,13 +1,13 @@ {- git-annex crypto types - - - Copyright 2011 Joey Hess <joey@kitenet.net> + - Copyright 2011-2012 Joey Hess <joey@kitenet.net> - - Licensed under the GNU GPL version 3 or higher. -} module Types.Crypto ( Cipher(..), - EncryptedCipher(..), + StorableCipher(..), KeyIds(..), ) where @@ -16,5 +16,5 @@ import Utility.Gpg (KeyIds(..)) -- XXX ideally, this would be a locked memory region newtype Cipher = Cipher String -data EncryptedCipher = EncryptedCipher String KeyIds +data StorableCipher = EncryptedCipher String KeyIds | SharedCipher String deriving (Ord, Eq) diff --git a/Utility/Gpg.hs b/Utility/Gpg.hs index 4c798f273..ff6735ba5 100644 --- a/Utility/Gpg.hs +++ b/Utility/Gpg.hs @@ -94,7 +94,18 @@ findPubKeys for = KeyIds . parse <$> readStrict params pubKey = isPrefixOf "pub:" keyIdField s = split ":" s !! 4 - +{- Creates a block of high-quality random data suitable to use as a cipher. + - It is armored, to avoid newlines, since gpg only reads ciphers up to the + - first newline. -} +genRandom :: Int -> IO String +genRandom size = readStrict + [ Params "--gen-random --armor" + , Param $ show randomquality + , Param $ show size + ] + where + -- 1 is /dev/urandom; 2 is /dev/random + randomquality = 1 :: Int {- A test key. This is provided pre-generated since generating a new gpg - key is too much work (requires too much entropy) for a test suite to diff --git a/debian/changelog b/debian/changelog index 06990a0f8..7a9b5c6ea 100644 --- a/debian/changelog +++ b/debian/changelog @@ -7,6 +7,9 @@ git-annex (3.20120419) UNRELEASED; urgency=low settings, to allow custom headers to be sent with all HTTP requests. (Requested by the Internet Archive) * uninit: Clear annex.uuid from .git/config. Closes: #670639 + * Added shared cipher mode to encryptable special remotes. This option + avoids gpg key distribution, at the expense of flexability, and with + the requirement that all clones of the git repository be equally trusted. -- Joey Hess <joeyh@debian.org> Fri, 20 Apr 2012 16:14:08 -0400 diff --git a/doc/encryption.mdwn b/doc/encryption.mdwn index 0f83bb7f9..cc61fea6f 100644 --- a/doc/encryption.mdwn +++ b/doc/encryption.mdwn @@ -33,3 +33,16 @@ Note that once a key has been given access to a remote, it's not possible to revoke that access, short of deleting the remote. See [[encryption_design|design/encryption]] for other security risks associated with encryption. + +## shared cipher mode + +Alternatively, you can configure git-annex to use a shared cipher to +encrypt data stored in a remote. This shared cipher is stored, +**unencrypted** in the git repository. So it's shared amoung every +clone of the git repository. The advantage is you don't need to set up gpg +keys. The disadvantage is that this is **insecure** unless you +trust every clone of the git repository with access to the encrypted data +stored in the special remote. + +To use shared encryption, specify "encryption=shared" when first setting +up a special remote. |