Generate ciphers with a better entropy.

Unless highRandomQuality=false (or --fast) is set, use Libgcypt's
'GCRY_VERY_STRONG_RANDOM' level by default for cipher generation, like
it's done for OpenPGP key generation.

On the assistant side, the random quality is left to the old (lower)
level, in order not to scare the user with an enless page load due to
the blocking PRNG waiting for IO actions.
This commit is contained in:
guilhem 2013-04-05 21:06:16 +02:00 committed by Joey Hess
parent 602baae12e
commit 00fc21bfec
6 changed files with 41 additions and 19 deletions

View file

@ -69,11 +69,15 @@ makeRsyncRemote name location = makeRemote name location $
, ("type", "rsync")
]
{- Inits a special remote. -}
{- Inits a special remote. Currently, only 'weak' ciphers can be
- generated from the assistant, because otherwise GnuPG may block once
- the entropy pool is drained, and as of now there's no way to tell the
- user to perform IO actions to refill the pool. -}
makeSpecialRemote :: String -> RemoteType -> R.RemoteConfig -> Annex ()
makeSpecialRemote name remotetype config = do
(u, c) <- Command.InitRemote.findByName name
c' <- R.setup remotetype u $ M.union config c
c' <- R.setup remotetype u $
M.insert "highRandomQuality" "false" $ M.union config c
describeUUID u name
configSet u c'

View file

@ -67,15 +67,16 @@ cipherMac :: Cipher -> String
cipherMac (Cipher c) = take cipherBeginning c
{- Creates a new Cipher, encrypted to the specified key id. -}
genEncryptedCipher :: String -> IO StorableCipher
genEncryptedCipher keyid = do
genEncryptedCipher :: String -> Bool -> IO StorableCipher
genEncryptedCipher keyid highQuality = do
ks <- Gpg.findPubKeys keyid
random <- Gpg.genRandom cipherSize
random <- Gpg.genRandom highQuality cipherSize
encryptCipher (Cipher random) ks
{- Creates a new, shared Cipher. -}
genSharedCipher :: IO StorableCipher
genSharedCipher = SharedCipher <$> Gpg.genRandom cipherSize
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

View file

@ -31,15 +31,20 @@ encryptionSetup c = case (M.lookup "encryption" c, extractCipher c) of
(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 "shared", Nothing) -> use "encryption setup" . genSharedCipher
=<< highRandomQuality
(Just keyid, Nothing) -> use "encryption setup" . genEncryptedCipher keyid
=<< highRandomQuality
(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
return $ M.delete "encryption" $ storeCipher c cipher
return $ M.delete "encryption" $ M.delete "highRandomQuality" $
storeCipher c cipher
highRandomQuality = (&&) (maybe True (/="false") (M.lookup "highRandomQuality" c))
<$> fmap not (Annex.getState Annex.fast)
{- Modifies a Remote to support encryption.
-

View file

@ -735,6 +735,7 @@ test_crypto = "git-annex crypto" ~: intmpclonerepo $ when Build.SysConfig.gpg $
, "type=directory"
, "encryption=" ++ Utility.Gpg.testKeyId
, "directory=dir"
, "highRandomQuality=false"
]
initremote @? "initremote failed"
initremote @? "initremote failed when run twice in a row"

View file

@ -85,7 +85,8 @@ feedRead params passphrase feeder reader = do
reader from
{- Finds gpg public keys matching some string. (Could be an email address,
- a key id, or a name. -}
- a key id, or a name; See the section 'HOW TO SPECIFY A USER ID' of
- GnuPG's manpage.) -}
findPubKeys :: String -> IO KeyIds
findPubKeys for = KeyIds . parse <$> readStrict params
where
@ -97,8 +98,8 @@ findPubKeys for = KeyIds . parse <$> readStrict params
{- 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 = checksize <$> readStrict
genRandom :: Bool -> Int -> IO String
genRandom highQuality size = checksize <$> readStrict
[ Params params
, Param $ show randomquality
, Param $ show size
@ -106,8 +107,13 @@ genRandom size = checksize <$> readStrict
where
params = "--gen-random --armor"
-- 1 is /dev/urandom; 2 is /dev/random
randomquality = 1 :: Int
-- See http://www.gnupg.org/documentation/manuals/gcrypt/Quality-of-random-numbers.html
-- for the meaning of random quality levels.
-- The highest available is 2, which is the default for OpenPGP
-- key generation; Note that it uses the blocking PRNG /dev/random
-- on the Linux kernel, hence the running time may take a while.
randomquality :: Int
randomquality = if highQuality then 2 else 1
{- The size is the number of bytes of entropy desired; the data is
- base64 encoded, so needs 8 bits to represent every 6 bytes of

View file

@ -23,10 +23,15 @@ The basis of this scheme was originally developed by Lars Wirzenius et al
[for Obnam](http://liw.fi/obnam/encryption/).
"""]]
Data is encrypted by gpg, using a symmetric cipher.
The cipher is itself checked into your git repository, encrypted using one or
more gpg public keys. This scheme allows new gpg private keys to be given
access to content that has already been stored in the remote.
Data is encrypted by GnuPG, using a symmetric cipher. The cipher is
generated by GnuPG when the special remote is created. By default the
best entropy pool is used, hence the generation may take a while; One
can use `initremote` with `highRandomQuality=false` or `--fast` options
to speed up things, but at the expense of using random numbers of a
lower quality. The generated cipher is then checked into your git
repository, encrypted using one or more OpenPGP public keys. This scheme
allows new OpenPGP private keys to be given access to content that has
already been stored in the remote.
Different encrypted remotes need to be able to each use different ciphers.
Allowing multiple ciphers to be used within a single remote would add a lot