split out Utility.Gpg with the generic gpg interface, from Crypto
This commit is contained in:
		
					parent
					
						
							
								bb84f6e4bd
							
						
					
				
			
			
				commit
				
					
						c11cfea355
					
				
			
		
					 3 changed files with 105 additions and 81 deletions
				
			
		
							
								
								
									
										84
									
								
								Crypto.hs
									
										
									
									
									
								
							
							
						
						
									
										84
									
								
								Crypto.hs
									
										
									
									
									
								
							|  | @ -30,14 +30,10 @@ import qualified Data.ByteString.Lazy.Char8 as L | ||||||
| import qualified Data.Map as M | import qualified Data.Map as M | ||||||
| import Data.ByteString.Lazy.UTF8 (fromString) | import Data.ByteString.Lazy.UTF8 (fromString) | ||||||
| import Data.Digest.Pure.SHA | import Data.Digest.Pure.SHA | ||||||
| import System.Posix.Types |  | ||||||
| import Control.Applicative | import Control.Applicative | ||||||
| import Control.Concurrent |  | ||||||
| import Control.Exception (finally) |  | ||||||
| import System.Exit |  | ||||||
| import System.Environment |  | ||||||
| 
 | 
 | ||||||
| import Common.Annex | import Common.Annex | ||||||
|  | import qualified Utility.Gpg as Gpg | ||||||
| import Types.Key | import Types.Key | ||||||
| import Types.Remote | import Types.Remote | ||||||
| import Utility.Base64 | import Utility.Base64 | ||||||
|  | @ -71,7 +67,7 @@ genCipher c = do | ||||||
| 	random <- genrandom | 	random <- genrandom | ||||||
| 	encryptCipher (Cipher random) ks | 	encryptCipher (Cipher random) ks | ||||||
| 	where | 	where | ||||||
| 		genrandom = gpgReadStrict | 		genrandom = Gpg.readStrict | ||||||
| 			-- Armor the random data, to avoid newlines, | 			-- Armor the random data, to avoid newlines, | ||||||
| 			-- since gpg only reads ciphers up to the first | 			-- since gpg only reads ciphers up to the first | ||||||
| 			-- newline. | 			-- newline. | ||||||
|  | @ -119,7 +115,7 @@ extractCipher c = | ||||||
| encryptCipher :: Cipher -> KeyIds -> IO EncryptedCipher | encryptCipher :: Cipher -> KeyIds -> IO EncryptedCipher | ||||||
| encryptCipher (Cipher c) (KeyIds ks) = do | encryptCipher (Cipher c) (KeyIds ks) = do | ||||||
| 	let ks' = nub $ sort ks -- gpg complains about duplicate recipient keyids | 	let ks' = nub $ sort ks -- gpg complains about duplicate recipient keyids | ||||||
| 	encipher <- gpgPipeStrict (encrypt++recipients ks') c | 	encipher <- Gpg.pipeStrict (encrypt++recipients ks') c | ||||||
| 	return $ EncryptedCipher encipher (KeyIds ks') | 	return $ EncryptedCipher encipher (KeyIds ks') | ||||||
| 	where | 	where | ||||||
| 		encrypt = [ Params "--encrypt" ] | 		encrypt = [ Params "--encrypt" ] | ||||||
|  | @ -132,7 +128,7 @@ encryptCipher (Cipher c) (KeyIds ks) = do | ||||||
| {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} | {- Decrypting an EncryptedCipher is expensive; the Cipher should be cached. -} | ||||||
| decryptCipher :: RemoteConfig -> EncryptedCipher -> IO Cipher | decryptCipher :: RemoteConfig -> EncryptedCipher -> IO Cipher | ||||||
| decryptCipher _ (EncryptedCipher encipher _) =  | decryptCipher _ (EncryptedCipher encipher _) =  | ||||||
| 	Cipher <$> gpgPipeStrict decrypt encipher | 	Cipher <$> Gpg.pipeStrict decrypt encipher | ||||||
| 	where | 	where | ||||||
| 		decrypt = [ Param "--decrypt" ] | 		decrypt = [ Param "--decrypt" ] | ||||||
| 
 | 
 | ||||||
