packages feed

hOpenPGP 2.5.5 → 2.6

raw patch · 18 files changed

+193/−117 lines, 18 filesdep +asn1-encodingdep +unliftio-coredep −byteabledep −securememdep ~basedep ~binary-conduitdep ~conduitbinary-added

Dependencies added: asn1-encoding, unliftio-core

Dependencies removed: byteable, securemem

Dependency ranges changed: base, binary-conduit, conduit, cryptonite, network

Files

Codec/Encryption/OpenPGP/Internal.hs view
@@ -16,20 +16,18 @@  , sigPKA  , sigHA  , sigCT- , truncatingVerify+ , curveoidBSToCurve+ , curveToCurveoidBS+ , point2BS ) where -import Crypto.Hash (hashWith)-import qualified Crypto.Hash.IO as CHI-import Crypto.Number.Basic (numBits)-import Crypto.Number.ModArithmetic (expFast, inverse)-import Crypto.Number.Serialize (os2ip)+import Crypto.Number.Serialize (i2osp, os2ip) import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Types as ECCT import qualified Crypto.PubKey.RSA as RSA  import Data.Bits (testBit)-import Data.ByteArray (ByteArrayAccess)-import qualified Data.ByteArray as BA import qualified Data.ByteString as B import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as BL@@ -74,6 +72,8 @@   where pkParams f = MPI . f . DSA.public_params $ k  pubkeyToMPIs (ElGamalPubKey p g y) = [MPI p, MPI g, MPI y]+pubkeyToMPIs (ECDHPubKey ((ECDSA_PublicKey (ECDSA.PublicKey _ q))) _ _) = [MPI (os2ip (point2BS q))]+pubkeyToMPIs (ECDSAPubKey ((ECDSA_PublicKey (ECDSA.PublicKey _ q)))) = [MPI (os2ip (point2BS q))]  multiplicativeInverse :: Integral a => a -> a -> a multiplicativeInverse _ 1 = 1@@ -100,18 +100,22 @@ sigCT (SigV4 _ _ _ hsubs _ _ _) = fmap (\(SigSubPacket _ (SigCreationTime i)) -> i) (find isSigCreationTime hsubs) sigCT _ = Nothing -truncatingVerify :: (ByteArrayAccess msg, CHI.HashAlgorithm hash) => hash -> DSA.PublicKey -> DSA.Signature -> msg -> Bool-truncatingVerify hashAlg pk (DSA.Signature r s) m-    -- Reject the signature if either 0 < r < q or 0 < s < q is not satisfied.-    | r <= 0 || r >= q || s <= 0 || s >= q = False-    | otherwise                            = v == r-    where (DSA.Params p g q) = DSA.public_params pk-          y       = DSA.public_y pk-          hm      = os2ip . dsaTruncate . BA.convert $ hashWith hashAlg m+curveoidBSToCurve :: B.ByteString -> Either String ECCT.Curve+curveoidBSToCurve oidbs+    | B.pack [0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07] == oidbs = Right $ ECCT.getCurveByName ECCT.SEC_p256r1+    | B.pack [0x2B,0x81,0x04,0x00,0x22] == oidbs = Right $ ECCT.getCurveByName ECCT.SEC_p384r1+    | B.pack [0x2B,0x81,0x04,0x00,0x23] == oidbs = Right $ ECCT.getCurveByName ECCT.SEC_p521r1+    | otherwise = Left "unknown curve OID" -          w       = fromJust $ inverse s q-          u1      = (hm*w) `mod` q-          u2      = (r*w) `mod` q-          v       = (expFast g u1 p * expFast y u2 p) `mod` p `mod` q-          dsaTruncate bs = let lbs = BL.fromStrict bs in if countBits lbs > fromIntegral dsaQLen then B.take (dsaQLen `div` 8) bs else bs -- FIXME: uneven bits-          dsaQLen = numBits q+-- [0x2B 0x06 0x01 0x04 0x01 0xDA 0x47 0x0F 0x01] -- ed25519++curveToCurveoidBS :: ECCT.Curve -> Either String B.ByteString+curveToCurveoidBS curve+    | curve == ECCT.getCurveByName ECCT.SEC_p256r1 = Right $ B.pack [0x2A,0x86,0x48,0xCE,0x3D,0x03,0x01,0x07]+    | curve == ECCT.getCurveByName ECCT.SEC_p384r1 = Right $ B.pack [0x2B,0x81,0x04,0x00,0x22]+    | curve == ECCT.getCurveByName ECCT.SEC_p521r1 = Right $ B.pack [0x2B,0x81,0x04,0x00,0x23]+    | otherwise = Left "unknown curve"++point2BS :: ECCT.PublicPoint -> B.ByteString+point2BS (ECCT.Point x y) = B.concat [B.singleton 0x04, i2osp x, i2osp y] -- FIXME: check for length equality?+point2BS (ECCT.PointO) = error "FIXME: point at infinity"
Codec/Encryption/OpenPGP/KeyInfo.hs view
@@ -8,8 +8,10 @@  , pkalgoAbbrev ) where -import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Types as ECCT+import qualified Crypto.PubKey.RSA as RSA import Data.Bits (shiftR) import Data.List (unfoldr) @@ -19,6 +21,8 @@ pubkeySize (RSAPubKey (RSA_PublicKey x)) = Right (RSA.public_size x * 8) pubkeySize (DSAPubKey (DSA_PublicKey x)) = Right (bitcount . DSA.params_p . DSA.public_params $ x) pubkeySize (ElGamalPubKey p _ _) = Right (bitcount p)+pubkeySize (ECDSAPubKey (ECDSA_PublicKey (ECDSA.PublicKey curve _))) = Right (fromIntegral (ECCT.curveSizeBits curve))+pubkeySize (ECDHPubKey (ECDSA_PublicKey (ECDSA.PublicKey curve _)) _ _) = Right (fromIntegral (ECCT.curveSizeBits curve)) pubkeySize x = Left $ "Unable to calculate size of " ++ show x  bitcount :: Integer -> Int
Codec/Encryption/OpenPGP/Serialize.hs view
@@ -23,6 +23,9 @@ import Crypto.Number.Serialize (i2osp, os2ip) import qualified Crypto.PubKey.RSA as R import qualified Crypto.PubKey.DSA as D+import qualified Crypto.PubKey.ECC.Types as ECCT+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import Data.Bifunctor (bimap) import Data.Bits ((.&.), (.|.), shiftL, shiftR, testBit) import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString as B@@ -45,7 +48,7 @@ import Data.Maybe (fromMaybe) import Network.URI (nullURI, parseURI, uriToString) -import Codec.Encryption.OpenPGP.Internal (pubkeyToMPIs, multiplicativeInverse)+import Codec.Encryption.OpenPGP.Internal (curveoidBSToCurve, curveToCurveoidBS, pubkeyToMPIs, multiplicativeInverse) import Codec.Encryption.OpenPGP.Types  instance Binary SigSubPacket where@@ -609,8 +612,12 @@                           udata <- getByteString (fromIntegral len)                           return . UserIdPkt . decodeUtf8With lenientDecode $ udata             | t == 14 = do-                          pkp <- getPKPayload-                          return $ PublicSubkeyPkt pkp+                          bs <- getLazyByteString len+                          let ps = flip runGetOrFail bs $ do pkp <- getPKPayload+                                                             return $ PublicSubkeyPkt pkp+                          case ps of+                              Left (_, _, err) -> fail ("public subkey " ++ err)+                              Right (_, _, key) -> return key             | t == 17 = do                         bs <- getLazyByteString len                         case runGetOrFail (many getUserAttrSubPacket) bs of@@ -783,10 +790,36 @@                                 MPI g <- get                                 MPI y <- get                                 return $ ElGamalPubKey p g y+getPubkey ECDSA = do+    curvelength <- getWord8 -- FIXME: test for 0 or 0xFF as they are reserved+    curveoid <- getByteString (fromIntegral curvelength)+    MPI mpi <- getMPI -- FIXME: check length against curve type?+    case curveoidBSToCurve curveoid >>= \c -> mpi2point c mpi of+        Left e -> fail e+        Right (curve, point) -> return . ECDSAPubKey . ECDSA_PublicKey . ECDSA.PublicKey curve $ point+    where+        mpi2point c cpi = fmap (\y -> (c,y)) (bs2Point (i2osp cpi))+getPubkey ECDH = do+    ECDSAPubKey ed <- getPubkey ECDSA+    kdflen <- getWord8 -- FIXME: should be 3, test for 0 or 0xFF as they are reserved+    one <- getWord8 -- FIXME: should be 1+    kdfHA <- get+    kdfSA <- get+    return $ ECDHPubKey ed kdfHA kdfSA+ getPubkey _ = UnknownPKey <$> getRemainingLazyByteString +bs2Point :: B.ByteString -> Either String ECDSA.PublicPoint+bs2Point bs = let xy = B.drop 1 bs in+                let l = B.length xy in+                  if B.head bs == 0x04+                    then return (uncurry ECCT.Point (bimap os2ip os2ip (B.splitAt (div l 2) xy)))+                    else fail $ "unknown type of point: " ++ show (B.unpack bs)+ putPubkey :: PKey -> Put putPubkey (UnknownPKey bs) = putLazyByteString bs+putPubkey p@(ECDSAPubKey (ECDSA_PublicKey (ECDSA.PublicKey curve _))) = let Right curveoidbs = curveToCurveoidBS curve in putWord8 (fromIntegral (B.length curveoidbs)) >> putByteString curveoidbs >> mapM_ put (pubkeyToMPIs p) -- FIXME: do not output length 0 or 0xff+putPubkey p@(ECDHPubKey (ECDSA_PublicKey (ECDSA.PublicKey curve _)) kha ksa) = let Right curveoidbs = curveToCurveoidBS curve in putWord8 (fromIntegral (B.length curveoidbs)) >> putByteString curveoidbs >> mapM_ put (pubkeyToMPIs p) >> putWord8 0x03 >> putWord8 0x01 >> put kha >> put ksa -- FIXME: do not output length 0 or 0xff putPubkey p = mapM_ put (pubkeyToMPIs p)  getSecretKey :: PKPayload -> Get SKey@@ -806,6 +839,14 @@     | _pkalgo pkp `elem` [ElgamalEncryptOnly,ForbiddenElgamal] =         do MPI x <- get            return $ ElGamalPrivateKey x+    | _pkalgo pkp == ECDSA =+        do MPI pn <- get+           let pubcurve = (\(ECDSAPubKey (ECDSA_PublicKey p)) -> ECDSA.public_curve p) (pkp^.pubkey)+           return $ ECDSAPrivateKey (ECDSA_PrivateKey (ECDSA.PrivateKey pubcurve pn))+    | _pkalgo pkp == ECDH = -- FIXME: deduplicate this and above+        do MPI pn <- get+           let pubcurve = (\(ECDSAPubKey (ECDSA_PublicKey p)) -> ECDSA.public_curve p) (pkp^.pubkey)+           return $ ECDHPrivateKey (ECDSA_PrivateKey (ECDSA.PrivateKey pubcurve pn))  putSKey :: SKey -> Put putSKey (RSAPrivateKey (RSA_PrivateKey (R.PrivateKey _ d p q _ _ _))) = put (MPI d) >> put (MPI p) >> put (MPI q) >> put (MPI u)
Codec/Encryption/OpenPGP/SerializeForSigs.hs view
@@ -38,7 +38,7 @@ putPKPforFingerprinting _ = fail "This should never happen (putPKPforFingerprinting)"  putMPIforFingerprinting:: MPI -> Put-putMPIforFingerprinting(MPI i) = let bs = i2osp i in putByteString bs+putMPIforFingerprinting (MPI i) = let bs = i2osp i in putByteString bs  putPartialSigforSigning :: Pkt -> Put putPartialSigforSigning (SignaturePkt (SigV4 st pka ha hashed _ _ _)) = do
Codec/Encryption/OpenPGP/Signatures.hs view
@@ -22,11 +22,17 @@ import qualified Crypto.Hash.Algorithms as CHA import Crypto.Number.Serialize (i2osp) import qualified Crypto.PubKey.DSA as DSA+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.RSA.PKCS15 as P15  import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString.Lazy as BL import Data.Either (lefts, rights)+#if MIN_VERSION_base(4,7,0)+import Data.Either (isRight)+#else+import Control.Error.Util (isRight)+#endif import Data.IxSet.Typed ((@=)) import qualified Data.IxSet.Typed as IxSet import Data.List.NonEmpty (NonEmpty(..))@@ -36,7 +42,7 @@ import Data.Binary.Put (runPut)  import Codec.Encryption.OpenPGP.Fingerprint (eightOctetKeyID, fingerprint)-import Codec.Encryption.OpenPGP.Internal (PktStreamContext(..), issuer, emptyPSC, truncatingVerify)+import Codec.Encryption.OpenPGP.Internal (PktStreamContext(..), issuer, emptyPSC) import Codec.Encryption.OpenPGP.Ontology (isRevokerP, isRevocationKeySSP, isSubkeyBindingSig, isSubkeyRevocation)  import Codec.Encryption.OpenPGP.SerializeForSigs (putPartialSigforSigning, putSigTrailer, payloadForSig)@@ -59,8 +65,8 @@ verifyTKWith vsf mt key = do     revokers <- checkRevokers key     revs <- checkKeyRevocations revokers key-    let uids = filter (\(_, sps) -> sps /= []) . checkUidSigs $ key^.tkUIDs -- FIXME: check revocations here?-    let uats = filter (\(_, sps) -> sps /= []) . checkUAtSigs $ key^.tkUAts -- FIXME: check revocations here?+    let uids = filter (not . null . snd) . checkUidSigs $ key^.tkUIDs -- FIXME: check revocations here?+    let uats = filter (not . null . snd) . checkUAtSigs $ key^.tkUAts -- FIXME: check revocations here?     let subs = concatMap checkSub $ key^.tkSubs -- FIXME: check revocations here?     return (TK (key^.tkKey) revs uids uats subs)     where@@ -92,9 +98,7 @@         vSig :: SignaturePayload -> Either String Verification         vSig sp = vsf (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (key ^. tkKey._1) } mt         vSubSig :: Pkt -> SignaturePayload -> Bool-        vSubSig sk sp = case vsf (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (key ^. tkKey._1), lastSubkey = sk} mt of-                                Left _ -> False-                                Right _ -> True+        vSubSig sk sp = isRight (vsf (SignaturePkt sp) emptyPSC { lastPrimaryKey = PublicKeyPkt (key ^. tkKey._1), lastSubkey = sk} mt)         verifyRevoker :: SignaturePayload -> Either String [(PubKeyAlgorithm, TwentyOctetFingerprint)]         verifyRevoker sp = do             _ <- vSig sp@@ -128,14 +132,17 @@         verify' (SignaturePkt s) (pub@(PKPayload V4 _ _ _ pkey)) SHA224 pl = verify'' (pkaAndMPIs s) CHA.SHA224 pub pkey pl         verify' (SignaturePkt s) (pub@(PKPayload V4 _ _ _ pkey)) DeprecatedMD5 pl = verify'' (pkaAndMPIs s) CHA.MD5 pub pkey pl         verify' _ _ _ _ = error "This should never happen (verify')."-        verify'' (DSA,mpis) hd pub (DSAPubKey (DSA_PublicKey pkey)) bs = verify''' (dsaVerify mpis hd pkey bs) pub-        verify'' (RSA,mpis) hd pub (RSAPubKey (RSA_PublicKey pkey)) bs = verify''' (rsaVerify mpis hd pkey bs) pub+        verify'' (DSA,mpis) hd pub (DSAPubKey (DSA_PublicKey pkey)) bs = dsaVerify pub mpis hd pkey bs+        verify'' (ECDSA,mpis) hd pub (ECDSAPubKey (ECDSA_PublicKey pkey)) bs = ecdsaVerify pub mpis hd pkey bs+        verify'' (RSA,mpis) hd pub (RSAPubKey (RSA_PublicKey pkey)) bs = rsaVerify pub mpis hd pkey bs         verify'' _ _ _ _ _ = Left "unimplemented key type"-        verify''' f pub = if f then Right pub else Left "verification failed"-        dsaVerify (r:|[s]) hd pkey = truncatingVerify hd pkey (dsaMPIsToSig r s)-        dsaVerify _ _ _ = const False -- FIXME: this should be some sort of Either chain?-        rsaVerify mpis hd pkey bs = P15.verify (Just hd) pkey bs (rsaMPItoSig mpis)+        dsaVerify pub (r:|[s]) hd pkey bs = if DSA.verify hd pkey (dsaMPIsToSig r s) bs then Right pub else Left ("DSA verification failed: " ++ show (hd,pkey,r,s,bs))+        dsaVerify _ _ _ _ _ = Left "cannot verify DSA signature of wrong shape"+        ecdsaVerify pub (r:|[s]) hd pkey bs = if ECDSA.verify hd pkey (ecdsaMPIsToSig r s) bs then Right pub else Left ("ECDSA verification failed: " ++ show (hd,pkey,r,s,bs))+        ecdsaVerify _ _ _ _ _ = Left "cannot verify ECDSA signature of wrong shape"+        rsaVerify pub mpis hd pkey bs = if P15.verify (Just hd) pkey bs (rsaMPItoSig mpis) then Right pub else Left ("DSA verification failed: " ++ show (hd,pkey,mpis,bs))         dsaMPIsToSig r s = DSA.Signature (unMPI r) (unMPI s)+        ecdsaMPIsToSig r s = ECDSA.Signature (unMPI r) (unMPI s)         rsaMPItoSig (s:|[]) = i2osp (unMPI s)         hashalgo :: Pkt -> HashAlgorithm         hashalgo (SignaturePkt (SigV4 _ _ ha _ _ _ _)) = ha
Codec/Encryption/OpenPGP/Types/Internal/Base.hs view
@@ -27,7 +27,6 @@ import Data.Aeson ((.=), object) import qualified Data.Aeson as A import qualified Data.Aeson.TH as ATH-import Data.Byteable (Byteable) import Data.ByteArray (ByteArrayAccess) import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString as B@@ -869,7 +868,7 @@ $(ATH.deriveJSON ATH.defaultOptions ''KeyVersion)  newtype IV = IV {unIV :: B.ByteString}-    deriving (Byteable, ByteArrayAccess, Data, Eq, Generic, Hashable, Monoid, Show, Typeable)+    deriving (ByteArrayAccess, Data, Eq, Generic, Hashable, Monoid, Show, Typeable)  instance Newtype IV B.ByteString where     pack = IV@@ -915,7 +914,7 @@ $(ATH.deriveJSON ATH.defaultOptions ''DataType)  newtype Salt = Salt {unSalt :: B.ByteString}-    deriving (Byteable, Data, Eq, Generic, Hashable, Show, Typeable)+    deriving (Data, Eq, Generic, Hashable, Show, Typeable)  instance Newtype Salt B.ByteString where     pack = Salt
Codec/Encryption/OpenPGP/Types/Internal/CryptoniteNewtypes.hs view
@@ -11,9 +11,11 @@  import GHC.Generics (Generic) +import Control.Monad (mzero) import qualified Crypto.PubKey.RSA as RSA import qualified Crypto.PubKey.DSA as DSA import qualified Crypto.PubKey.ECC.ECDSA as ECDSA+import qualified Crypto.PubKey.ECC.Types as ECCT import qualified Data.Aeson as A import Data.Data (Data) import Data.Hashable (Hashable(..))@@ -83,4 +85,13 @@     hashWithSalt s (ECDSA_PublicKey (ECDSA.PublicKey curve q)) = s `hashWithSalt` show curve `hashWithSalt` show q   -- FIXME: don't use show instance Hashable ECDSA_PrivateKey where     hashWithSalt s (ECDSA_PrivateKey (ECDSA.PrivateKey curve d)) = s `hashWithSalt` show curve `hashWithSalt` show d  -- FIXME: don't use show++newtype ECurvePoint = ECurvePoint { unECurvepoint :: ECCT.Point }+    deriving (Data, Eq, Generic, Show, Typeable)+instance A.ToJSON ECurvePoint where+    toJSON (ECurvePoint (ECCT.Point x y)) = A.toJSON (x, y)+    toJSON (ECurvePoint ECCT.PointO) = A.toJSON "point at infinity"+instance A.FromJSON ECurvePoint where+    parseJSON (A.Object v) = error "FIXME: whatsit"+    parseJSON _            = mzero 
Data/Conduit/OpenPGP/Compression.hs view
@@ -1,5 +1,5 @@ -- Compression.hs: OpenPGP (RFC4880) compression conduits--- Copyright © 2012-2014  Clint Adams+-- Copyright © 2012-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -14,8 +14,8 @@ import qualified Data.Conduit.List as CL import Control.Monad.Trans.Resource (MonadThrow) -conduitCompress :: MonadThrow m => CompressionAlgorithm -> Conduit Pkt m Pkt+conduitCompress :: MonadThrow m => CompressionAlgorithm -> ConduitT Pkt Pkt m () conduitCompress algo = CL.consume >>= \ps -> yield (compressPkts algo ps) -conduitDecompress :: MonadThrow m => Conduit Pkt m Pkt+conduitDecompress :: MonadThrow m => ConduitT Pkt Pkt m () conduitDecompress = CL.concatMap decompressPkt
Data/Conduit/OpenPGP/Decrypt.hs view
@@ -1,5 +1,5 @@ -- Decrypt.hs: OpenPGP (RFC4880) recursive packet decryption--- Copyright © 2013-2016  Clint Adams+-- Copyright © 2013-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -11,7 +11,8 @@  import Control.Monad (when) import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad.Trans.Resource (MonadBaseControl, MonadResource, MonadThrow, runResourceT)+import Control.Monad.IO.Unlift (MonadUnliftIO)+import Control.Monad.Trans.Resource (MonadResource, MonadThrow) import qualified Control.Monad.Trans.State.Lazy as S import qualified Crypto.Hash as CH import qualified Crypto.Hash.Algorithms as CHA@@ -42,13 +43,13 @@  type InputCallback m = String -> m BL.ByteString -conduitDecrypt :: (MonadBaseControl IO m, MonadResource m) => InputCallback IO -> Conduit Pkt m Pkt+conduitDecrypt :: (MonadUnliftIO m, MonadResource m, MonadThrow m) => InputCallback IO -> ConduitT Pkt Pkt m () conduitDecrypt = conduitDecrypt' 0 -conduitDecrypt' :: (MonadBaseControl IO m, MonadResource m) => Int -> InputCallback IO -> Conduit Pkt m Pkt+conduitDecrypt' :: (MonadUnliftIO m, MonadResource m, MonadThrow m) => Int -> InputCallback IO -> ConduitT Pkt Pkt m () conduitDecrypt' depth cb = CL.concatMapAccumM push def { _depth = depth }  -- FIXME: this depth stuff is convoluted     where-        push :: (MonadBaseControl IO m, MonadResource m) => Pkt -> RecursorState -> m (RecursorState, [Pkt])+        push :: (MonadUnliftIO m, MonadResource m, MonadThrow m) => Pkt -> RecursorState -> m (RecursorState, [Pkt])         push i s             | _depth s > 42 = fail "I think we've been quine-attacked"             | otherwise = case i of@@ -65,20 +66,20 @@         ldpCheck l@LiteralDataPkt{} = S.get >>= \o -> S.put o { _lastLDP = Just . fromPkt $ l }         ldpCheck _ = return () -decryptSEDP :: (MonadBaseControl IO m, MonadIO m, MonadThrow m) => Int -> InputCallback IO -> SKESK -> BL.ByteString -> m [Pkt]+decryptSEDP :: (MonadUnliftIO m, MonadIO m, MonadThrow m) => Int -> InputCallback IO -> SKESK -> BL.ByteString -> m [Pkt] decryptSEDP depth cb skesk bs = do -- FIXME: this shouldn't pass the whole SKESK     passphrase <- liftIO $ cb "Input the passphrase I want"     let key = skesk2Key skesk passphrase         decrypted = case decryptOpenPGPCfb (_skeskSymmetricAlgorithm skesk) (BL.toStrict bs) key of                         Left e -> error e                         Right x -> x-    runResourceT $ CB.sourceLbs (BL.fromStrict decrypted) $= conduitGet get $= conduitDecompress $= conduitDecrypt' depth cb $$ CL.consume+    runConduitRes $ CB.sourceLbs (BL.fromStrict decrypted) .| conduitGet get .| conduitDecompress .| conduitDecrypt' depth cb .| CL.consume -decryptSEIPDP :: (MonadBaseControl IO m, MonadIO m, MonadThrow m) => Int -> InputCallback IO -> SKESK -> BL.ByteString -> m [Pkt]+decryptSEIPDP :: (MonadUnliftIO m, MonadIO m, MonadThrow m) => Int -> InputCallback IO -> SKESK -> BL.ByteString -> m [Pkt] decryptSEIPDP depth cb skesk bs = do -- FIXME: this shouldn't pass the whole SKESK     passphrase <- liftIO $ cb "Input the passphrase I want"     let key = skesk2Key skesk passphrase         decrypted = case decrypt (_skeskSymmetricAlgorithm skesk) (BL.toStrict bs) key of                         Left e -> error e                         Right x -> x-    runResourceT $ CB.sourceLbs (BL.fromStrict decrypted) $= conduitGet get $= conduitDecompress $= conduitDecrypt' depth cb $$ CL.consume+    runConduitRes $ CB.sourceLbs (BL.fromStrict decrypted) .| conduitGet get .| conduitDecompress .| conduitDecrypt' depth cb .| CL.consume
Data/Conduit/OpenPGP/Filter.hs view
@@ -1,5 +1,5 @@ -- Filter.hs: OpenPGP (RFC4880) packet filtering--- Copyright © 2014-2015  Clint Adams+-- Copyright © 2014-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -12,7 +12,7 @@ ) where  import Control.Monad.Trans.Reader (Reader, runReader)-import Data.Conduit (Conduit)+import Data.Conduit (ConduitT) import qualified Data.Conduit.List as CL  import Codec.Encryption.OpenPGP.Types@@ -22,14 +22,14 @@     RTKFilterPredicate (Reader TK Bool)       -- ^ fp for transferable keys   | RPFilterPredicate (Reader Pkt Bool)       -- ^ fp for context-less packets -conduitPktFilter :: Monad m => FilterPredicates -> Conduit Pkt m Pkt+conduitPktFilter :: Monad m => FilterPredicates -> ConduitT Pkt Pkt m () conduitPktFilter = CL.filter . superPredicate  superPredicate :: FilterPredicates -> Pkt -> Bool superPredicate (RPFilterPredicate e) p = runReader e p superPredicate _ _ = False   -- do not match incorrect type of packet -conduitTKFilter :: Monad m => FilterPredicates -> Conduit TK m TK+conduitTKFilter :: Monad m => FilterPredicates -> ConduitT TK TK m () conduitTKFilter = CL.filter . superTKPredicate  superTKPredicate :: FilterPredicates -> TK -> Bool
Data/Conduit/OpenPGP/Keyring.hs view
@@ -1,5 +1,5 @@ -- Keyring.hs: OpenPGP (RFC4880) transferable keys parsing--- Copyright © 2012-2016  Clint Adams+-- Copyright © 2012-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -21,13 +21,13 @@ data Phase = MainKey | Revs | Uids | UAts | Subs | SkippingBroken     deriving (Eq, Ord, Show) -conduitToTKs :: Monad m => Conduit Pkt m TK+conduitToTKs :: Monad m => ConduitT Pkt TK m () conduitToTKs = conduitToTKs' True -conduitToTKsDropping :: Monad m => Conduit Pkt m TK+conduitToTKsDropping :: Monad m => ConduitT Pkt TK m () conduitToTKsDropping = conduitToTKs' False -fakecmAccum :: Monad m => (accum -> (accum, [b])) -> (a -> accum -> (accum, [b])) -> accum -> Conduit a m b+fakecmAccum :: Monad m => (accum -> (accum, [b])) -> (a -> accum -> (accum, [b])) -> accum -> ConduitT a b m () fakecmAccum finalizer f =     loop   where@@ -39,10 +39,10 @@             mapM_ yield bs             loop accum' -conduitToTKs' :: Monad m => Bool -> Conduit Pkt m TK-conduitToTKs' intolerant = CL.filter notTrustPacket =$= CL.map (:[]) =$= fakecmAccum finalizeParsing (parseAChunk (anyTK intolerant)) ([], Just (Nothing, anyTK intolerant)) =$= CL.catMaybes+conduitToTKs' :: Monad m => Bool -> ConduitT Pkt TK m ()+conduitToTKs' intolerant = CL.filter notTrustPacket .| CL.map (:[]) .| fakecmAccum finalizeParsing (parseAChunk (anyTK intolerant)) ([], Just (Nothing, anyTK intolerant)) .| CL.catMaybes     where         notTrustPacket = not . isTrustPkt -sinkKeyringMap :: Monad m => Sink TK m Keyring+sinkKeyringMap :: Monad m => ConduitT TK Void m Keyring sinkKeyringMap = CL.fold (flip insert) empty
Data/Conduit/OpenPGP/Verify.hs view
@@ -15,7 +15,7 @@ import Codec.Encryption.OpenPGP.Signatures (verifySigWith, verifyAgainstKeyring) import qualified Data.Conduit.List as CL -conduitVerify :: Monad m => Keyring -> Maybe UTCTime -> Conduit Pkt m (Either String Verification)+conduitVerify :: Monad m => Keyring -> Maybe UTCTime -> ConduitT Pkt (Either String Verification) m () conduitVerify kr mt = CL.concatMapAccum (flip push) emptyPSC     where         push state ld@LiteralDataPkt{} = (state { lastLD = ld }, [])
bench/mark.hs view
@@ -1,5 +1,5 @@ -- mark.hs: hOpenPGP benchmark suite--- Copyright © 2014-2016  Clint Adams+-- Copyright © 2014-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -9,7 +9,6 @@  import Codec.Encryption.OpenPGP.Signatures (verifyTKWith, verifySigWith, verifyAgainstKeys, verifyAgainstKeyring) -import Control.Monad.Trans.Resource (runResourceT) import Data.Conduit.Serialization.Binary (conduitGet) import Data.Conduit.OpenPGP.Keyring (conduitToTKs, sinkKeyringMap) import qualified Data.IxSet.Typed as IxSet@@ -28,8 +27,8 @@                    ]                ]   where-    loadKeys fp = runResourceT $ CB.sourceFile fp DC.$= conduitGet get DC.$= conduitToTKs DC.$$ CL.consume-    loadKeyring fp = runResourceT $ CB.sourceFile fp DC.$= conduitGet get DC.$= conduitToTKs DC.$$ sinkKeyringMap+    loadKeys fp = DC.runConduitRes $ CB.sourceFile fp DC..| conduitGet get DC..| conduitToTKs DC..| CL.consume+    loadKeyring fp = DC.runConduitRes $ CB.sourceFile fp DC..| conduitGet get DC..| conduitToTKs DC..| sinkKeyringMap      selfVerifyKeys fp = fmap (\ks -> mapM (verifyTKWith (verifySigWith (verifyAgainstKeys ks)) Nothing) ks) (loadKeys fp)     selfVerifyKeyring fp = fmap (\kr -> mapM (verifyTKWith (verifySigWith (verifyAgainstKeyring kr)) Nothing) (IxSet.toList kr)) (loadKeyring fp)
hOpenPGP.cabal view
@@ -1,13 +1,13 @@ Name:                hOpenPGP-Version:             2.5.5+Version:             2.6 Synopsis:            native Haskell implementation of OpenPGP (RFC4880)-Description:         native Haskell implementation of OpenPGP (RFC4880), plus Camellia (RFC5581)-Homepage:            http://floss.scru.org/hOpenPGP/+Description:         native Haskell implementation of OpenPGP (RFC4880), plus Camellia (RFC5581), plus ECC (RFC6637)+Homepage:            https://salsa.debian.org/clint/hOpenPGP License:             MIT License-file:        LICENSE Author:              Clint Adams Maintainer:          Clint Adams <clint@debian.org>-Copyright:           2012-2016  Clint Adams+Copyright:           2012-2018  Clint Adams Category:            Codec, Data Build-type:          Simple Extra-source-files: tests/suite.hs@@ -146,7 +146,9 @@   , tests/data/sigs-with-regexes   , tests/data/gnu-dummy-s2k-101-secret-key.gpg   , tests/data/anibal-ed25519.gpg-+  , tests/data/nist_p-256_key.gpg+  , tests/data/nist_p-256_secretkey.gpg+  , tests/data/ecdsa-key-without-ecdh.pubkey Cabal-version:       >= 1.10  flag network-uri@@ -186,20 +188,20 @@                , Codec.Encryption.OpenPGP.Types.Internal.TK                , Codec.Encryption.OpenPGP.BlockCipher   Build-depends: aeson+               , asn1-encoding                , attoparsec                , base                  > 4       && < 5                , base16-bytestring                , base64-bytestring                , bifunctors-               , byteable                , bytestring                , bzlib                , binary                >= 0.6.4.0-               , binary-conduit-               , conduit               >= 0.5    && < 1.3+               , binary-conduit        >= 1.3+               , conduit               >= 1.2.8                , conduit-extra         >= 1.1                , containers-               , cryptonite            >= 0.5+               , cryptonite            >= 0.11                , crypto-cipher-types                , data-default-class                , errors@@ -213,18 +215,18 @@                , newtype                , openpgp-asciiarmor                 >= 0.1                , resourcet              > 0.4-               , securemem                , semigroups                , split                , text                , time                               >= 1.1                , time-locale-compat                , transformers+               , unliftio-core                , unordered-containers                , wl-pprint-extras                , zlib   if flag(network-uri)-    build-depends: network-uri >= 2.6, network >= 2.6+    build-depends: network-uri >= 2.6   else     build-depends: network-uri < 2.6, network < 2.6   default-language: Haskell2010@@ -237,19 +239,19 @@   Ghc-options: -Wall -with-rtsopts=-K1K   Build-depends: hOpenPGP                , aeson+               , asn1-encoding                , attoparsec                , base                  > 4       && < 5                , base16-bytestring                , bifunctors-               , byteable                , bytestring                , bzlib                , binary                >= 0.6.4.0-               , binary-conduit-               , conduit+               , binary-conduit        >= 1.3+               , conduit               >= 1.3                , conduit-extra                , containers-               , cryptonite            >= 0.5+               , cryptonite            >= 0.11                , crypto-cipher-types                , data-default-class                , errors@@ -261,13 +263,13 @@                , monad-loops                , nettle                , newtype-               , securemem                , semigroups                , split                , text                , time                               >= 1.1                , time-locale-compat                , transformers+               , unliftio-core                , unordered-containers                , wl-pprint-extras                , zlib@@ -293,15 +295,14 @@                , base16-bytestring                , base64-bytestring                , bifunctors-               , byteable                , bytestring                , bzlib                , binary                >= 0.6.4.0-               , binary-conduit-               , conduit               >= 0.5    && < 1.3+               , binary-conduit        >= 1.3+               , conduit               >= 1.3                , conduit-extra         >= 1.1                , containers-               , cryptonite            >= 0.5+               , cryptonite            >= 0.11                , crypto-cipher-types                , data-default-class                , errors@@ -315,13 +316,13 @@                , newtype                , openpgp-asciiarmor                 >= 0.1                , resourcet              > 0.4-               , securemem                , semigroups                , split                , text                , time                               >= 1.1                , time-locale-compat                , transformers+               , unliftio-core                , unordered-containers                , wl-pprint-extras                , zlib@@ -334,9 +335,9 @@  source-repository head   type:     git-  location: git://git.debian.org/users/clint/hOpenPGP.git+  location: https://salsa.debian.org/clint/hOpenPGP.git  source-repository this   type:     git-  location: git://git.debian.org/users/clint/hOpenPGP.git-  tag:      v2.5.5+  location: https://salsa.debian.org/clint/hOpenPGP.git+  tag:      v2.6
+ tests/data/ecdsa-key-without-ecdh.pubkey view

