packages feed

hOpenPGP 0.6.3 → 0.7

raw patch · 7 files changed

+173/−222 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Codec.Encryption.OpenPGP.Types: Elgamal :: PubKeyAlgorithm
- Codec.Encryption.OpenPGP.Types: RSAEncryptOnly :: PubKeyAlgorithm
- Codec.Encryption.OpenPGP.Types: RSASignOnly :: PubKeyAlgorithm
- Codec.Encryption.OpenPGP.Verify: verify :: Keyring -> Pkt -> Maybe UTCTime -> ByteString -> Either String Verification
- Codec.Encryption.OpenPGP.Verify: verifySig :: Keyring -> Pkt -> PktStreamContext -> Maybe UTCTime -> Either String Verification
- Codec.Encryption.OpenPGP.Verify: verifyTK :: Keyring -> Maybe UTCTime -> TK -> Either String TK
+ Codec.Encryption.OpenPGP.Signatures: verify :: Keyring -> Pkt -> Maybe UTCTime -> ByteString -> Either String Verification
+ Codec.Encryption.OpenPGP.Signatures: verifySig :: Keyring -> Pkt -> PktStreamContext -> Maybe UTCTime -> Either String Verification
+ Codec.Encryption.OpenPGP.Signatures: verifyTK :: Keyring -> Maybe UTCTime -> TK -> Either String TK
+ Codec.Encryption.OpenPGP.Types: DeprecatedRSAEncryptOnly :: PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: DeprecatedRSASignOnly :: PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: FeatureOther :: Int -> FeatureFlag
+ Codec.Encryption.OpenPGP.Types: ForbiddenElgamal :: PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: HumanReadable :: NotationFlag
+ Codec.Encryption.OpenPGP.Types: KSPOther :: Int -> KSPFlag
+ Codec.Encryption.OpenPGP.Types: KeyMaterialCompromised :: RevocationCode
+ Codec.Encryption.OpenPGP.Types: KeyRetiredAndNoLongerUsed :: RevocationCode
+ Codec.Encryption.OpenPGP.Types: KeySuperseded :: RevocationCode
+ Codec.Encryption.OpenPGP.Types: ModificationDetection :: FeatureFlag
+ Codec.Encryption.OpenPGP.Types: NoModify :: KSPFlag
+ Codec.Encryption.OpenPGP.Types: NoReason :: RevocationCode
+ Codec.Encryption.OpenPGP.Types: OtherNF :: Int -> NotationFlag
+ Codec.Encryption.OpenPGP.Types: RClOther :: Int -> RevocationClass
+ Codec.Encryption.OpenPGP.Types: RCoOther :: Word8 -> RevocationCode
+ Codec.Encryption.OpenPGP.Types: SensitiveRK :: RevocationClass
+ Codec.Encryption.OpenPGP.Types: UserIdInfoNoLongerValid :: RevocationCode
+ Codec.Encryption.OpenPGP.Types: _sspCriticality :: SigSubPacket -> Bool
+ Codec.Encryption.OpenPGP.Types: _sspPayload :: SigSubPacket -> SigSubPacketPayload
+ Codec.Encryption.OpenPGP.Types: compressedDataCompressionAlgorithm :: Lens' CompressedData CompressionAlgorithm
+ Codec.Encryption.OpenPGP.Types: compressedDataPayload :: Lens' CompressedData CompressedDataPayload
+ Codec.Encryption.OpenPGP.Types: data FeatureFlag
+ Codec.Encryption.OpenPGP.Types: data KSPFlag
+ Codec.Encryption.OpenPGP.Types: data NotationFlag
+ Codec.Encryption.OpenPGP.Types: data RevocationClass
+ Codec.Encryption.OpenPGP.Types: data RevocationCode
+ Codec.Encryption.OpenPGP.Types: hexToW8s :: ReadS Word8
+ Codec.Encryption.OpenPGP.Types: keyVersion :: Lens' PKPayload KeyVersion
+ Codec.Encryption.OpenPGP.Types: literalDataDataType :: Lens' LiteralData DataType
+ Codec.Encryption.OpenPGP.Types: literalDataFileName :: Lens' LiteralData FileName
+ Codec.Encryption.OpenPGP.Types: literalDataPayload :: Lens' LiteralData ByteString
+ Codec.Encryption.OpenPGP.Types: literalDataTimeStamp :: Lens' LiteralData TimeStamp
+ Codec.Encryption.OpenPGP.Types: markerPayload :: Iso' Marker ByteString
+ Codec.Encryption.OpenPGP.Types: modificationDetectionCodePayload :: Iso' ModificationDetectionCode ByteString
+ Codec.Encryption.OpenPGP.Types: onePassSignatureEightOctetKeyId :: Lens' OnePassSignature EightOctetKeyId
+ Codec.Encryption.OpenPGP.Types: onePassSignatureHashAlgorithm :: Lens' OnePassSignature HashAlgorithm
+ Codec.Encryption.OpenPGP.Types: onePassSignatureNestedFlag :: Lens' OnePassSignature NestedFlag
+ Codec.Encryption.OpenPGP.Types: onePassSignaturePacketVersion :: Lens' OnePassSignature PacketVersion
+ Codec.Encryption.OpenPGP.Types: onePassSignaturePubKeyAlgorithm :: Lens' OnePassSignature PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: onePassSignatureSigType :: Lens' OnePassSignature SigType
+ Codec.Encryption.OpenPGP.Types: otherPacketPayload :: Lens' OtherPacket ByteString
+ Codec.Encryption.OpenPGP.Types: otherPacketType :: Lens' OtherPacket Word8
+ Codec.Encryption.OpenPGP.Types: packetCode :: Packet a => PacketType a -> Word8
+ Codec.Encryption.OpenPGP.Types: packetType :: Packet a => a -> PacketType a
+ Codec.Encryption.OpenPGP.Types: pkalgo :: Lens' PKPayload PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: pkeskEightOctetKeyId :: Lens' PKESK EightOctetKeyId
+ Codec.Encryption.OpenPGP.Types: pkeskMPIs :: Lens' PKESK [MPI]
+ Codec.Encryption.OpenPGP.Types: pkeskPacketVersion :: Lens' PKESK PacketVersion
+ Codec.Encryption.OpenPGP.Types: pkeskPubKeyAlgorithm :: Lens' PKESK PubKeyAlgorithm
+ Codec.Encryption.OpenPGP.Types: pubkey :: Lens' PKPayload PKey
+ Codec.Encryption.OpenPGP.Types: publicKeyPKPayload :: Iso' PublicKey PKPayload
+ Codec.Encryption.OpenPGP.Types: publicSubkeyPKPayload :: Iso' PublicSubkey PKPayload
+ Codec.Encryption.OpenPGP.Types: secretKeyPKPayload :: Lens' SecretKey PKPayload
+ Codec.Encryption.OpenPGP.Types: secretKeySKAddendum :: Lens' SecretKey SKAddendum
+ Codec.Encryption.OpenPGP.Types: secretSubkeyPKPayload :: Lens' SecretSubkey PKPayload
+ Codec.Encryption.OpenPGP.Types: secretSubkeySKAddendum :: Lens' SecretSubkey SKAddendum
+ Codec.Encryption.OpenPGP.Types: signaturePayload :: Iso' Signature SignaturePayload
+ Codec.Encryption.OpenPGP.Types: skeskMPIs :: Lens' SKESK [MPI]
+ Codec.Encryption.OpenPGP.Types: skeskPacketVersion :: Lens' SKESK PacketVersion
+ Codec.Encryption.OpenPGP.Types: skeskS2K :: Lens' SKESK S2K
+ Codec.Encryption.OpenPGP.Types: skeskSymmetricAlgorithm :: Lens' SKESK SymmetricAlgorithm
+ Codec.Encryption.OpenPGP.Types: symEncDataPayload :: Iso' SymEncData ByteString
+ Codec.Encryption.OpenPGP.Types: symEncIntegrityProtectedDataPacketVersion :: Lens' SymEncIntegrityProtectedData PacketVersion
+ Codec.Encryption.OpenPGP.Types: symEncIntegrityProtectedDataPayload :: Lens' SymEncIntegrityProtectedData ByteString
+ Codec.Encryption.OpenPGP.Types: timestamp :: Lens' PKPayload TimeStamp
+ Codec.Encryption.OpenPGP.Types: tkPKP :: Lens' TK PKPayload
+ Codec.Encryption.OpenPGP.Types: tkRevs :: Lens' TK [SignaturePayload]
+ Codec.Encryption.OpenPGP.Types: tkSubs :: Lens' TK [(Pkt, SignaturePayload, Maybe SignaturePayload)]
+ Codec.Encryption.OpenPGP.Types: tkUAts :: Lens' TK [([UserAttrSubPacket], [SignaturePayload])]
+ Codec.Encryption.OpenPGP.Types: tkUIDs :: Lens' TK [(String, [SignaturePayload])]
+ Codec.Encryption.OpenPGP.Types: tkmSKA :: Lens' TK (Maybe SKAddendum)
+ Codec.Encryption.OpenPGP.Types: trustPayload :: Iso' Trust ByteString
+ Codec.Encryption.OpenPGP.Types: type AlmostPublicDomainRegex = ByteString
+ Codec.Encryption.OpenPGP.Types: type CompressedDataPayload = ByteString
+ Codec.Encryption.OpenPGP.Types: type Count = Int
+ Codec.Encryption.OpenPGP.Types: type Exportability = Bool
+ Codec.Encryption.OpenPGP.Types: type FileName = ByteString
+ Codec.Encryption.OpenPGP.Types: type IV = ByteString
+ Codec.Encryption.OpenPGP.Types: type ImageData = ByteString
+ Codec.Encryption.OpenPGP.Types: type KeyServer = ByteString
+ Codec.Encryption.OpenPGP.Types: type NestedFlag = Bool
+ Codec.Encryption.OpenPGP.Types: type NotationName = ByteString
+ Codec.Encryption.OpenPGP.Types: type NotationValue = ByteString
+ Codec.Encryption.OpenPGP.Types: type PacketVersion = Word8
+ Codec.Encryption.OpenPGP.Types: type Revocability = Bool
+ Codec.Encryption.OpenPGP.Types: type RevocationReason = ByteString
+ Codec.Encryption.OpenPGP.Types: type Salt = ByteString
+ Codec.Encryption.OpenPGP.Types: type SignatureHash = ByteString
+ Codec.Encryption.OpenPGP.Types: type TimeStamp = Word32
+ Codec.Encryption.OpenPGP.Types: type TrustAmount = Word8
+ Codec.Encryption.OpenPGP.Types: type TrustLevel = Word8
+ Codec.Encryption.OpenPGP.Types: type URL = ByteString
+ Codec.Encryption.OpenPGP.Types: type V3Expiration = Word16
+ Codec.Encryption.OpenPGP.Types: userAttributeSubPackets :: Iso' UserAttribute [UserAttrSubPacket]
+ Codec.Encryption.OpenPGP.Types: userIdPayload :: Iso' UserId String
+ Codec.Encryption.OpenPGP.Types: v3exp :: Lens' PKPayload V3Expiration
+ Codec.Encryption.OpenPGP.Types: verificationSignature :: Lens' Verification SignaturePayload
+ Codec.Encryption.OpenPGP.Types: verificationSigner :: Lens' Verification PKPayload
+ Codec.Encryption.OpenPGP.Types: w8sToHex :: [Word8] -> String
- Codec.Encryption.OpenPGP.Types: class Packet a
+ Codec.Encryption.OpenPGP.Types: class Packet a where data family PacketType a :: *
- Codec.Encryption.OpenPGP.Types: sspCriticality :: SigSubPacket -> Bool
+ Codec.Encryption.OpenPGP.Types: sspCriticality :: Lens' SigSubPacket Bool
- Codec.Encryption.OpenPGP.Types: sspPayload :: SigSubPacket -> SigSubPacketPayload
+ Codec.Encryption.OpenPGP.Types: sspPayload :: Lens' SigSubPacket SigSubPacketPayload

