summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2012-04-29 14:02:18 -0400
committerGravatar Joey Hess <joey@kitenet.net>2012-04-29 14:02:43 -0400
commit1c16f616df9a8469d24cefb6007333df3a35a449 (patch)
tree2198232da7d7594d1a8d952724497c08ee22d2ce
parentd7a4a9a66bd51b18a9e5f4427c3492db1adec40d (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.hs2
-rw-r--r--Crypto.hs77
-rw-r--r--Remote/Helper/Encryptable.hs25
-rw-r--r--Types/Crypto.hs6
-rw-r--r--Utility/Gpg.hs13
-rw-r--r--debian/changelog3
-rw-r--r--doc/encryption.mdwn13
7 files changed, 80 insertions, 59 deletions
diff --git a/Annex.hs b/Annex.hs
index 441e460be..f2fb1f401 100644
--- a/Annex.hs
+++ b/Annex.hs
@@ -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
diff --git a/Crypto.hs b/Crypto.hs
index cb1ca40d1..e530bd0e6 100644
--- a/Crypto.hs
+++ b/Crypto.hs
@@ -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.