binary file changed (absent → 258 bytes)

+ tests/data/nist_p-256_key.gpg view

binary file changed (absent → 451 bytes)

+ tests/data/nist_p-256_secretkey.gpg view

binary file changed (absent → 617 bytes)

tests/suite.hs view
@@ -1,7 +1,7 @@ {-# LANGUAGE OverloadedStrings #-}  -- suite.hs: hOpenPGP test suite--- Copyright © 2012-2016  Clint Adams+-- Copyright © 2012-2018  Clint Adams -- This software is released under the terms of the Expat license. -- (See the LICENSE file). @@ -24,7 +24,8 @@ import Codec.Encryption.OpenPGP.Signatures (verifyTKWith, verifySigWith, verifyAgainstKeys) import Codec.Encryption.OpenPGP.Types import Control.Error.Util (isRight)-import Control.Monad.Trans.Resource (ResourceT, runResourceT)+import Control.Monad.Trans.Resource (ResourceT)+import qualified Crypto.PubKey.ECC.ECDSA as ECDSA import qualified Crypto.PubKey.RSA as RSA import Data.Conduit.Serialization.Binary (conduitGet) import Data.Conduit.OpenPGP.Compression (conduitCompress, conduitDecompress)@@ -76,12 +77,12 @@                           else                               assertEqual ("for " ++ fpr) firstpass secondpass -counter :: (Monad m) => DC.Sink a m Int+counter :: (Monad m) => DC.ConduitT a DC.Void m Int counter = CL.fold (const . (1+)) 0 -testConduitOutputLength :: FilePath -> DC.Conduit B.ByteString (ResourceT IO) b -> Int -> Assertion+testConduitOutputLength :: FilePath -> DC.ConduitT B.ByteString b (ResourceT IO) () -> Int -> Assertion testConduitOutputLength fpr c target = do-    len <- runResourceT $ CB.sourceFile ("tests/data/" ++ fpr) DC.$= c DC.$$ counter+    len <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ fpr) DC..| c DC..| counter     assertEqual ("expected length " ++ show target) target len  testPKAandSizeAndKeyIDandFingerprint :: FilePath -> String -> Assertion@@ -99,26 +100,26 @@  testKeyringLookup :: FilePath -> String -> Bool -> Assertion testKeyringLookup fpr eok expected = do-    kr <- runResourceT $ CB.sourceFile ("tests/data/" ++ fpr) DC.$= conduitGet get DC.$= conduitToTKs DC.$$ sinkKeyringMap+    kr <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ fpr) DC..| conduitGet get DC..| conduitToTKs DC..| sinkKeyringMap     let key = getOne (kr @= (read eok :: EightOctetKeyId))     assertEqual (eok ++ " in " ++ fpr) expected (isJust key)  testVerifyMessage :: FilePath -> FilePath -> [TwentyOctetFingerprint] -> Assertion testVerifyMessage keyring message issuers = do-    kr <- runResourceT $ CB.sourceFile ("tests/data/" ++ keyring) DC.$= conduitGet get DC.$= conduitToTKs DC.$$ sinkKeyringMap-    verification <- runResourceT $ CB.sourceFile ("tests/data/" ++ message) DC.$= conduitGet get DC.$= conduitDecompress DC.$= conduitVerify kr Nothing DC.$$ CL.consume+    kr <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ keyring) DC..| conduitGet get DC..| conduitToTKs DC..| sinkKeyringMap+    verification <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ message) DC..| conduitGet get DC..| conduitDecompress DC..| conduitVerify kr Nothing DC..| CL.consume     let verification' = map (fmap (fingerprint . _verificationSigner)) verification     assertEqual (keyring ++ " for " ++ message) (map Right issuers) verification'  testKeysSelfVerification :: Bool -> FilePath -> Assertion testKeysSelfVerification expectsuccess keyfile = do-    ks <- runResourceT $ CB.sourceFile ("tests/data/" ++ keyfile) DC.$= conduitGet get DC.$= conduitToTKs DC.$$ CL.consume+    ks <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ keyfile) DC..| conduitGet get DC..| conduitToTKs DC..| CL.consume     let verifieds = mapM (verifyTKWith (verifySigWith (verifyAgainstKeys ks)) Nothing) ks     assertEqual (keyfile ++ " self-verification") expectsuccess (isRight verifieds)  testKeysExpiration :: Bool -> FilePath -> Assertion testKeysExpiration expectsuccess keyfile = do-    ks <- runResourceT $ CB.sourceFile ("tests/data/" ++ keyfile) DC.$= conduitGet get DC.$= conduitToTKs DC.$$ CL.consume+    ks <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ keyfile) DC..| conduitGet get DC..| conduitToTKs DC..| CL.consume     let Right verifieds = mapM (verifyTKWith (verifySigWith (verifyAgainstKeys ks)) Nothing) ks         tvalid = all (isTKTimeValid (posixSecondsToUTCTime (realToFrac (1400000000 :: Integer)))) verifieds     assertEqual (keyfile ++ " key expiration") expectsuccess tvalid@@ -128,7 +129,7 @@ testSymmetricEncryption encfile passfile cleartext = do   passphrase <- BL.readFile $ "tests/data/" ++ passfile   -- get parse tree-  pt <- runResourceT $ CB.sourceFile ("tests/data/" ++ encfile) DC.$= conduitGet get DC.$$ CL.consume+  pt <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ encfile) DC..| conduitGet get DC..| CL.consume   -- assert parse tree has exactly two packets: skesk, encdata   assertEqual "wrong number of packets" 2 (length pt)   let skesk = fromPkt.head $ pt@@ -138,7 +139,7 @@   -- and the type system chokes before we hit them:   assertEqual "first packet should be SKESK" SKESKType (packetType skesk)   assertEqual "second packet should be encrypted data" SymEncIntegrityProtectedDataType (packetType d)-  decrypted <- runResourceT $ CL.sourceList pt DC.$= conduitDecrypt (fakeCallback passphrase) DC.$$ CL.consume+  decrypted <- DC.runConduitRes $ CL.sourceList pt DC..| conduitDecrypt (fakeCallback passphrase) DC..| CL.consume   let payload = _literalDataPayload . fromPkt . head $ decrypted   assertEqual ("cleartext for " ++ encfile) cleartext payload       where@@ -148,7 +149,7 @@ testSecretKeyDecryption :: FilePath -> FilePath -> Assertion testSecretKeyDecryption keyfile passfile = do   passphrase <- BL.readFile $ "tests/data/" ++ passfile-  kr <- runResourceT $ CB.sourceFile ("tests/data/" ++ keyfile) DC.$= conduitGet get DC.$$ CL.consume+  kr <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ keyfile) DC..| conduitGet get DC..| CL.consume   let SecretKey pkp ska = fromPkt . head $ kr       SUUnencrypted skey _ = decryptPrivateKey (pkp, ska) passphrase   doPkeyAndSkeyMatch (_pubkey pkp) skey@@ -157,8 +158,8 @@ testSecretKeyEncryption :: FilePath -> FilePath -> Assertion testSecretKeyEncryption keyfile passfile = do   passphrase <- BL.readFile $ "tests/data/" ++ passfile-  kr <- runResourceT $ CB.sourceFile ("tests/data/" ++ keyfile) DC.$= conduitGet get DC.$$ CL.consume-  gkr <- runResourceT $ CB.sourceFile ("tests/data/" ++ "aes256-sha512.seckey") DC.$= conduitGet get DC.$$ CL.consume+  kr <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ keyfile) DC..| conduitGet get DC..| CL.consume+  gkr <- DC.runConduitRes $ CB.sourceFile ("tests/data/" ++ "aes256-sha512.seckey") DC..| conduitGet get DC..| CL.consume   let SecretKey pkp ska = fromPkt . head $ kr       newska = encryptPrivateKey "\226~\197\a\202#\"G" (IV "\187\219\253I\236\204\t5D\196\NAK>;\202\185\t") ska passphrase       newtruck = toPkt (SecretKey pkp newska):tail kr@@ -167,7 +168,7 @@ testParsePktsUtil :: FilePath -> Assertion testParsePktsUtil fn = do     let fpath = "tests/data/" ++ fn-    cp <- runResourceT $ CB.sourceFile fpath DC.$= conduitGet get DC.$$ CL.consume+    cp <- DC.runConduitRes $ CB.sourceFile fpath DC..| conduitGet get DC..| CL.consume     pp <- parsePkts `fmap` BL.readFile fpath     assertEqual "parsePkts utility function gives same results as conduit pipeline" cp pp @@ -175,7 +176,7 @@ testParseTKsUtil fn = do     let fpath = "tests/data/" ++ fn     lbs <- BL.readFile fpath-    cp <- runResourceT $ CB.sourceLbs lbs DC.$= conduitGet get DC.$= conduitToTKs DC.$$ CL.consume+    cp <- DC.runConduitRes $ CB.sourceLbs lbs DC..| conduitGet get DC..| conduitToTKs DC..| CL.consume     let pt = parseTKs True . parsePkts $ lbs     assertEqual "parsePkts utility function gives same results as conduit pipeline" cp pt @@ -276,10 +277,13 @@    , testCase "sigs-with-regexes" (testSerialization "sigs-with-regexes")    , testCase "gnu-dummy-s2k-101-secret-key.gpg" (testSerialization "gnu-dummy-s2k-101-secret-key.gpg")    , testCase "anibal-ed25519.gpg" (testSerialization "anibal-ed25519.gpg")+   , testCase "nist_p-256_key.gpg" (testSerialization "nist_p-256_key.gpg")+   , testCase "nist_p-256_secretkey.gpg" (testSerialization "nist_p-256_secretkey.gpg")    ],   testGroup "PKA/Size/KeyID/fingerprint group" [      testCase "v3 key" (testPKAandSizeAndKeyIDandFingerprint "v3.key" "R1024:C7261095/CBD9 F412 6807 E405 CC2D  2712 1DF5 E86E")    , testCase "v4 key" (testPKAandSizeAndKeyIDandFingerprint "000001-006.public_key" "R1248:D4D54EA16F87040E/421F 28FE AAD2 22F8 56C8  FFD5 D4D5 4EA1 6F87 040E")+   , testCase "ECDSA key" (testPKAandSizeAndKeyIDandFingerprint "nist_p-256_key.gpg" "E256:F7708BADD6063224/174C CF12 C571 6D0E 527F  B50E F770 8BAD D606 3224")    ],   testGroup "Keyring group" [      testCase "pubring 7732CF988A63EA86" (testKeyringLookup "pubring.gpg" "7732CF988A63EA86" True)@@ -308,15 +312,18 @@    , testCase "6F87040E" (testVerifyMessage "pubring.gpg" "6F87040E.pubkey" [fp "421F 28FE AAD2 22F8 56C8  FFD5 D4D5 4EA1 6F87 040E", fp "CB79 3345 9F59 C70D F1C3  FBEE DEDC 3ECF 689A F56D", fp "AF95 E4D7 BAC5 21EE 9740  BED7 5E9F 1523 4132 62DC"])    , testCase "6F87040E-cr" (testVerifyMessage "pubring.gpg" "6F87040E-cr.pubkey" [fp "AF95 E4D7 BAC5 21EE 9740  BED7 5E9F 1523 4132 62DC", fp "AF95 E4D7 BAC5 21EE 9740  BED7 5E9F 1523 4132 62DC", fp "421F 28FE AAD2 22F8 56C8  FFD5 D4D5 4EA1 6F87 040E", fp "CB79 3345 9F59 C70D F1C3  FBEE DEDC 3ECF 689A F56D", fp "AF95 E4D7 BAC5 21EE 9740  BED7 5E9F 1523 4132 62DC"])    , testCase "simple RSA secret key" (testVerifyMessage "pubring.gpg" "simple.seckey" [fp "421F 28FE AAD2 22F8 56C8  FFD5 D4D5 4EA1 6F87 040E"])+   , testCase "simple ECDSA public key" (testVerifyMessage "ecdsa-key-without-ecdh.pubkey" "ecdsa-key-without-ecdh.pubkey" [fp "174C CF12 C571 6D0E 527F  B50E F770 8BAD D606 3224"])    ],   testGroup "Key verification group" [       testCase "6F87040E pubkey" (testKeysSelfVerification True "6F87040E.pubkey")    ,  testCase "revoked pubkey" (testKeysSelfVerification False "revoked.pubkey")    ,  testCase "expired pubkey" (testKeysSelfVerification True "expired.pubkey")+   ,  testCase "nist_p-256 pubkey" (testKeysSelfVerification True "nist_p-256_key.gpg")    ],   testGroup "Key expiration group" [       testCase "6F87040E pubkey" (testKeysExpiration True "6F87040E.pubkey")    ,  testCase "expired pubkey" (testKeysExpiration False "expired.pubkey")+   ,  testCase "nist_p-256 pubkey" (testKeysExpiration True "nist_p-256_key.gpg")    ],   testGroup "Compression group" [      testCase "compressedsig.gpg" (testCompression "compressedsig.gpg")@@ -324,11 +331,11 @@    , testCase "compressedsig-bzip2.gpg" (testCompression "compressedsig-bzip2.gpg")    ],   testGroup "Conduit length group" [-     testCase "conduitCompress (ZIP)" (testConduitOutputLength "pubring.gpg" (cgp DC.=$= conduitCompress ZIP) 1)-   , testCase "conduitCompress (Zlib)" (testConduitOutputLength "pubring.gpg" (cgp DC.=$= conduitCompress ZLIB) 1)-   , testCase "conduitCompress (BZip2)" (testConduitOutputLength "pubring.gpg" (cgp DC.=$= conduitCompress BZip2) 1)-   , testCase "conduitToTKs" (testConduitOutputLength "pubring.gpg" (cgp DC.=$= conduitToTKs) 4)-   , testCase "conduitToTKsDropping" (testConduitOutputLength "pubring.gpg" (cgp DC.=$= conduitToTKsDropping) 4)+     testCase "conduitCompress (ZIP)" (testConduitOutputLength "pubring.gpg" (cgp DC..| conduitCompress ZIP) 1)+   , testCase "conduitCompress (Zlib)" (testConduitOutputLength "pubring.gpg" (cgp DC..| conduitCompress ZLIB) 1)+   , testCase "conduitCompress (BZip2)" (testConduitOutputLength "pubring.gpg" (cgp DC..| conduitCompress BZip2) 1)+   , testCase "conduitToTKs" (testConduitOutputLength "pubring.gpg" (cgp DC..| conduitToTKs) 4)+   , testCase "conduitToTKsDropping" (testConduitOutputLength "pubring.gpg" (cgp DC..| conduitToTKsDropping) 4)    ],   testGroup "Encrypted data" [      testCase "Symmetric Encryption simple S2K SHA1 3DES, no MDC" (testSymmetricEncryption "encryption-sym-3des-s2k0.gpg" "symmetric-password.txt" "test\n")@@ -360,6 +367,7 @@      testCase "SUSSHA1 CAST5 IteratedSalted SHA1 RSA" (testSecretKeyDecryption "simple.seckey" "pki-password.txt")    , testCase "SUS16bit CAST5 IteratedSalted SHA1 RSA" (testSecretKeyDecryption "16bitcksum.seckey" "pki-password.txt")    , testCase "SUSSHA1 AES256 IteratedSalted SHA512 RSA" (testSecretKeyDecryption "aes256-sha512.seckey" "pki-password.txt")+   , testCase "SUSSHA1 AES128 IteratedSalted SHA256 ECDSA" (testSecretKeyDecryption "nist_p-256_secretkey.gpg" "pki-password.txt")    ],   testGroup "Encrypting secret keys" [      testCase "SUSSHA1 AES256 IteratedSalted SHA512 RSA" (testSecretKeyEncryption "unencrypted.seckey" "pki-password.txt")@@ -383,7 +391,7 @@     \uid -> Right (uid :: UserId) == runGet get (runPut (put uid))   ] -cgp :: DC.Conduit B.ByteString (ResourceT IO) Pkt+cgp :: DC.ConduitT B.ByteString Pkt (ResourceT IO) () cgp = conduitGet (get :: Get Pkt)  fp :: Text -> TwentyOctetFingerprint@@ -391,6 +399,7 @@  doPkeyAndSkeyMatch :: PKey -> SKey -> Assertion doPkeyAndSkeyMatch (RSAPubKey (RSA_PublicKey rpub)) (RSAPrivateKey (RSA_PrivateKey rpriv)) = assertEqual "RSA private key matches RSA public key" rpub (RSA.private_pub rpriv)+doPkeyAndSkeyMatch (ECDSAPubKey (ECDSA_PublicKey ecpub)) (ECDSAPrivateKey (ECDSA_PrivateKey ecpriv)) = assertEqual "ECDSA private key curve matches ECDSA public key curve" (ECDSA.public_curve ecpub) (ECDSA.private_curve ecpriv) doPkeyAndSkeyMatch _ _ = assertFailure "matching unimplemented"  main :: IO ()