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 =
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"

View file

@ -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)

View file

@ -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"
]

View file

@ -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.

View file

@ -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.

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
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

View file

@ -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:
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.
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 ...]