assistant: When generating a gpg secret key, avoid hardcoding the key algorithm and size

This aims to future-proof gpg key generation. OpenPGP is in flux with a
conflict over standards ongoing. It seems not unlikely that different
systems will have different gpg commands that support different algorithms.

This also simplifies the code by using the --quick-gen-key interface rather
than the experimental batch interface. It seems less likely that
--quick-gen-key will break than an experimental interface (whose
documentation I can no longer find).

--quick-gen-key is supported since gpg 2.1.0 (2014).

Sponsored-by: Graham Spencer on Patreon
This commit is contained in:
Joey Hess 2024-01-09 15:31:53 -04:00
parent d37dbd62b8
commit de6a297d36
No known key found for this signature in database
GPG key ID: DB12DB0FF05F8F38
4 changed files with 21 additions and 39 deletions

View file

@ -54,7 +54,7 @@ withNewSecretKey :: (KeyId -> Handler Html) -> Handler Html
withNewSecretKey use = do withNewSecretKey use = do
cmd <- liftAnnex $ gpgCmd <$> Annex.getGitConfig cmd <- liftAnnex $ gpgCmd <$> Annex.getGitConfig
userid <- liftIO $ newUserId cmd userid <- liftIO $ newUserId cmd
liftIO $ genSecretKey cmd RSA "" userid maxRecommendedKeySize liftIO $ genSecretKey cmd "" userid
results <- M.keys . M.filter (== userid) <$> liftIO (secretKeys cmd) results <- M.keys . M.filter (== userid) <$> liftIO (secretKeys cmd)
case results of case results of
[] -> giveup "Failed to generate gpg key!" [] -> giveup "Failed to generate gpg key!"

View file

@ -2,6 +2,8 @@ git-annex (10.20231228) UNRELEASED; urgency=medium
* info: Added "annex sizes of repositories" table to the overall display. * info: Added "annex sizes of repositories" table to the overall display.
* import: Sped up import from special remotes. * import: Sped up import from special remotes.
* assistant: When generating a gpg secret key, avoid hardcoding the
key algorithm and size.
-- Joey Hess <id@joeyh.name> Fri, 29 Dec 2023 11:52:06 -0400 -- Joey Hess <id@joeyh.name> Fri, 29 Dec 2023 11:52:06 -0400

View file

@ -23,8 +23,6 @@ module Utility.Gpg (
findPubKeys, findPubKeys,
UserId, UserId,
secretKeys, secretKeys,
KeyType(..),
maxRecommendedKeySize,
genSecretKey, genSecretKey,
genRandom, genRandom,
testKeyId, testKeyId,
@ -60,6 +58,8 @@ newtype KeyIds = KeyIds { keyIds :: [KeyId] }
newtype GpgCmd = GpgCmd { unGpgCmd :: String } newtype GpgCmd = GpgCmd { unGpgCmd :: String }
type Passphrase = B.ByteString
{- Get gpg command to use, Just what's specified or, if a specific gpg {- Get gpg command to use, Just what's specified or, if a specific gpg
- command was found at configure time, use it, or otherwise, "gpg". -} - command was found at configure time, use it, or otherwise, "gpg". -}
mkGpgCmd :: Maybe FilePath -> GpgCmd mkGpgCmd :: Maybe FilePath -> GpgCmd
@ -157,7 +157,7 @@ pipeStrict' (GpgCmd cmd) params environ input = do
- the passphrase. - the passphrase.
- -
- Note that the reader must fully consume gpg's input before returning. -} - Note that the reader must fully consume gpg's input before returning. -}
feedRead :: (MonadIO m, MonadMask m) => GpgCmd -> [CommandParam] -> B.ByteString -> (Handle -> IO ()) -> (Handle -> m a) -> m a feedRead :: (MonadIO m, MonadMask m) => GpgCmd -> [CommandParam] -> Passphrase -> (Handle -> IO ()) -> (Handle -> m a) -> m a
feedRead cmd params passphrase feeder reader = do feedRead cmd params passphrase feeder reader = do
#ifndef mingw32_HOST_OS #ifndef mingw32_HOST_OS
let setup = liftIO $ do let setup = liftIO $ do
@ -260,49 +260,29 @@ secretKeys cmd = catchDefaultIO M.empty makemap
extract c k (_:rest) = extract c k (_:rest) =
extract c k rest extract c k rest
type Passphrase = String
type Size = Int
data KeyType = Algo Int | DSA | RSA
{- The maximum key size that gpg currently offers in its UI when
- making keys. -}
maxRecommendedKeySize :: Size
maxRecommendedKeySize = 4096
{- Generates a secret key using the experimental batch mode. {- Generates a secret key using the experimental batch mode.
- The key is added to the secret key ring. - The key is added to the secret key ring.
- Can take a very long time, depending on system entropy levels. - Can take a very long time, depending on system entropy levels.
-} -}
genSecretKey :: GpgCmd -> KeyType -> Passphrase -> UserId -> Size -> IO () genSecretKey :: GpgCmd -> Passphrase -> UserId -> IO ()
genSecretKey (GpgCmd cmd) keytype passphrase userid keysize = genSecretKey gpgcmd passphrase userid =
let p = (proc cmd params) feedRead gpgcmd params passphrase feeder reader
{ std_in = CreatePipe }
in withCreateProcess p (go p)
where where
params = ["--batch", "--gen-key"] params =
[ Param "--batch"
go p (Just h) _ _ pid = do , Param "--quick-gen-key"
hPutStr h $ unlines $ catMaybes , Param userid
[ Just $ "Key-Type: " ++ , Param "default" -- algo
case keytype of , Param "default" -- usage
DSA -> "DSA" , Param "never" -- expire
RSA -> "RSA" ]
Algo n -> show n feeder = hClose
, Just $ "Key-Length: " ++ show keysize reader = void . hGetContents
, Just $ "Name-Real: " ++ userid
, Just "Expire-Date: 0"
, if null passphrase
then Nothing
else Just $ "Passphrase: " ++ passphrase
]
hClose h
forceSuccessProcess p pid
go _ _ _ _ _ = error "internal"
{- Creates a block of high-quality random data suitable to use as a cipher. {- 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 - It is armored, to avoid newlines, since gpg only reads ciphers up to the
- first newline. -} - first newline. -}
genRandom :: GpgCmd -> Bool -> Size -> IO B.ByteString genRandom :: GpgCmd -> Bool -> Int -> IO B.ByteString
genRandom cmd highQuality size = do genRandom cmd highQuality size = do
s <- readStrict cmd params s <- readStrict cmd params
checksize s checksize s

View file

@ -5,7 +5,7 @@
<div .modal-header> <div .modal-header>
<h3> <h3>
<img src="@{StaticR activityicon_gif}" alt=""> # <img src="@{StaticR activityicon_gif}" alt=""> #
Generating a #{maxRecommendedKeySize} bit GnuPg key. Generating a GnuPg key.
<div .modal-body> <div .modal-body>
<p> <p>
Generating a GnuPg key can take a long time. To speed up the process, # Generating a GnuPg key can take a long time. To speed up the process, #