hopenpgp-tools-0.21: HOpenPGP/Tools/Common.hs
-- Common.hs: hOpenPGP-tools common functions
-- Copyright © 2012-2018 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 <http://www.gnu.org/licenses/>.
module HOpenPGP.Tools.Common (
banner
, versioner
, warranty
, prependAuto
, keyMatchesFingerprint
, keyMatchesEightOctetKeyId
, keyMatchesExactUIDString
, keyMatchesUIDSubString
, keyMatchesPKPred
-- hmm
, pkpGetPKVersion
, pkpGetPKAlgo
, pkpGetKeysize
, pkpGetTimestamp
, pkpGetFingerprint
, pkpGetEOKI
, tkUsingPKP
, pUsingPKP
, pUsingSP
, tkGetUIDs
, tkGetSubs
, anyOrAll
, anyReader
, oGetTag
, oGetLength
, spGetSigVersion
, spGetSigType
, spGetPKAlgo
, spGetHashAlgo
, maybeR
) where
import Paths_hopenpgp_tools (version)
import Data.Version (showVersion)
import Codec.Encryption.OpenPGP.Fingerprint (eightOctetKeyID, fingerprint)
import Codec.Encryption.OpenPGP.Types
import Control.Lens ((^..))
import Data.Binary (put)
import Data.Binary.Put (runPut)
import qualified Data.ByteString.Lazy as BL
import Data.Data.Lens (biplate)
import Data.Monoid ((<>))
import Data.Text (Text)
import qualified Data.Text as T
import Options.Applicative.Builder (auto, help, hidden, infoOption, long, short)
import Options.Applicative.Types (Parser, ReadM(..))
import Text.PrettyPrint.ANSI.Leijen (Doc, (<+>), hardline, text)
-- hmm --
import Data.Maybe (fromMaybe, mapMaybe)
import Codec.Encryption.OpenPGP.KeyInfo (pubkeySize)
import Control.Error.Util (hush)
import Control.Monad.Trans.Reader (ask, withReader, reader, Reader, runReader, ReaderT, local)
banner :: String -> Doc
{-# INLINE banner #-}
banner name = text name <+> text "(hopenpgp-tools)" <+> text (showVersion version) <> hardline
<> text "Copyright (C) 2012-2018 Clint Adams"
warranty :: String -> Doc
{-# INLINE warranty #-}
warranty name = text name <+> text "comes with ABSOLUTELY NO WARRANTY." <+>
text "This is free software, and you are welcome to redistribute it" <+>
text "under certain conditions."
versioner :: String -> Parser (a -> a)
{-# INLINE versioner #-}
versioner name = infoOption (name ++ " (hopenpgp-tools) " ++ showVersion version) $
long "version"
<> short 'V'
<> help "Show version information"
<> hidden
prependAuto :: Read a => String -> ReadM a
prependAuto s = ReadM (local (s++) (unReadM auto))
keyMatchesFingerprint :: Bool -> TK -> TwentyOctetFingerprint -> Bool
keyMatchesFingerprint = keyMatchesPKPred fingerprint
keyMatchesEightOctetKeyId :: Bool -> TK -> Either String EightOctetKeyId -> Bool -- FIXME: refactor this somehow
keyMatchesEightOctetKeyId = keyMatchesPKPred eightOctetKeyID
keyMatchesExactUIDString :: Text -> TK -> Bool
keyMatchesExactUIDString uidstr = elem uidstr . map fst . _tkUIDs
keyMatchesUIDSubString :: Text -> TK -> Bool
keyMatchesUIDSubString uidstr = any (T.toLower uidstr `T.isInfixOf`) . map (T.toLower . fst) . _tkUIDs
keyMatchesPKPred :: Eq a => (PKPayload -> a) -> Bool -> TK -> a -> Bool
keyMatchesPKPred p False = (==) . p . fst . _tkKey
keyMatchesPKPred p True = \tk v -> elem v (map p (tk ^.. biplate))
-- The following should probably be moved elsewhere
tkUsingPKP :: Reader PKPayload a -> Reader TK a
tkUsingPKP = withReader (fst . _tkKey)
pkpGetPKVersion :: PKPayload -> Integer
pkpGetPKVersion t = if _keyVersion t == DeprecatedV3 then 3 else 4
pkpGetPKAlgo :: PKPayload -> Integer
pkpGetPKAlgo = fromIntegral . fromFVal . _pkalgo
pkpGetKeysize :: PKPayload -> Integer
pkpGetKeysize = fromIntegral . fromMaybe 0 . hush . pubkeySize . _pubkey
pkpGetTimestamp :: PKPayload -> Integer
pkpGetTimestamp = fromIntegral . _timestamp
pkpGetFingerprint :: PKPayload -> TwentyOctetFingerprint
pkpGetFingerprint = fingerprint
pkpGetEOKI :: PKPayload -> String
pkpGetEOKI = either (const "UNKNOWN") show . eightOctetKeyID
tkGetUIDs :: TK -> [Text]
tkGetUIDs = map fst . _tkUIDs
tkGetSubs :: TK -> [PKPayload]
tkGetSubs = mapMaybe (grabPKP . fst) . _tkSubs
where
grabPKP (PublicSubkeyPkt p) = Just p
grabPKP (SecretSubkeyPkt p _) = Just p
grabPKP _ = Nothing
anyOrAll :: (Monad m, Monad m1) => ((a1 -> c) -> a -> ReaderT a m b) -> (m1 a1 -> c) -> ReaderT a m b
anyOrAll aa op = ask >>= aa (op . return)
anyReader :: Reader a Bool -> Reader [a] Bool
anyReader p = any (runReader p) `fmap` ask
oGetTag :: Pkt -> Integer
oGetTag = fromIntegral . pktTag
oGetLength :: Pkt -> Integer
oGetLength = fromIntegral . BL.length . runPut . put -- FIXME: this should be a length that makes sense
spGetSigVersion :: Pkt -> Maybe Integer
spGetSigVersion (SignaturePkt s) = Just (sigVersion s)
where
sigVersion SigV3{} = 3
sigVersion SigV4{} = 4
sigVersion (SigVOther v _) = fromIntegral v
spGetSigVersion _ = Nothing
spGetSigType :: Pkt -> Maybe Integer
spGetSigType (SignaturePkt s) = fmap (fromIntegral . fromFVal) (sigType s)
where -- FIXME: deduplicate this and hOpenPGP .Internal
sigType :: SignaturePayload -> Maybe SigType
sigType (SigV3 st _ _ _ _ _ _) = Just st
sigType (SigV4 st _ _ _ _ _ _) = Just st
sigType _ = Nothing -- this includes v2 sigs, which don't seem to be specified in the RFCs but exist in the wild
spGetSigType _ = Nothing
spGetPKAlgo :: Pkt -> Maybe Integer
spGetPKAlgo (SignaturePkt s) = fmap (fromIntegral . fromFVal) (sigPKA s)
where
sigPKA (SigV3 _ _ _ pka _ _ _) = Just pka
sigPKA (SigV4 _ pka _ _ _ _ _) = Just pka
sigPKA _ = Nothing -- this includes v2 sigs, which don't seem to be specified in the RFCs but exist in the wild
spGetPKAlgo _ = Nothing
spGetHashAlgo :: Pkt -> Maybe Integer
spGetHashAlgo (SignaturePkt s) = fmap (fromIntegral . fromFVal) (sigHA s)
where
sigHA (SigV3 _ _ _ _ ha _ _) = Just ha
sigHA (SigV4 _ _ ha _ _ _ _) = Just ha
sigHA _ = Nothing -- this includes v2 sigs, which don't seem to be specified in the RFCs but exist in the wild
spGetHashAlgo _ = Nothing
pUsingPKP :: Reader (Maybe PKPayload) a -> Reader Pkt a
pUsingPKP = withReader grabPayload
where
grabPayload (SecretKeyPkt p _) = Just p
grabPayload (PublicKeyPkt p) = Just p
grabPayload (SecretSubkeyPkt p _) = Just p
grabPayload (PublicSubkeyPkt p) = Just p
grabPayload _ = Nothing
pUsingSP :: Reader (Maybe SignaturePayload) a -> Reader Pkt a
pUsingSP = withReader grabPayload
where
grabPayload (SignaturePkt s ) = Just s
grabPayload _ = Nothing
maybeR :: a -> Reader r a -> Reader (Maybe r) a
maybeR x r = reader (maybe x (runReader r))