-- hkt.hs: hOpenPGP key tool -- Copyright © 2013-2014 Clint Adams -- -- vim: softtabstop=4:shiftwidth=4:expandtab -- -- This program is free software: you can redistribute it and/or modify -- it under the terms of the GNU Affero General Public License as -- published by the Free Software Foundation, either version 3 of the -- License, or (at your option) any later version. -- -- This program is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU Affero General Public License for more details. -- -- You should have received a copy of the GNU Affero General Public License -- along with this program. If not, see . import HOpenPGP.Tools.Common (banner, versioner, warranty, keyMatchesFingerprint, keyMatchesEightOctetKeyId, keyMatchesUIDSubString) import Codec.Encryption.OpenPGP.Fingerprint (eightOctetKeyID) import Codec.Encryption.OpenPGP.KeyInfo (keySize, pkalgoAbbrev) import Codec.Encryption.OpenPGP.KeySelection (parseEightOctetKeyId, parseFingerprint) import Codec.Encryption.OpenPGP.Serialize () import Codec.Encryption.OpenPGP.Types import Control.Applicative ((<$>),(<*>), optional, (<|>)) import Control.Lens ((^.)) import Control.Monad.Trans.Writer.Lazy (execWriter, tell) import qualified Data.ByteString as B import Data.Conduit (($=),($$), runResourceT) import qualified Data.Conduit.Binary as CB import Data.Conduit.Cereal (conduitGet) import qualified Data.Conduit.List as CL import Data.Conduit.OpenPGP.Keyring (conduitToTKsDropping) import Data.Foldable (traverse_) import Data.Maybe (fromMaybe) import Data.Monoid ((<>)) import Data.Serialize (get, put, runPut) import qualified Data.Text as T import System.Directory (getHomeDirectory) import Options.Applicative.Builder (argument, command, footer, header, help, info, long, metavar, prefs, progDesc, showHelpOnError, str, strOption, subparser) import Options.Applicative.Extra (customExecParser, helper) import Options.Applicative.Types (Parser) import System.IO (Handle, hFlush, hPutStrLn, stderr, hSetBuffering, BufferMode(..)) grabMatchingKeys :: FilePath -> String -> IO [TK] grabMatchingKeys fp srch = runResourceT $ CB.sourceFile fp $= conduitGet get $= conduitToTKsDropping $= CL.filter matchAny $$ CL.consume where matchAny tk = either (const False) id $ fmap (keyMatchesFingerprint True tk) efp <|> fmap (keyMatchesEightOctetKeyId True tk) eeok <|> return (keyMatchesUIDSubString srch tk) efp = parseFingerprint . T.pack $ srch eeok = parseEightOctetKeyId . T.pack $ srch showKey :: TK -> IO () showKey key = putStrLn . unlines . execWriter $ do tell [ "pub " ++ show (keySize (key^.tkPKP^.pubkey)) ++ pkalgoAbbrev (key^.tkPKP^.pkalgo) ++ "/0x" ++ (show . eightOctetKeyID $ key^.tkPKP ) ] tell $ map (\(x,_) -> "uid " ++ x) (key^.tkUIDs) tell $ map (\(PublicSubkeyPkt x,_,_) -> "sub " ++ show (keySize (x^.pubkey)) ++ pkalgoAbbrev (x^.pkalgo) ++ "/0x" ++ (show . eightOctetKeyID $ x)) (key^.tkSubs) data Options = Options { keyring :: String , target :: String } data Command = List Options | ExportPubkeys Options listO :: String -> Parser Options listO homedir = Options <$> (fromMaybe (homedir ++ "/.gnupg/pubring.gpg") <$> optional (strOption ( long "keyring" <> metavar "FILE" <> help "file containing keyring" ))) <*> argument str ( metavar "TARGET" ) dispatch :: Command -> IO () dispatch (List o) = banner' stderr >> hFlush stderr >> doList o dispatch (ExportPubkeys o) = banner' stderr >> hFlush stderr >> doExportPubkeys o main :: IO () main = do hSetBuffering stderr LineBuffering homedir <- getHomeDirectory customExecParser (prefs showHelpOnError) (info (helper <*> versioner <*> cmd homedir) (header (banner "hkt") <> progDesc "hOpenPGP Keyring Tool" <> footer (warranty "hot"))) >>= dispatch cmd :: String -> Parser Command cmd homedir = subparser ( command "list" (info ( List <$> listO homedir) ( progDesc "list matching keys" )) <> command "export-pubkeys" (info ( ExportPubkeys <$> listO homedir) ( progDesc "export matching keys to stdout" ))) banner' :: Handle -> IO () banner' h = hPutStrLn h (banner "hkt" ++ "\n" ++ warranty "hkt") doList :: Options -> IO () doList o = do keys <- grabMatchingKeys (keyring o) (target o) mapM_ showKey keys doExportPubkeys :: Options -> IO () doExportPubkeys o = do keys <- grabMatchingKeys (keyring o) (target o) mapM_ (B.putStr . putTK') keys where putTK' key = runPut $ do put (PublicKey (_tkPKP key)) mapM_ (put . Signature) (_tkRevs key) mapM_ putUid' (_tkUIDs key) mapM_ putUat' (_tkUAts key) mapM_ putSub' (_tkSubs key) putUid' (u, sps) = put (UserId u) >> mapM_ (put . Signature) sps putUat' (us, sps) = put (UserAttribute us) >> mapM_ (put . Signature) sps putSub' (p, sp, msp) = put p >> (put . Signature) sp >> traverse_ (put . Signature) msp