Files

Codec/Encryption/OpenPGP/Serialize.hs view
@@ -8,6 +8,7 @@ ) where  import Control.Applicative ((<$>),(<*>))+import Control.Lens ((^.)) import Control.Monad (guard, mplus, replicateM) import qualified Crypto.PubKey.RSA as R import qualified Crypto.PubKey.DSA as D@@ -750,18 +751,18 @@ getPubkey RSA = do MPI n <- get                    MPI e <- get                    return $ RSAPubKey (R.PublicKey (B.length . integerToBEBS $ n) n e)-getPubkey RSAEncryptOnly = getPubkey RSA-getPubkey RSASignOnly = getPubkey RSA+getPubkey DeprecatedRSAEncryptOnly = getPubkey RSA+getPubkey DeprecatedRSASignOnly = getPubkey RSA getPubkey DSA = do MPI p <- get                    MPI q <- get                    MPI g <- get                    MPI y <- get                    return $ DSAPubKey (D.PublicKey (D.Params p g q) y)-getPubkey ElgamalEncryptOnly = getPubkey Elgamal-getPubkey Elgamal = do MPI p <- get-                       MPI g <- get-                       MPI y <- get-                       return $ ElGamalPubKey [p,g,y]+getPubkey ElgamalEncryptOnly = getPubkey ForbiddenElgamal+getPubkey ForbiddenElgamal = do MPI p <- get+                                MPI g <- get+                                MPI y <- get+                                return $ ElGamalPubKey [p,g,y] getPubkey t = fail ("Unsupported pubkey type " ++ show t)  putPubkey :: PKey -> Put@@ -769,7 +770,7 @@  getSecretKey :: PKPayload -> Get SKey getSecretKey pkp-    | _pkalgo pkp `elem` [RSA, RSAEncryptOnly, RSASignOnly] =+    | _pkalgo pkp `elem` [RSA, DeprecatedRSAEncryptOnly, DeprecatedRSASignOnly] =         do MPI d <- get            MPI p <- get            MPI q <- get@@ -778,11 +779,11 @@                dP = 0                dQ = 0                qinv = 0-               pub = (\(RSAPubKey x) -> x) (_pubkey pkp)+               pub = (\(RSAPubKey x) -> x) (pkp^.pubkey)            return $ RSAPrivateKey (R.PrivateKey pub d p q dP dQ qinv)     | _pkalgo pkp == DSA = do MPI x <- get                               return $ DSAPrivateKey (D.PrivateKey (D.Params 0 0 0) x)-    | _pkalgo pkp `elem` [ElgamalEncryptOnly,Elgamal] =+    | _pkalgo pkp `elem` [ElgamalEncryptOnly,ForbiddenElgamal] =         do MPI x <- get            return $ ElGamalPrivateKey [x] 
Codec/Encryption/OpenPGP/SerializeForSigs.hs view
@@ -15,6 +15,7 @@  , payloadForSig ) where +import Control.Lens ((^.)) import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC8@@ -100,7 +101,7 @@     putByteString bs  payloadForSig :: SigType -> PktStreamContext -> ByteString-payloadForSig BinarySig state = (\(LiteralDataPkt _ _ _ bs) -> bs) (lastLD state)+payloadForSig BinarySig state = (fromPkt (lastLD state))^.literalDataPayload payloadForSig CanonicalTextSig state = payloadForSig BinarySig state payloadForSig StandaloneSig _ = B.empty payloadForSig GenericCert state = kandUPayload (lastPrimaryKey state) (lastUIDorUAt state)
+ Codec/Encryption/OpenPGP/Signatures.hs view
@@ -0,0 +1,143 @@+-- Verify.hs: OpenPGP (RFC4880) signature verification+-- Copyright © 2012-2013  Clint Adams+-- This software is released under the terms of the ISC license.+-- (See the LICENSE file).++module Codec.Encryption.OpenPGP.Signatures (+   verifySig+ , verify+ , verifyTK+) where++import Control.Monad (guard, liftM2)++import Crypto.PubKey.HashDescr (HashDescr(..))+import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.RSA.PKCS15 as P15++import Control.Lens ((^.))+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Either (lefts, rights)+import Data.IxSet ((@=))+import qualified Data.IxSet as IxSet+import Data.Time.Clock (UTCTime(..), diffUTCTime)+import Data.Time.Clock.POSIX (posixSecondsToUTCTime)+import Data.Serialize.Put (runPut)++import Codec.Encryption.OpenPGP.Fingerprint (eightOctetKeyID, fingerprint)+import Codec.Encryption.OpenPGP.Internal (countBits, integerToBEBS, PktStreamContext(..), issuer, emptyPSC, hashDescr)+import Codec.Encryption.OpenPGP.SerializeForSigs (putPartialSigforSigning, putSigTrailer, payloadForSig)+import Codec.Encryption.OpenPGP.Types+import Data.Conduit.OpenPGP.Keyring.Instances ()++verifySig :: Keyring -> Pkt -> PktStreamContext -> Maybe UTCTime -> Either String Verification -- FIXME: check expiration here?+verifySig kr sig@(SignaturePkt (SigV4 st _ _ hs _ _ _)) state mt = do+    v <- verify kr sig mt (payloadForSig st state)+    _ <- mapM_ (checkIssuer (eightOctetKeyID (_verificationSigner v)) . _sspPayload) hs+    return v+    where+        checkIssuer :: EightOctetKeyId -> SigSubPacketPayload -> Either String Bool+        checkIssuer signer (Issuer i) = if signer == i then Right True else Left "issuer subpacket does not match"+        checkIssuer _ _ = Right True+verifySig _ _ _ _ = Left "This should never happen."++verifyTK :: Keyring -> Maybe UTCTime -> TK -> Either String TK+verifyTK kr mt key = do+    revokers <- checkRevokers key+    revs <- checkKeyRevocations revokers key+    let uids = filter (\(_, sps) -> sps /= []) . checkUidSigs $ _tkUIDs key -- FIXME: check revocations here?+    let uats = filter (\(_, sps) -> sps /= []) . checkUAtSigs $ _tkUAts key -- FIXME: check revocations here?+    let subs = concatMap checkSub $ _tkSubs key -- FIXME: check revocations here?+    return (TK (_tkPKP key) (_tkmSKA key) revs uids uats subs)+    where+        checkRevokers = Right . concat . rights . map verifyRevoker . filter isRevokerP . _tkRevs+        checkKeyRevocations :: [(PubKeyAlgorithm, TwentyOctetFingerprint)] -> TK -> Either String [SignaturePayload]+        checkKeyRevocations rs k = Prelude.sequence . concatMap (filterRevs rs) . rights . map (liftM2 fmap (,) (vSig kr)) . _tkRevs $ k+        checkUidSigs :: [(String, [SignaturePayload])] -> [(String, [SignaturePayload])]+        checkUidSigs = map (\(uid, sps) -> (uid, (rights . map (\sp -> fmap (const sp) (vUid kr (uid, sp)))) sps))+        checkUAtSigs :: [([UserAttrSubPacket], [SignaturePayload])] -> [([UserAttrSubPacket], [SignaturePayload])]+        checkUAtSigs = map (\(uat, sps) -> (uat, (rights . map (\sp -> fmap (const sp) (vUAt kr (uat, sp)))) sps))+        checkSub :: (Pkt, SignaturePayload, Maybe SignaturePayload) -> [(Pkt, SignaturePayload, Maybe SignaturePayload)]+        checkSub (pkt, sp, mrp) = if revokedSub pkt mrp then [] else checkSub' pkt sp+        revokedSub :: Pkt -> Maybe SignaturePayload -> Bool+        revokedSub _ Nothing = False+        revokedSub p (Just rp) = vSubSig kr p rp+        checkSub' :: Pkt -> SignaturePayload -> [(Pkt, SignaturePayload, Maybe SignaturePayload)]+        checkSub' p sp = guard (vSubSig kr p sp) >> return (p, sp, Nothing)+        getHasheds (SigV4 _ _ _ ha _ _ _) = ha+        getHasheds _ = []+	filterRevs :: [(PubKeyAlgorithm, TwentyOctetFingerprint)] -> (SignaturePayload, Verification) -> [Either String SignaturePayload]+	filterRevs vokers spv = case spv of+                                     (s@(SigV4 SignatureDirectlyOnAKey _ _ _ _ _ _), _) -> [Right s]+                                     (s@(SigV4 KeyRevocationSig pka _ _ _ _ _), v) -> if any (\(p,f) -> p == pka && f == fingerprint (_verificationSigner v)) vokers then [Left "Key revoked"] else [Right s]+				     _ -> []+        isKeyRevocation (SigV4 KeyRevocationSig _ _ _ _ _ _) = True+        isKeyRevocation _ = False+        isRevokerP (SigV4 SignatureDirectlyOnAKey _ _ h u _ _) = any isRevocationKeySSP h && any isIssuerSSP u+        isRevokerP _ = False+        isRevocationKeySSP (SigSubPacket _ (RevocationKey {})) = True+        isRevocationKeySSP _ = False+        isIssuerSSP (SigSubPacket _ (Issuer _)) = True+        isIssuerSSP _ = False+        vUid :: Keyring -> (String, SignaturePayload) -> Either String Verification+        vUid keyring (uid, sp) = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastUIDorUAt = UserIdPkt uid } mt+        vUAt :: Keyring -> ([UserAttrSubPacket], SignaturePayload) -> Either String Verification+        vUAt keyring (uat, sp) = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastUIDorUAt = UserAttributePkt uat } mt+        vSig :: Keyring -> SignaturePayload -> Either String Verification+        vSig keyring sp = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key) } mt+        vSubSig :: Keyring -> Pkt -> SignaturePayload -> Bool+        vSubSig keyring sk sp = case verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastSubkey = sk} mt of+                                Left _ -> False+				Right _ -> True+        verifyRevoker :: SignaturePayload -> Either String [(PubKeyAlgorithm, TwentyOctetFingerprint)]+        verifyRevoker sp = do+            _ <- vSig kr sp+            return (map (\(SigSubPacket _ (RevocationKey _ pka fp)) -> (pka, fp)) . filter isRevocationKeySSP $ getHasheds sp)++verify :: Keyring -> Pkt -> Maybe UTCTime -> ByteString -> Either String Verification+verify kr sig mt payload = do+    i <- maybe (Left "issuer not found") Right (issuer sig)+    potentialmatches <- if IxSet.null (kr @= i) then Left "pubkey not found" else Right (kr @= i)+    let allrelevantpkps = filter (\x -> issuer sig == Just (eightOctetKeyID x)) (concatMap (\x -> _tkPKP x:map subPKP (_tkSubs x)) (IxSet.toList potentialmatches))+    let results = map (\pkp -> verify' sig pkp (hashalgo sig) (finalPayload sig payload)) allrelevantpkps+    case rights results of+        [] -> Left (concatMap (++"/") (lefts results))+        [r] -> do _ <- isSignatureExpired sig mt+	          return (Verification r ((_signaturePayload . fromPkt) sig)) -- FIXME: this should also check expiration time and flags of the signing key+        _ -> Left "multiple successes; unexpected condition"+    where+        subPKP (pack, _, _) = subPKP' pack+        subPKP' (PublicSubkeyPkt p) = p+        subPKP' (SecretSubkeyPkt p _) = p+        verify' (SignaturePkt s) (pub@(PKPayload V4 _ _ _ pkey)) ha pl = verify'' (pkaAndMPIs s) ha pub pkey pl+        verify' _ _ _ _ = error "This should never happen."+        verify'' (DSA,mpis) ha pub (DSAPubKey pkey) bs = verify''' (dsaVerify mpis ha pkey bs) pub+        verify'' (RSA,mpis) ha pub (RSAPubKey pkey) bs = verify''' (rsaVerify mpis ha pkey bs) pub+        verify'' _ _ _ _ _ = Left "unimplemented key type"+	verify''' f pub = if f then Right pub else Left "verification failed"+	dsaVerify mpis ha pkey bs = DSA.verify (dsaTruncate pkey . hashFunction (hashDescr ha)) pkey (dsaMPIsToSig mpis) bs+	rsaVerify mpis ha pkey bs = P15.verify (hashDescr ha) pkey bs (rsaMPItoSig mpis)+        dsaMPIsToSig mpis = (DSA.Signature (unMPI (mpis !! 0)) (unMPI (mpis !! 1)))+        rsaMPItoSig mpis = integerToBEBS (unMPI (head mpis))+        hashalgo :: Pkt -> HashAlgorithm+        hashalgo (SignaturePkt (SigV4 _ _ ha _ _ _ _)) = ha+        hashalgo _ = error "This should never happen."+        dsaTruncate pkey bs = if countBits bs > dsaQLen pkey then B.take (fromIntegral (dsaQLen pkey) `div` 8) bs else bs -- FIXME: uneven bits+        dsaQLen = countBits . integerToBEBS . DSA.params_q . DSA.public_params+	pkaAndMPIs (SigV4 _ pka _ _ _ _ mpis) = (pka,mpis)+	pkaAndMPIs _ = error "This should never happen."+        isSignatureExpired :: Pkt -> Maybe UTCTime -> Either String Bool+        isSignatureExpired s Nothing = return False+        isSignatureExpired s (Just t) = if any (expiredBefore t) ((\(SigV4 _ _ _ h _ _ _) -> h) . _signaturePayload . fromPkt $ s) then Left "signature expired" else return True+        expiredBefore :: UTCTime -> SigSubPacket -> Bool+        expiredBefore ct (SigSubPacket _ (SigExpirationTime et)) = fromEnum ((posixSecondsToUTCTime . toEnum . fromEnum) et `diffUTCTime` ct) < 0+        expiredBefore _ _ = False++finalPayload :: Pkt -> ByteString -> ByteString+finalPayload s pl = B.concat [pl, sigbit s, trailer s]+    where+        sigbit s = runPut $ putPartialSigforSigning s+        trailer :: Pkt -> ByteString+        trailer s@(SignaturePkt (SigV4 {})) = runPut $ putSigTrailer s+        trailer _ = B.empty
Codec/Encryption/OpenPGP/Types.hs view
@@ -5,63 +5,7 @@  {-# LANGUAGE DeriveDataTypeable, ExistentialQuantification, TemplateHaskell, TypeFamilies #-} -module Codec.Encryption.OpenPGP.Types (-   SigSubPacket(..)- , SigSubPacketPayload(..)- , CompressionAlgorithm(..)- , HashAlgorithm(..)- , PubKeyAlgorithm(..)- , SymmetricAlgorithm(..)- , MPI(..)- , S2K(..)- , SignaturePayload(..)- , UserAttrSubPacket(..)- , SigType(..)- , ImageHeader(..)- , ImageFormat(..)- , KeyVersion(..)- , PKPayload(..)- , SKAddendum(..)- , DataType(..)- , PKey(..)- , SKey(..)- , EightOctetKeyId(..)- , TwentyOctetFingerprint(..)- , TK(..)- , FutureFlag- , FutureVal- , Keyring- , fromFVal- , fromFFlag- , toFVal- , toFFlag- , Block(Block)- , unBlock- , Pkt(..)- , Packet- , PKESK(..)- , Signature(..)- , SKESK(..)- , OnePassSignature(..)- , SecretKey(..)- , PublicKey(..)- , SecretSubkey(..)- , CompressedData(..)- , SymEncData(..)- , Marker(..)- , LiteralData(..)- , Trust(..)- , UserId(..)- , PublicSubkey(..)- , UserAttribute(..)- , SymEncIntegrityProtectedData(..)- , ModificationDetectionCode(..)- , OtherPacket(..)- , fromPkt- , toPkt- , Verification(..)- , KeyFlag(..)-) where+module Codec.Encryption.OpenPGP.Types where  import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA@@ -165,12 +109,12 @@     toFFlag o = OtherNF (fromIntegral o)  data SigSubPacket = SigSubPacket {-    sspCriticality :: Bool-  , sspPayload :: SigSubPacketPayload+    _sspCriticality :: Bool+  , _sspPayload :: SigSubPacketPayload   } deriving (Data, Eq, Typeable)  instance Show SigSubPacket where-    show x = (if sspCriticality x then "*" else "") ++ (show . sspPayload) x+    show x = (if _sspCriticality x then "*" else "") ++ (show . _sspPayload) x  data SigSubPacketPayload = SigCreationTime TimeStamp                   | SigExpirationTime TimeStamp@@ -263,13 +207,13 @@    toFVal :: Word8 -> a  data PubKeyAlgorithm = RSA-                     | RSAEncryptOnly-                     | RSASignOnly+                     | DeprecatedRSAEncryptOnly+                     | DeprecatedRSASignOnly                      | ElgamalEncryptOnly                      | DSA                      | EC                      | ECDSA-                     | Elgamal+                     | ForbiddenElgamal                      | DH                      | OtherPKA Word8     deriving (Show, Data, Typeable)@@ -282,23 +226,23 @@  instance FutureVal PubKeyAlgorithm where     fromFVal RSA = 1-    fromFVal RSAEncryptOnly = 2-    fromFVal RSASignOnly = 3+    fromFVal DeprecatedRSAEncryptOnly = 2+    fromFVal DeprecatedRSASignOnly = 3     fromFVal ElgamalEncryptOnly = 16     fromFVal DSA = 17     fromFVal EC = 18     fromFVal ECDSA = 19-    fromFVal Elgamal = 20+    fromFVal ForbiddenElgamal = 20     fromFVal DH = 21     fromFVal (OtherPKA o) = o     toFVal 1 = RSA-    toFVal 2 = RSAEncryptOnly-    toFVal 3 = RSASignOnly+    toFVal 2 = DeprecatedRSAEncryptOnly+    toFVal 3 = DeprecatedRSASignOnly     toFVal 16 = ElgamalEncryptOnly     toFVal 17 = DSA     toFVal 18 = EC     toFVal 19 = ECDSA-    toFVal 20 = Elgamal+    toFVal 20 = ForbiddenElgamal     toFVal 21 = DH     toFVal o = OtherPKA o @@ -865,3 +809,4 @@ $(makeLenses ''OtherPacket) $(makeLenses ''TK) $(makeLenses ''Verification)+$(makeLenses ''SigSubPacket)
− Codec/Encryption/OpenPGP/Verify.hs
@@ -1,139 +0,0 @@--- Verify.hs: OpenPGP (RFC4880) signature verification--- Copyright © 2012-2013  Clint Adams--- This software is released under the terms of the ISC license.--- (See the LICENSE file).--module Codec.Encryption.OpenPGP.Verify (-   verifySig- , verify- , verifyTK-) where--import Control.Monad (guard, liftM2)--import Crypto.PubKey.HashDescr (HashDescr(..))-import qualified Crypto.PubKey.DSA as DSA-import qualified Crypto.PubKey.RSA.PKCS15 as P15--import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import Data.Either (lefts, rights)-import Data.IxSet ((@=))-import qualified Data.IxSet as IxSet-import Data.Time.Clock (UTCTime(..), diffUTCTime)-import Data.Time.Clock.POSIX (posixSecondsToUTCTime)-import Data.Serialize.Put (runPut)--import Codec.Encryption.OpenPGP.Fingerprint (eightOctetKeyID, fingerprint)-import Codec.Encryption.OpenPGP.Internal (countBits, integerToBEBS, PktStreamContext(..), issuer, emptyPSC, hashDescr)-import Codec.Encryption.OpenPGP.SerializeForSigs (putPartialSigforSigning, putSigTrailer, payloadForSig)-import Codec.Encryption.OpenPGP.Types-import Data.Conduit.OpenPGP.Keyring.Instances ()--verifySig :: Keyring -> Pkt -> PktStreamContext -> Maybe UTCTime -> Either String Verification -- FIXME: check expiration here?-verifySig kr sig@(SignaturePkt (SigV4 st _ _ hs _ _ _)) state mt = do-    v <- verify kr sig mt (payloadForSig st state)-    _ <- mapM_ (checkIssuer (eightOctetKeyID (_verificationSigner v)) . sspPayload) hs-    return v-    where-        checkIssuer :: EightOctetKeyId -> SigSubPacketPayload -> Either String Bool-        checkIssuer signer (Issuer i) = if signer == i then Right True else Left "issuer subpacket does not match"-        checkIssuer _ _ = Right True-verifySig _ _ _ _ = Left "This should never happen."--verifyTK :: Keyring -> Maybe UTCTime -> TK -> Either String TK-verifyTK kr mt key = do-    revokers <- checkRevokers key-    revs <- checkKeyRevocations revokers key-    let uids = filter (\(_, sps) -> sps /= []) . checkUidSigs $ _tkUIDs key -- FIXME: check revocations here?-    let uats = filter (\(_, sps) -> sps /= []) . checkUAtSigs $ _tkUAts key -- FIXME: check revocations here?-    let subs = concatMap checkSub $ _tkSubs key -- FIXME: check revocations here?-    return (TK (_tkPKP key) (_tkmSKA key) revs uids uats subs)-    where-        checkRevokers = Right . concat . rights . map verifyRevoker . filter isRevokerP . _tkRevs-        checkKeyRevocations :: [(PubKeyAlgorithm, TwentyOctetFingerprint)] -> TK -> Either String [SignaturePayload]-        checkKeyRevocations rs k = Prelude.sequence . concatMap (filterRevs rs) . rights . map (liftM2 fmap (,) (vSig kr)) . _tkRevs $ k-        checkUidSigs :: [(String, [SignaturePayload])] -> [(String, [SignaturePayload])]-        checkUidSigs = map (\(uid, sps) -> (uid, (rights . map (\sp -> fmap (const sp) (vUid kr (uid, sp)))) sps))-        checkUAtSigs :: [([UserAttrSubPacket], [SignaturePayload])] -> [([UserAttrSubPacket], [SignaturePayload])]-        checkUAtSigs = map (\(uat, sps) -> (uat, (rights . map (\sp -> fmap (const sp) (vUAt kr (uat, sp)))) sps))-        checkSub :: (Pkt, SignaturePayload, Maybe SignaturePayload) -> [(Pkt, SignaturePayload, Maybe SignaturePayload)]-        checkSub (pkt, sp, mrp) = if revokedSub pkt mrp then [] else checkSub' pkt sp-        revokedSub :: Pkt -> Maybe SignaturePayload -> Bool-        revokedSub _ Nothing = False-        revokedSub p (Just rp) = vSubSig kr p rp-        checkSub' :: Pkt -> SignaturePayload -> [(Pkt, SignaturePayload, Maybe SignaturePayload)]-        checkSub' p sp = guard (vSubSig kr p sp) >> return (p, sp, Nothing)-        getHasheds (SigV4 _ _ _ ha _ _ _) = ha-        getHasheds _ = []-	filterRevs :: [(PubKeyAlgorithm, TwentyOctetFingerprint)] -> (SignaturePayload, Verification) -> [Either String SignaturePayload]-	filterRevs vokers spv = case spv of-                                     (s@(SigV4 SignatureDirectlyOnAKey _ _ _ _ _ _), _) -> [Right s]-                                     (s@(SigV4 KeyRevocationSig pka _ _ _ _ _), v) -> if any (\(p,f) -> p == pka && f == fingerprint (_verificationSigner v)) vokers then [Left "Key revoked"] else [Right s]-				     _ -> []-        isKeyRevocation (SigV4 KeyRevocationSig _ _ _ _ _ _) = True-        isKeyRevocation _ = False-        isRevokerP (SigV4 SignatureDirectlyOnAKey _ _ h u _ _) = any isRevocationKeySSP h && any isIssuerSSP u-        isRevokerP _ = False-        isRevocationKeySSP (SigSubPacket _ (RevocationKey {})) = True-        isRevocationKeySSP _ = False-        isIssuerSSP (SigSubPacket _ (Issuer _)) = True-        isIssuerSSP _ = False-        vUid :: Keyring -> (String, SignaturePayload) -> Either String Verification-        vUid keyring (uid, sp) = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastUIDorUAt = UserIdPkt uid } mt-        vUAt :: Keyring -> ([UserAttrSubPacket], SignaturePayload) -> Either String Verification-        vUAt keyring (uat, sp) = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastUIDorUAt = UserAttributePkt uat } mt-        vSig :: Keyring -> SignaturePayload -> Either String Verification-        vSig keyring sp = verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key) } mt-        vSubSig :: Keyring -> Pkt -> SignaturePayload -> Bool-        vSubSig keyring sk sp = case verifySig keyring (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (_tkPKP key), lastSubkey = sk} mt of-                                Left _ -> False-				Right _ -> True-        verifyRevoker :: SignaturePayload -> Either String [(PubKeyAlgorithm, TwentyOctetFingerprint)]-        verifyRevoker sp = do-            _ <- vSig kr sp-            return (map (\(SigSubPacket _ (RevocationKey _ pka fp)) -> (pka, fp)) . filter isRevocationKeySSP $ getHasheds sp)--verify :: Keyring -> Pkt -> Maybe UTCTime -> ByteString -> Either String Verification-verify kr sig mt payload = do-    i <- maybe (Left "issuer not found") Right (issuer sig)-    potentialmatches <- if IxSet.null (kr @= i) then Left "pubkey not found" else Right (kr @= i)-    let allrelevantpkps = filter (\x -> issuer sig == Just (eightOctetKeyID x)) (concatMap (\x -> _tkPKP x:map subPKP (_tkSubs x)) (IxSet.toList potentialmatches))-    let results = map (\pkp -> verify' sig pkp (hashalgo sig) (finalPayload sig payload)) allrelevantpkps-    case rights results of-        [] -> Left (concatMap (++"/") (lefts results))-        [r] -> do _ <- isSignatureExpired sig mt-	          return (Verification r ((_signaturePayload . fromPkt) sig)) -- FIXME: this should also check expiration time and flags of the signing key-        _ -> Left "multiple successes; unexpected condition"-    where-        subPKP (pack, _, _) = subPKP' pack-        subPKP' (PublicSubkeyPkt p) = p-        subPKP' (SecretSubkeyPkt p _) = p-        verify' (SignaturePkt s) (pub@(PKPayload V4 _ _ _ pkey)) ha pl = verify'' (pkaAndMPIs s) ha pub pkey pl-        verify' _ _ _ _ = error "This should never happen."-        verify'' (DSA,mpis) ha pub (DSAPubKey pkey) bs = verify''' (dsaVerify mpis ha pkey bs) pub-        verify'' (RSA,mpis) ha pub (RSAPubKey pkey) bs = verify''' (rsaVerify mpis ha pkey bs) pub-        verify'' _ _ _ _ _ = Left "unimplemented key type"-	verify''' f pub = if f then Right pub else Left "verification failed"-	dsaVerify mpis ha pkey bs = DSA.verify (dsaTruncate pkey . hashFunction (hashDescr ha)) pkey (dsaMPIsToSig mpis) bs-	rsaVerify mpis ha pkey bs = P15.verify (hashDescr ha) pkey bs (rsaMPItoSig mpis)-        dsaMPIsToSig mpis = (DSA.Signature (unMPI (mpis !! 0)) (unMPI (mpis !! 1)))-        rsaMPItoSig mpis = integerToBEBS (unMPI (head mpis))-        finalPayload s pl = B.concat [pl, sigbit s, trailer s]-        sigbit s = runPut $ putPartialSigforSigning s-        hashalgo :: Pkt -> HashAlgorithm-        hashalgo (SignaturePkt (SigV4 _ _ ha _ _ _ _)) = ha-        hashalgo _ = error "This should never happen."-        trailer :: Pkt -> ByteString-        trailer s@(SignaturePkt (SigV4 {})) = runPut $ putSigTrailer s-        trailer _ = B.empty-        dsaTruncate pkey bs = if countBits bs > dsaQLen pkey then B.take (fromIntegral (dsaQLen pkey) `div` 8) bs else bs -- FIXME: uneven bits-        dsaQLen = countBits . integerToBEBS . DSA.params_q . DSA.public_params-	pkaAndMPIs (SigV4 _ pka _ _ _ _ mpis) = (pka,mpis)-	pkaAndMPIs _ = error "This should never happen."-        isSignatureExpired :: Pkt -> Maybe UTCTime -> Either String Bool-        isSignatureExpired s Nothing = return False-        isSignatureExpired s (Just t) = if any (expiredBefore t) ((\(SigV4 _ _ _ h _ _ _) -> h) . _signaturePayload . fromPkt $ s) then Left "signature expired" else return True-        expiredBefore :: UTCTime -> SigSubPacket -> Bool-        expiredBefore ct (SigSubPacket _ (SigExpirationTime et)) = fromEnum ((posixSecondsToUTCTime . toEnum . fromEnum) et `diffUTCTime` ct) < 0-        expiredBefore _ _ = False
Data/Conduit/OpenPGP/Verify.hs view
@@ -12,7 +12,7 @@  import Codec.Encryption.OpenPGP.Internal (PktStreamContext(..), emptyPSC) import Codec.Encryption.OpenPGP.Types-import Codec.Encryption.OpenPGP.Verify (verifySig)+import Codec.Encryption.OpenPGP.Signatures (verifySig) import qualified Data.Conduit.List as CL  conduitVerify :: MonadResource m => Keyring -> Maybe UTCTime -> Conduit Pkt m (Either String Verification)
hOpenPGP.cabal view
@@ -1,5 +1,5 @@ Name:                hOpenPGP-Version:             0.6.3+Version:             0.7 Synopsis:            native Haskell implementation of OpenPGP (RFC4880) Description:         native Haskell implementation of OpenPGP (RFC4880) Homepage:            http://floss.scru.org/hOpenPGP/@@ -120,7 +120,7 @@                      , Codec.Encryption.OpenPGP.Serialize                      , Codec.Encryption.OpenPGP.Compression                      , Codec.Encryption.OpenPGP.Fingerprint-                     , Codec.Encryption.OpenPGP.Verify+                     , Codec.Encryption.OpenPGP.Signatures                      , Data.Conduit.OpenPGP.Compression                      , Data.Conduit.OpenPGP.Keyring                      , Data.Conduit.OpenPGP.Keyring.Instances