summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar guilhem <guilhem@fripost.org>2013-08-28 04:24:14 +0200
committerGravatar Joey Hess <joey@kitenet.net>2013-08-29 14:31:33 -0400
commitd9fcbfa495a981ce0afc0d66560bd90eff8559bf (patch)
treefe2acb468ac8e2c70726a0153bb52b4044c9ec68
parentc0a39909829a131e4216b2f6021430fcbdad30b4 (diff)
Allow revocation of OpenPGP keys.
/!\ It is to be noted that revoking a key does NOT necessarily prevent the owner of its private part from accessing data on the remote /!\ The only sound use of `keyid-=` is probably to replace a (sub-)key by another, where the private part of both is owned by the same person/entity: git annex enableremote myremote keyid-=2512E3C7 keyid+=788A3F4C Reference: http://git-annex.branchable.com/bugs/Using_a_revoked_GPG_key/ * Other change introduced by this patch: New keys now need to be added with option `keyid+=`, and the scheme specified (upon initremote only) with `encryption=`. The motivation for this change is to open for new schemes, e.g., strict asymmetric encryption. git annex initremote myremote encryption=hybrid keyid=2512E3C7 git annex enableremote myremote keyid+=788A3F4C
-rw-r--r--Crypto.hs21
-rw-r--r--Remote/Helper/Encryptable.hs42
-rw-r--r--Test.hs2
-rw-r--r--Utility/Gpg.hs2
-rw-r--r--doc/design/encryption.mdwn12
-rw-r--r--doc/encryption.mdwn24
-rw-r--r--doc/git-annex.mdwn27
7 files changed, 84 insertions, 46 deletions
diff --git a/Crypto.hs b/Crypto.hs
index 21b1ae41b..a86f9f976 100644
--- a/Crypto.hs
+++ b/Crypto.hs
@@ -78,15 +78,22 @@ genSharedCipher :: Bool -> IO StorableCipher
genSharedCipher highQuality =
SharedCipher <$> Gpg.genRandom highQuality 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
+{- Updates an existing Cipher, re-encrypting it to add or remove keyids,
+ - depending on whether the first component is True or False. -}
+updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
+updateEncryptedCipher _ SharedCipher{} = undefined
+updateEncryptedCipher [] encipher = return encipher
+updateEncryptedCipher newkeys encipher@(EncryptedCipher _ (KeyIds ks)) = do
+ dropKeys <- listKeyIds [ k | (False, k) <- newkeys ]
+ forM_ dropKeys $ \k -> unless (k `elem` ks) $
+ error $ "Key " ++ k ++ " is not granted access."
+ addKeys <- listKeyIds [ k | (True, k) <- newkeys ]
+ let ks' = (addKeys ++ ks) \\ dropKeys
+ when (null ks') $ error "The new access list would become empty."
cipher <- decryptCipher encipher
- encryptCipher cipher (merge ks ks')
+ encryptCipher cipher $ KeyIds ks'
where
- merge (KeyIds a) (KeyIds b) = KeyIds $ a ++ b
+ listKeyIds = mapM (Gpg.findPubKeys >=*> keyIds) >=*> concat
describeCipher :: StorableCipher -> String
describeCipher (SharedCipher _) = "shared cipher"
diff --git a/Remote/Helper/Encryptable.hs b/Remote/Helper/Encryptable.hs
index 22e1c9fc0..63efcb378 100644
--- a/Remote/Helper/Encryptable.hs
+++ b/Remote/Helper/Encryptable.hs
@@ -23,27 +23,37 @@ import Utility.Metered
- 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
- =<< highRandomQuality
- (Just keyid, Nothing) -> use "encryption setup" . genEncryptedCipher keyid
- =<< highRandomQuality
- (Just keyid, Just v) -> use "encryption update" $ updateEncryptedCipher keyid v
+encryptionSetup c = maybe genCipher updateCipher $ extractCipher c
where
- cannotchange = error "Cannot change encryption type of existing remote."
+ -- The type of encryption
+ encryption = M.lookup "encryption" c
+ -- Generate a new cipher, depending on the chosen encryption scheme
+ genCipher = case encryption of
+ Just "none" -> return c
+ Just "shared" -> use "encryption setup" . genSharedCipher
+ =<< highRandomQuality
+ -- hybrid encryption by default
+ _ | maybe True (== "hybrid") encryption ->
+ use "encryption setup" . genEncryptedCipher key
+ =<< highRandomQuality
+ _ -> error "Specify encryption=none or encryption=shared or encryption=hybrid (default)."
+ key = fromMaybe (error "Specifiy keyid=...") $ M.lookup "keyid" c
+ newkeys = maybe [] (\k -> [(True,k)]) (M.lookup "keyid+" c) ++
+ maybe [] (\k -> [(False,k)]) (M.lookup "keyid-" c)
+ -- Update an existing cipher if possible.
+ updateCipher v
+ | isJust encryption = error "Cannot set encryption type of existing remote."
+ | otherwise = case v of
+ SharedCipher{} -> return c
+ EncryptedCipher{} ->
+ use "encryption update" $ updateEncryptedCipher newkeys v
use m a = do
showNote m
cipher <- liftIO a
showNote $ describeCipher cipher
- return $ M.delete "encryption" $ M.delete "highRandomQuality" $
- storeCipher c cipher
+ return $ flip storeCipher cipher $ foldr M.delete c
+ [ "keyid", "keyid+", "keyid-"
+ , "encryption", "highRandomQuality" ]
highRandomQuality =
(&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c)
<$> fmap not (Annex.getState Annex.fast)
diff --git a/Test.hs b/Test.hs
index 3eb330c22..b7b80f914 100644
--- a/Test.hs
+++ b/Test.hs
@@ -880,7 +880,7 @@ test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path
let a cmd = git_annex env cmd
[ "foo"
, "type=directory"
- , "encryption=" ++ Utility.Gpg.testKeyId
+ , "keyid=" ++ Utility.Gpg.testKeyId
, "directory=dir"
, "highRandomQuality=false"
]
diff --git a/Utility/Gpg.hs b/Utility/Gpg.hs
index 81180148e..291b06e1c 100644
--- a/Utility/Gpg.hs
+++ b/Utility/Gpg.hs
@@ -24,7 +24,7 @@ import Utility.Env
import Utility.Tmp
#endif
-newtype KeyIds = KeyIds [String]
+newtype KeyIds = KeyIds { keyIds :: [String] }
deriving (Ord, Eq)
{- If a specific gpg command was found at configure time, use it.
diff --git a/doc/design/encryption.mdwn b/doc/design/encryption.mdwn
index 6a380abe1..377de476e 100644
--- a/doc/design/encryption.mdwn
+++ b/doc/design/encryption.mdwn
@@ -103,14 +103,16 @@ use the special remote.
## risks
-A risk of this scheme is that, once the symmetric cipher has been obtained, it
-allows full access to all the encrypted content. This scheme does not allow
-revoking a given gpg key access to the cipher, since anyone with such a key
-could have already decrypted the cipher and stored a copy.
+A risk of this scheme is that, once the symmetric cipher has been
+obtained, it allows full access to all the encrypted content. Indeed
+anyone owning a key that used to be granted access could already have
+decrypted the cipher and stored a copy. While it is in possible to
+revoke a key with `keyid-=`, it is designed for a
+[[completely_different_purpose|encryption]].
If git-annex stores the decrypted symmetric cipher in memory, then there
is a risk that it could be intercepted from there by an attacker. Gpg
-amelorates these type of risks by using locked memory. For git-annex, note
+ameliorates these type of risks by using locked memory. For git-annex, note
that an attacker with local machine access can tell at least all the
filenames and metadata of files stored in the encrypted remote anyway,
and can access whatever content is stored locally.
diff --git a/doc/encryption.mdwn b/doc/encryption.mdwn
index d93bee9d2..6463827af 100644
--- a/doc/encryption.mdwn
+++ b/doc/encryption.mdwn
@@ -6,8 +6,9 @@ Encryption is needed when using [[special_remotes]] like Amazon S3, where
file content is sent to an untrusted party who does not have access to the
git repository.
-Such an encrypted remote uses strong GPG encryption on the contents of files,
-as well as HMAC hashing of the filenames. The size of the encrypted files,
+Such an encrypted remote uses strong
+[[symmetric_encryptiondesign/encryption]] on the contents of files, as
+well as HMAC hashing of the filenames. The size of the encrypted files,
and access patterns of the data, should be the only clues to what is
stored in such a remote.
@@ -34,18 +35,25 @@ to access content that has already been stored in the special remote.
To add a new key, just run `git annex enableremote` specifying the
new encryption key:
- git annex enableremote myremote encryption=788A3F4C
+ git annex enableremote myremote keyid+=788A3F4C
-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.
+While a key can later be removed from the list, it is to be noted that
+it does **not** necessarily prevent the owner of the private material
+from accessing data on the remote (which is by design impossible, short
+of deleting the remote). In fact the only sound use of `keyid-=` is
+probably to replace a (sub-)key by another, where the private part of
+both is owned by the same person/entity:
+
+ git annex enableremote myremote keyid-=2512E3C7 keyid+=788A3F4C
+
+See also [[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
+**unencrypted** in the git repository. So it's shared among 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
diff --git a/doc/git-annex.mdwn b/doc/git-annex.mdwn
index 7cac9087d..832a3cd68 100644
--- a/doc/git-annex.mdwn
+++ b/doc/git-annex.mdwn
@@ -312,10 +312,11 @@ subdirectories).
(or encryption=emailaddress) to specify a gpg key that can access
the encrypted special remote.
- Note that with encryption enabled, a gpg key is created. This requires
- sufficient entropy. If initremote seems to hang or take a long time
- while generating the key, you may want to ctrl-c it and re-run with --fast,
- which causes it to use a lower-quality source of randomness.
+ Note that with encryption enabled, a cryptographic key is created.
+ This requires sufficient entropy. If initremote seems to hang or take
+ a long time while generating the key, you may want to ctrl-c it and
+ re-run with --fast, which causes it to use a lower-quality source of
+ randomness.
Example Amazon S3 remote:
@@ -336,10 +337,20 @@ subdirectories).
This command can also be used to modify the configuration of an existing
special remote, by specifying new values for parameters that were originally
- set when using initremote. For example, to add a new gpg key to the keys
- that can access an encrypted remote:
-
- git annex enableremote mys3 encryption=friend@example.com
+ set when using initremote. With the exception of some configuration values such
+ as the encryption scheme scheme, which cannot be changed once the
+ remote has been created.
+
+ If encryption is enabled and the remote's access limited to one or
+ more OpenPGP key(s), it is possible to give access to another key ID
+ by specifing the keyid+= parameter. While a key can later be removed
+ from the list, it is to be noted that it does NOT necessarily prevent
+ the owner of the private material from accessing data on the remote
+ (which is by design impossible, short of deleting the remote);
+ however, a fine use-case of keyid-= is to replace a revoked key by
+ a new one superseeding it:
+
+ git annex enableremote mys3 keyid-=revokedkey keyid+=newkey
* trust [repository ...]