|  | @ -150,12 +146,12 @@ encryptKey c k = Key | ||||||
| {- Runs an action, passing it a handle from which it can  | {- Runs an action, passing it a handle from which it can  | ||||||
|  - stream encrypted content. -} |  - stream encrypted content. -} | ||||||
| withEncryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a | withEncryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a | ||||||
| withEncryptedHandle = gpgPassphraseHandle [Params "--symmetric --force-mdc"] . cipherPassphrase | withEncryptedHandle = Gpg.passphraseHandle [Params "--symmetric --force-mdc"] . cipherPassphrase | ||||||
| 
 | 
 | ||||||
| {- Runs an action, passing it a handle from which it can | {- Runs an action, passing it a handle from which it can | ||||||
|  - stream decrypted content. -} |  - stream decrypted content. -} | ||||||
| withDecryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a | withDecryptedHandle :: Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a | ||||||
| withDecryptedHandle = gpgPassphraseHandle [Param "--decrypt"] . cipherPassphrase | withDecryptedHandle = Gpg.passphraseHandle [Param "--decrypt"] . cipherPassphrase | ||||||
| 
 | 
 | ||||||
| {- Streams encrypted content to an action. -} | {- Streams encrypted content to an action. -} | ||||||
| withEncryptedContent :: Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a | withEncryptedContent :: Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a | ||||||
|  | @ -169,74 +165,8 @@ pass :: (Cipher -> IO L.ByteString -> (Handle -> IO a) -> IO a) | ||||||
|       -> Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a |       -> Cipher -> IO L.ByteString -> (L.ByteString -> IO a) -> IO a | ||||||
| pass to c i a = to c i $ \h -> a =<< L.hGetContents h | pass to c i a = to c i $ \h -> a =<< L.hGetContents h | ||||||
| 
 | 
 | ||||||
| gpgParams :: [CommandParam] -> IO [String] |  | ||||||
| gpgParams params = do |  | ||||||
| 	-- Enable batch mode if GPG_AGENT_INFO is set, to avoid extraneous |  | ||||||
| 	-- gpg output about password prompts. |  | ||||||
| 	e <- catchDefaultIO (getEnv "GPG_AGENT_INFO") "" |  | ||||||
| 	let batch = if null e then [] else ["--batch"] |  | ||||||
| 	return $ batch ++ defaults ++ toCommand params |  | ||||||
| 	where |  | ||||||
| 		-- be quiet, even about checking the trustdb |  | ||||||
| 		defaults = ["--quiet", "--trust-model", "always"] |  | ||||||
| 
 |  | ||||||
| {- Runs gpg with some params and returns its stdout, strictly. -} |  | ||||||
| gpgReadStrict :: [CommandParam] -> IO String |  | ||||||
| gpgReadStrict params = do |  | ||||||
| 	params' <- gpgParams params |  | ||||||
| 	pOpen ReadFromPipe "gpg" params' hGetContentsStrict |  | ||||||
| 
 |  | ||||||
| {- Runs gpg, piping an input value to it, and returninging its stdout, |  | ||||||
|  - strictly. -} |  | ||||||
| gpgPipeStrict :: [CommandParam] -> String -> IO String |  | ||||||
| gpgPipeStrict params input = do |  | ||||||
| 	params' <- gpgParams params |  | ||||||
| 	(pid, fromh, toh) <- hPipeBoth "gpg" params' |  | ||||||
| 	_ <- forkIO $ finally (hPutStr toh input) (hClose toh) |  | ||||||
| 	output <- hGetContentsStrict fromh |  | ||||||
| 	forceSuccess pid |  | ||||||
| 	return output |  | ||||||
| 
 |  | ||||||
