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:
parent
484daf5b39
commit
53ce59021a
7 changed files with 83 additions and 45 deletions
21
Crypto.hs
21
Crypto.hs
|
@ -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"
|
||||||
|
|
|
@ -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)
|
||||||
|
|
2
Test.hs
2
Test.hs
|
@ -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"
|
||||||
]
|
]
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 ...]
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue