hopenpgp-tools-0.25.3.1: HOpenPGP/Tools/Common/Common.hs
-- Common.hs: hOpenPGP-tools common functions
-- Copyright © 2012-2026 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.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
, spGetSCT
, maybeR
, renderKeyID
, renderFingerprint
, primaryPKP
) where
import Codec.Encryption.OpenPGP.Fingerprint
( eightOctetKeyID
, fingerprint
)
import Codec.Encryption.OpenPGP.KeyInfo (pubkeySize)
import Codec.Encryption.OpenPGP.SignatureQualities (sigCT)
import Codec.Encryption.OpenPGP.Types
import Control.Error.Util (hush)
import Control.Monad.Trans.Reader
( Reader
, ReaderT
, ask
, local
, reader
, runReader
, withReader
)
import Data.Binary (put)
import Data.Binary.Put (runPut)
import qualified Data.ByteString.Lazy as BL
-- hmm --
import Data.Maybe (fromMaybe, mapMaybe)
import Data.Text (Text)
import qualified Data.Text as T
import Data.Version (showVersion)
import Options.Applicative.Builder
( auto
, help
, hidden
, infoOption
, long
, short
)
import Options.Applicative.Types (Parser, ReadM (..))
import Prettyprinter
( Doc
, defaultLayoutOptions
, hardline
, layoutPretty
, pretty
, (<+>)
)
import qualified Prettyprinter.Render.Text as PPA
import Paths_hopenpgp_tools (version)
banner :: String -> Doc ann
{-# INLINE banner #-}
banner name =
pretty name
<+> pretty "(hopenpgp-tools)"
<+> pretty (showVersion version)
<> hardline
<> pretty "Copyright (C) 2012-2026 Clint Adams"
warranty :: String -> Doc ann
{-# INLINE warranty #-}
warranty name =
pretty name
<+> pretty "comes with ABSOLUTELY NO WARRANTY."
<+> pretty
"This is free software, and you are welcome to redistribute it"
<+> pretty "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 -> SomeTK -> Fingerprint -> Bool
keyMatchesFingerprint = keyMatchesPKPred fingerprint
keyMatchesEightOctetKeyId
:: Bool -> SomeTK -> Either String EightOctetKeyId -> Bool -- FIXME: refactor this somehow
keyMatchesEightOctetKeyId = keyMatchesPKPred eightOctetKeyID
keyMatchesExactUIDString :: Text -> SomeTK -> Bool
keyMatchesExactUIDString uidstr = elem uidstr . map fst . _tkUIDs . someTKToPublicViewTK
keyMatchesUIDSubString :: Text -> SomeTK -> Bool
keyMatchesUIDSubString uidstr stk =
any (T.toLower uidstr `T.isInfixOf`)
. map (T.toLower . fst)
. _tkUIDs $
someTKToPublicViewTK stk
keyMatchesPKPred
:: Eq a => (SomePKPayload -> a) -> Bool -> SomeTK -> a -> Bool
keyMatchesPKPred p False = (==) . p . primaryPKP
keyMatchesPKPred p True = \stk v -> elem v (p (primaryPKP stk) : map p (tkGetSubs stk))
primaryPKP :: SomeTK -> SomePKPayload
primaryPKP = keyPktPKPayload . _tkPrimaryKey . someTKToPublicViewTK
-- The following should probably be moved elsewhere
tkUsingPKP :: Reader SomePKPayload a -> Reader SomeTK a
tkUsingPKP = withReader primaryPKP
pkpGetPKVersion :: SomePKPayload -> Integer
pkpGetPKVersion t =
if _keyVersion t == DeprecatedV3
then 3
else 4
pkpGetPKAlgo :: SomePKPayload -> Integer
pkpGetPKAlgo = fromIntegral . fromFVal . _pkalgo
pkpGetKeysize :: SomePKPayload -> Integer
pkpGetKeysize = fromIntegral . fromMaybe 0 . hush . pubkeySize . _pubkey
pkpGetTimestamp :: SomePKPayload -> Integer
pkpGetTimestamp = fromIntegral . _timestamp
pkpGetFingerprint :: SomePKPayload -> Fingerprint
pkpGetFingerprint = fingerprint
pkpGetEOKI :: SomePKPayload -> String
pkpGetEOKI = either (const "UNKNOWN") show . eightOctetKeyID
tkGetUIDs :: SomeTK -> [Text]
tkGetUIDs = map fst . _tkUIDs . someTKToPublicViewTK
tkGetSubs :: SomeTK -> [SomePKPayload]
tkGetSubs stk = mapMaybe (grabPKP . fst) (_tkSubs (someTKToPublicViewTK stk))
where
grabPKP kp = Just (keyPktPKPayload kp)
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 SigV6 {} = 6
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
spGetSCT :: Pkt -> Maybe Integer
spGetSCT (SignaturePkt s) = fmap fromIntegral (sigCT s)
spGetSCT _ = Nothing
pUsingPKP :: Reader (Maybe SomePKPayload) 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))
renderKeyID :: EightOctetKeyId -> String
renderKeyID =
T.unpack
. PPA.renderStrict
. layoutPretty defaultLayoutOptions
. pretty
renderFingerprint :: Fingerprint -> String
renderFingerprint =
T.unpack
. PPA.renderStrict
. layoutPretty defaultLayoutOptions
. pretty