| {- Runs gpg with some parameters, first feeding it a passphrase via |  | ||||||
|  - --passphrase-fd, then feeding it an input, and passing a handle |  | ||||||
|  - to its output to an action. |  | ||||||
|  - |  | ||||||
|  - Note that to avoid deadlock with the cleanup stage, |  | ||||||
|  - the action must fully consume gpg's input before returning. -} |  | ||||||
| gpgPassphraseHandle :: [CommandParam] -> String -> IO L.ByteString -> (Handle -> IO a) -> IO a |  | ||||||
| gpgPassphraseHandle params passphrase a b = do |  | ||||||
| 	-- pipe the passphrase into gpg on a fd |  | ||||||
| 	(frompipe, topipe) <- createPipe |  | ||||||
| 	_ <- forkIO $ do |  | ||||||
| 		toh <- fdToHandle topipe |  | ||||||
| 		hPutStrLn toh passphrase |  | ||||||
| 		hClose toh |  | ||||||
| 	let Fd pfd = frompipe |  | ||||||
| 	let passphrasefd = [Param "--passphrase-fd", Param $ show pfd] |  | ||||||
| 
 |  | ||||||
| 	params' <- gpgParams $ passphrasefd ++ params |  | ||||||
| 	(pid, fromh, toh) <- hPipeBoth "gpg" params' |  | ||||||
| 	pid2 <- forkProcess $ do |  | ||||||
| 		L.hPut toh =<< a |  | ||||||
| 		hClose toh |  | ||||||
| 		exitSuccess |  | ||||||
| 	hClose toh |  | ||||||
| 	ret <- b fromh |  | ||||||
| 
 |  | ||||||
| 	-- cleanup |  | ||||||
| 	forceSuccess pid |  | ||||||
| 	_ <- getProcessStatus True False pid2 |  | ||||||
| 	closeFd frompipe |  | ||||||
| 	return ret |  | ||||||
| 
 |  | ||||||
| configKeyIds :: RemoteConfig -> IO KeyIds | configKeyIds :: RemoteConfig -> IO KeyIds | ||||||
| configKeyIds c = parse <$> gpgReadStrict params | configKeyIds c = Gpg.findPubKeys $ configGet c "encryption" | ||||||
| 	where |  | ||||||
| 		params = [Params "--with-colons --list-public-keys", |  | ||||||
| 			Param $ configGet c "encryption"] |  | ||||||
| 		parse = KeyIds . map keyIdField . filter pubKey . lines |  | ||||||
| 		pubKey = isPrefixOf "pub:" |  | ||||||
| 		keyIdField s = split ":" s !! 4 |  | ||||||
| 
 | 
 | ||||||
| configGet :: RemoteConfig -> String -> String | configGet :: RemoteConfig -> String -> String | ||||||
| configGet c key = fromMaybe missing $ M.lookup key c | configGet c key = fromMaybe missing $ M.lookup key c | ||||||
|  |  | ||||||
|  | @ -5,13 +5,16 @@ | ||||||
|  - Licensed under the GNU GPL version 3 or higher. |  - Licensed under the GNU GPL version 3 or higher. | ||||||
|  -} |  -} | ||||||
| 
 | 
 | ||||||
| module Types.Crypto where | module Types.Crypto ( | ||||||
|  | 	Cipher(..), | ||||||
|  | 	EncryptedCipher(..), | ||||||
|  | 	KeyIds(..), | ||||||
|  | ) where | ||||||
|  | 
 | ||||||
|  | import Utility.Gpg (KeyIds(..)) | ||||||
| 
 | 
 | ||||||
| -- XXX ideally, this would be a locked memory region | -- XXX ideally, this would be a locked memory region | ||||||
| newtype Cipher = Cipher String | newtype Cipher = Cipher String | ||||||
| 
 | 
 | ||||||
| data EncryptedCipher = EncryptedCipher String KeyIds | data EncryptedCipher = EncryptedCipher String KeyIds | ||||||
| 	deriving (Ord, Eq) | 	deriving (Ord, Eq) | ||||||
| 
 |  | ||||||
| newtype KeyIds = KeyIds [String] |  | ||||||
| 	deriving (Ord, Eq) |  | ||||||
|  |  | ||||||
							
								
								
									
										91
									
								
								Utility/Gpg.hs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								Utility/Gpg.hs
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,91 @@ | ||||||
|  | {- gpg interface | ||||||
|  |  - | ||||||
|  |  - Copyright 2011 Joey Hess <joey@kitenet.net> | ||||||
|  |  - | ||||||
|  |  - Licensed under the GNU GPL version 3 or higher. | ||||||
|  |  -} | ||||||
|  | 
 | ||||||
|  | module Utility.Gpg where | ||||||
|  | 
 | ||||||
|  | import qualified Data.ByteString.Lazy.Char8 as L | ||||||
|  | import System.Posix.Types | ||||||
|  | import Control.Applicative | ||||||
|  | import Control.Concurrent | ||||||
|  | import Control.Exception (finally) | ||||||
|  | import System.Exit | ||||||
|  | import System.Environment | ||||||
|  | 
 | ||||||
|  | import Common | ||||||
|  | 
 | ||||||
|  | newtype KeyIds = KeyIds [String] | ||||||
|  |         deriving (Ord, Eq) | ||||||
|  | 
 | ||||||
