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
This commit is contained in:
guilhem 2013-08-28 04:24:14 +02:00 committed by Joey Hess
parent 484daf5b39
commit 53ce59021a
7 changed files with 83 additions and 45 deletions

View file

@ -78,15 +78,22 @@ genSharedCipher :: Bool -> IO StorableCipher
genSharedCipher highQuality = genSharedCipher highQuality =
SharedCipher <$> Gpg.genRandom highQuality cipherSize SharedCipher <$> Gpg.genRandom highQuality cipherSize
{- Updates an existing Cipher, re-encrypting it to add a keyid. -} {- Updates an existing Cipher, re-encrypting it to add or remove keyids,
updateEncryptedCipher :: String -> StorableCipher -> IO StorableCipher - depending on whether the first component is True or False. -}
updateEncryptedCipher _ (SharedCipher _) = undefined updateEncryptedCipher :: [(Bool, String)] -> StorableCipher -> IO StorableCipher
updateEncryptedCipher keyid encipher@(EncryptedCipher _ ks) = do updateEncryptedCipher _ SharedCipher{} = undefined
ks' <- Gpg.findPubKeys keyid 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 cipher <- decryptCipher encipher
encryptCipher cipher (merge ks ks') encryptCipher cipher $ KeyIds ks'
where where
merge (KeyIds a) (KeyIds b) = KeyIds $ a ++ b listKeyIds = mapM (Gpg.findPubKeys >=*> keyIds) >=*> concat
describeCipher :: StorableCipher -> String describeCipher :: StorableCipher -> String
describeCipher (SharedCipher _) = "shared cipher" describeCipher (SharedCipher _) = "shared cipher"

View file

@ -23,27 +23,37 @@ import Utility.Metered
- updated to be accessible to an additional encryption key. Or the user - updated to be accessible to an additional encryption key. Or the user
- could opt to use a shared cipher, which is stored unencrypted. -} - could opt to use a shared cipher, which is stored unencrypted. -}
encryptionSetup :: RemoteConfig -> Annex RemoteConfig encryptionSetup :: RemoteConfig -> Annex RemoteConfig
encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of encryptionSetup c = maybe genCipher updateCipher $ extractCipher c
(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
where 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 use m a = do
showNote m showNote m
cipher <- liftIO a cipher <- liftIO a
showNote $ describeCipher cipher showNote $ describeCipher cipher
return $ M.delete "encryption" $ M.delete "highRandomQuality" $ return $ flip storeCipher cipher $ foldr M.delete c
storeCipher c cipher [ "keyid", "keyid+", "keyid-"
, "encryption", "highRandomQuality" ]
highRandomQuality = highRandomQuality =
(&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c) (&&) (maybe True ( /= "false") $ M.lookup "highRandomQuality" c)
<$> fmap not (Annex.getState Annex.fast) <$> fmap not (Annex.getState Annex.fast)

View file

@ -880,7 +880,7 @@ test_crypto env = "git-annex crypto" ~: intmpclonerepo env $ whenM (Utility.Path
let a cmd = git_annex env cmd let a cmd = git_annex env cmd
[ "foo" [ "foo"
, "type=directory" , "type=directory"
, "encryption=" ++ Utility.Gpg.testKeyId , "keyid=" ++ Utility.Gpg.testKeyId
, "directory=dir" , "directory=dir"
, "highRandomQuality=false" , "highRandomQuality=false"
] ]

View file

@ -24,7 +24,7 @@ import Utility.Env
import Utility.Tmp import Utility.Tmp
#endif #endif
newtype KeyIds = KeyIds [String] newtype KeyIds = KeyIds { keyIds :: [String] }
deriving (Ord, Eq) deriving (Ord, Eq)
{- If a specific gpg command was found at configure time, use it. {- If a specific gpg command was found at configure time, use it.

View file

@ -103,14 +103,16 @@ use the special remote.
## risks ## risks
A risk of this scheme is that, once the symmetric cipher has been obtained, it A risk of this scheme is that, once the symmetric cipher has been
allows full access to all the encrypted content. This scheme does not allow obtained, it allows full access to all the encrypted content. Indeed
revoking a given gpg key access to the cipher, since anyone with such a key anyone owning a key that used to be granted access could already have
could have already decrypted the cipher and stored a copy. 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 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 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 that an attacker with local machine access can tell at least all the
filenames and metadata of files stored in the encrypted remote anyway, filenames and metadata of files stored in the encrypted remote anyway,
and can access whatever content is stored locally. and can access whatever content is stored locally.

View file

@ -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 file content is sent to an untrusted party who does not have access to the
git repository. git repository.
Such an encrypted remote uses strong GPG encryption on the contents of files, Such an encrypted remote uses strong
as well as HMAC hashing of the filenames. The size of the encrypted files, [[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 and access patterns of the data, should be the only clues to what is
stored in such a remote. 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 To add a new key, just run `git annex enableremote` specifying the
new encryption key: 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 While a key can later be removed from the list, it is to be noted that
possible to revoke that access, short of deleting the remote. See it does **not** necessarily prevent the owner of the private material
[[encryption_design|design/encryption]] for other security risks from accessing data on the remote (which is by design impossible, short
associated with encryption. 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 ## shared cipher mode
Alternatively, you can configure git-annex to use a shared cipher to Alternatively, you can configure git-annex to use a shared cipher to
encrypt data stored in a remote. This shared cipher is stored, 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 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 keys. The disadvantage is that this is **insecure** unless you
trust every clone of the git repository with access to the encrypted data trust every clone of the git repository with access to the encrypted data

View file

@ -312,10 +312,11 @@ subdirectories).
(or encryption=emailaddress) to specify a gpg key that can access (or encryption=emailaddress) to specify a gpg key that can access
the encrypted special remote. the encrypted special remote.
Note that with encryption enabled, a gpg key is created. This requires Note that with encryption enabled, a cryptographic key is created.
sufficient entropy. If initremote seems to hang or take a long time This requires sufficient entropy. If initremote seems to hang or take
while generating the key, you may want to ctrl-c it and re-run with --fast, a long time while generating the key, you may want to ctrl-c it and
which causes it to use a lower-quality source of randomness. re-run with --fast, which causes it to use a lower-quality source of
randomness.
Example Amazon S3 remote: Example Amazon S3 remote:
@ -336,10 +337,20 @@ subdirectories).
This command can also be used to modify the configuration of an existing This command can also be used to modify the configuration of an existing
special remote, by specifying new values for parameters that were originally 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 set when using initremote. With the exception of some configuration values such
that can access an encrypted remote: as the encryption scheme scheme, which cannot be changed once the
remote has been created.
git annex enableremote mys3 encryption=friend@example.com 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 ...] * trust [repository ...]