|  | stdParams :: [CommandParam] -> IO [String] | ||||||
|  | stdParams params = do | ||||||
|  | 	-- Enable batch mode if GPG_AGENT_INFO is set, to avoid extraneous | ||||||
|  | 	-- gpg output about password prompts. | ||||||
|  | 	e <- catchDefaultIO (getEnv "GPG_AGENT_INFO") "" | ||||||
|  | 	let batch = if null e then [] else ["--batch"] | ||||||
|  | 	return $ batch ++ defaults ++ toCommand params | ||||||
|  | 	where | ||||||
|  | 		-- be quiet, even about checking the trustdb | ||||||
|  | 		defaults = ["--quiet", "--trust-model", "always"] | ||||||
|  | 
 | ||||||
|  | {- Runs gpg with some params and returns its stdout, strictly. -} | ||||||
|  | readStrict :: [CommandParam] -> IO String | ||||||
|  | readStrict params = do | ||||||
|  | 	params' <- stdParams params | ||||||
|  | 	pOpen ReadFromPipe "gpg" params' hGetContentsStrict | ||||||
|  | 
 | ||||||
|  | {- Runs gpg, piping an input value to it, and returninging its stdout, | ||||||
|  |  - strictly. -} | ||||||
|  | pipeStrict :: [CommandParam] -> String -> IO String | ||||||
|  | pipeStrict params input = do | ||||||
|  | 	params' <- stdParams params | ||||||
|  | 	(pid, fromh, toh) <- hPipeBoth "gpg" params' | ||||||
|  | 	_ <- forkIO $ finally (hPutStr toh input) (hClose toh) | ||||||
|  | 	output <- hGetContentsStrict fromh | ||||||
|  | 	forceSuccess pid | ||||||
|  | 	return output | ||||||
|  | 
 | ||||||
|  | {- Runs gpg with some parameters, first feeding it a passphrase via | ||||||
|  |  - --passphrase-fd, then feeding it an input, and passing a handle | ||||||
|  |  - to its output to an action. | ||||||
|  |  - | ||||||
|  |  - Note that to avoid deadlock with the cleanup stage, | ||||||
|  |  - the action must fully consume gpg's input before returning. -} | ||||||
|  | passphraseHandle :: [CommandParam] -> String -> IO L.ByteString -> (Handle -> IO a) -> IO a | ||||||
|  | passphraseHandle params passphrase a b = do | ||||||
|  | 	-- pipe the passphrase into gpg on a fd | ||||||
|  | 	(frompipe, topipe) <- createPipe | ||||||
|  | 	_ <- forkIO $ do | ||||||
|  | 		toh <- fdToHandle topipe | ||||||
|  | 		hPutStrLn toh passphrase | ||||||
|  | 		hClose toh | ||||||
|  | 	let Fd pfd = frompipe | ||||||
|  | 	let passphrasefd = [Param "--passphrase-fd", Param $ show pfd] | ||||||
|  | 
 | ||||||
|  | 	params' <- stdParams $ passphrasefd ++ params | ||||||
|  | 	(pid, fromh, toh) <- hPipeBoth "gpg" params' | ||||||
|  | 	pid2 <- forkProcess $ do | ||||||
|  | 		L.hPut toh =<< a | ||||||
|  | 		hClose toh | ||||||
|  | 		exitSuccess | ||||||
|  | 	hClose toh | ||||||
|  | 	ret <- b fromh | ||||||
|  | 
 | ||||||
|  | 	-- cleanup | ||||||
|  | 	forceSuccess pid | ||||||
|  | 	_ <- getProcessStatus True False pid2 | ||||||
|  | 	closeFd frompipe | ||||||
|  | 	return ret | ||||||
|  | 
 | ||||||
|  | {- Finds gpg public keys matching some string. (Could be an email address, | ||||||
|  |  - a key id, or a name. -} | ||||||
|  | findPubKeys :: String -> IO KeyIds | ||||||
|  | findPubKeys for = KeyIds . parse <$> readStrict params | ||||||
|  | 	where | ||||||
|  | 		params = [Params "--with-colons --list-public-keys", Param for] | ||||||
|  | 		parse = map keyIdField . filter pubKey . lines | ||||||
|  | 		pubKey = isPrefixOf "pub:" | ||||||
|  | 		keyIdField s = split ":" s !! 4 | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Joey Hess
				Joey Hess