packages feed

hOpenPGP 0.8 → 0.9

raw patch · 7 files changed

+274/−32 lines, 7 filesdep +crypto-cipher-typesdep +cryptocipherdep +data-defaultPVP ok

version bump matches the API change (PVP)

Dependencies added: crypto-cipher-types, cryptocipher, data-default, monad-loops, nettle, securemem, transformers

API changes (from Hackage documentation)

- Codec.Encryption.OpenPGP.Types: _skeskMPIs :: SKESK -> [MPI]
- Codec.Encryption.OpenPGP.Types: skeskMPIs :: Lens' SKESK [MPI]
+ Codec.Encryption.OpenPGP.CFB: decrypt :: SymmetricAlgorithm -> ByteString -> ByteString -> Either String ByteString
+ Codec.Encryption.OpenPGP.CFB: decryptOpenPGPCfb :: SymmetricAlgorithm -> ByteString -> ByteString -> Either String ByteString
+ Codec.Encryption.OpenPGP.S2K: skesk2Key :: SKESK -> ByteString -> ByteString
+ Codec.Encryption.OpenPGP.Types: _skeskESK :: SKESK -> Maybe ByteString
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType CompressedData)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType LiteralData)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType Marker)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType ModificationDetectionCode)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType OnePassSignature)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType OtherPacket)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType PKESK)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType PublicKey)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType PublicSubkey)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType SKESK)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType SecretKey)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType SecretSubkey)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType Signature)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType SymEncData)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType SymEncIntegrityProtectedData)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType Trust)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType UserAttribute)
+ Codec.Encryption.OpenPGP.Types: instance Eq (PacketType UserId)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType CompressedData)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType LiteralData)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType Marker)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType ModificationDetectionCode)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType OnePassSignature)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType OtherPacket)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType PKESK)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType PublicKey)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType PublicSubkey)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType SKESK)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType SecretKey)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType SecretSubkey)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType Signature)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType SymEncData)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType SymEncIntegrityProtectedData)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType Trust)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType UserAttribute)
+ Codec.Encryption.OpenPGP.Types: instance Show (PacketType UserId)
+ Codec.Encryption.OpenPGP.Types: skeskESK :: Lens' SKESK (Maybe ByteString)
+ Data.Conduit.OpenPGP.Decrypt: conduitDecrypt :: (MonadBaseControl IO m, MonadResource m) => InputCallback IO -> Conduit Pkt m Pkt
+ Data.Conduit.OpenPGP.Decrypt: instance Default RecursorState
+ Data.Conduit.OpenPGP.Decrypt: instance Eq RecursorState
+ Data.Conduit.OpenPGP.Decrypt: instance Show RecursorState
- Codec.Encryption.OpenPGP.S2K: string2Key :: Integral a => S2K -> a -> ByteString -> ByteString
+ Codec.Encryption.OpenPGP.S2K: string2Key :: S2K -> Int -> ByteString -> ByteString
- Codec.Encryption.OpenPGP.Types: SKESK :: PacketVersion -> SymmetricAlgorithm -> S2K -> [MPI] -> SKESK
+ Codec.Encryption.OpenPGP.Types: SKESK :: PacketVersion -> SymmetricAlgorithm -> S2K -> Maybe ByteString -> SKESK
- Codec.Encryption.OpenPGP.Types: SKESKPkt :: PacketVersion -> SymmetricAlgorithm -> S2K -> [MPI] -> Pkt
+ Codec.Encryption.OpenPGP.Types: SKESKPkt :: PacketVersion -> SymmetricAlgorithm -> S2K -> (Maybe ByteString) -> Pkt

Files

+ Codec/Encryption/OpenPGP/CFB.hs view
@@ -0,0 +1,75 @@+-- CFB.hs: OpenPGP (RFC4880) CFB mode+-- Copyright © 2013 Daniel Kahn Gillmor and Clint Adams+-- This software is released under the terms of the Expat license.+-- (See the LICENSE file).++{-# LANGUAGE ExistentialQuantification #-}++module Codec.Encryption.OpenPGP.CFB (+   decrypt+ , decryptOpenPGPCfb+) where++import Codec.Encryption.OpenPGP.Types+import Crypto.Cipher.Types (makeKey, nullIV, BlockCipher(..), Cipher(..))+import qualified Crypto.Cipher as CC+import qualified Crypto.Nettle.Ciphers as CNC+import qualified Data.ByteString as B+import Data.Maybe (fromJust)+import Data.SecureMem (ToSecureMem)++data BCipher = forall a. (BlockCipher a) => BCipher a++decryptOpenPGPCfb :: SymmetricAlgorithm -> B.ByteString -> B.ByteString -> Either String B.ByteString+decryptOpenPGPCfb Plaintext ciphertext _ = return ciphertext+decryptOpenPGPCfb sa ciphertext keydata = do+    bc <- mkBCipher sa keydata+    return $ B.drop (bsize bc + 2) (decrypt1 ciphertext bc `B.append` decrypt2 ciphertext bc) -- FIXME: verify the code+    where+        decrypt1 :: B.ByteString -> BCipher -> B.ByteString+        decrypt1 ct (BCipher cipher) = cdecrypt sa cipher nullIV (B.take (blockSize cipher + 2) ct)+        decrypt2 :: B.ByteString -> BCipher -> B.ByteString+        decrypt2 ct (BCipher cipher) = cdecrypt sa cipher (fromJust (CC.makeIV (B.take (blockSize cipher) (B.drop 2 ct)))) (B.drop (blockSize cipher + 2) ct)++decrypt :: SymmetricAlgorithm -> B.ByteString -> B.ByteString -> Either String B.ByteString+decrypt Plaintext ciphertext _ = return ciphertext+decrypt sa ciphertext keydata = do+    bc <- mkBCipher sa keydata+    return $ B.drop (bsize bc + 2) (decrypt' ciphertext bc)  -- FIXME: verify the code+    where+        decrypt' :: B.ByteString -> BCipher -> B.ByteString+        decrypt' ct (BCipher cipher) = cdecrypt sa cipher nullIV ct++bsize :: BCipher -> Int+bsize (BCipher bc) = blockSize bc++cdecrypt :: BlockCipher cipher => SymmetricAlgorithm -> cipher -> CC.IV cipher -> B.ByteString -> B.ByteString+cdecrypt sa+    | sa `elem` [CAST5, Twofish] = paddedCfbDecrypt+    | otherwise = cfbDecrypt++paddedCfbDecrypt :: BlockCipher cipher => cipher -> CC.IV cipher -> B.ByteString -> B.ByteString+paddedCfbDecrypt cipher iv ciphertext = B.take (B.length ciphertext) (cfbDecrypt cipher iv padded)+    where+        padded = ciphertext `B.append` B.pack (replicate (blockSize cipher - (B.length ciphertext `mod` blockSize cipher)) 0)++mkBCipher :: ToSecureMem b => SymmetricAlgorithm -> b -> Either String BCipher+mkBCipher Plaintext = const (fail "this shouldn't have happened") -- FIXME: orphan instance?+mkBCipher IDEA = const (fail "IDEA not yet implemented") -- FIXME: IDEA+mkBCipher ReservedSAFER = const (fail "SAFER not implemented") -- FIXME: or not?+mkBCipher ReservedDES = const (fail "DES not implemented") -- FIXME: or not?+mkBCipher (OtherSA _) = const (fail "Unknown, unimplemented symmetric algorithm")+mkBCipher CAST5 = return . BCipher . (ciph :: ToSecureMem b => b -> CNC.CAST128)+mkBCipher Twofish = return . BCipher . (ciph :: ToSecureMem b => b -> CNC.TWOFISH)+mkBCipher TripleDES = return . BCipher . (ciph :: ToSecureMem b => b -> CC.DES_EDE3)+mkBCipher Blowfish = return . BCipher . (ciph :: ToSecureMem b => b -> CC.Blowfish128)+mkBCipher AES128 = return . BCipher . (ciph :: ToSecureMem b => b -> CC.AES128)+mkBCipher AES192 = return . BCipher . (ciph :: ToSecureMem b => b -> CC.AES192)+mkBCipher AES256 = return . BCipher . (ciph :: ToSecureMem b => b -> CC.AES256)++ciph :: (CC.BlockCipher cipher, ToSecureMem b) => b -> cipher -- FIXME: return an Either String cipher ?+ciph keydata = cipherInit ekey+    where+        ekey = case makeKey keydata of+          Left _ -> error "bad cipher parameters"+          Right key -> key
Codec/Encryption/OpenPGP/S2K.hs view
@@ -5,17 +5,44 @@  module Codec.Encryption.OpenPGP.S2K (   string2Key+  ,skesk2Key ) where  import Codec.Encryption.OpenPGP.Types+import Control.Monad.Loops (untilM_)+import Control.Monad.Trans.State.Lazy (execState, get, put) import qualified Crypto.Hash.SHA1 as SHA1 import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL -string2Key :: Integral a => S2K -> a -> BL.ByteString -> B.ByteString-string2Key (Simple SHA1) ksz bs-    | 20 < ksz = error "FIXME"-    | otherwise = B.take (fromIntegral ksz) . SHA1.hashlazy $ bs+string2Key :: S2K -> Int -> BL.ByteString -> B.ByteString+string2Key (Simple SHA1) ksz bs = B.take (fromIntegral ksz) $ hashpp SHA1.hashlazy ksz bs string2Key (Salted SHA1 salt) ksz bs = string2Key (Simple SHA1) ksz (BL.append (BL.fromChunks [salt]) bs) string2Key (IteratedSalted SHA1 salt cnt) ksz bs = string2Key (Simple SHA1) ksz (BL.take (fromIntegral cnt) . BL.cycle $ BL.append (BL.fromChunks [salt]) bs)-string2Key _ _ _ = error "FIXME"+string2Key _ _ _ = error "FIXME: unimplemented S2K hash"++-- in bytes+keySize :: SymmetricAlgorithm -> Int+keySize Plaintext = 0+keySize IDEA = 16 -- according to https://en.wikipedia.org/wiki/International_Data_Encryption_Algorithm+keySize TripleDES = 24 -- RFC 4880 says 168 bits (derived from 192 bits) but we don't know who does the derivation+keySize CAST5 = 16+keySize Blowfish = 16+keySize ReservedSAFER = undefined+keySize ReservedDES = undefined+keySize AES128 = 16+keySize AES192 = 24+keySize AES256 = 32+keySize Twofish = 32+keySize (OtherSA _) = undefined++skesk2Key :: SKESK -> BL.ByteString -> B.ByteString+skesk2Key (SKESK 4 sa s2k Nothing) pass = string2Key s2k (keySize sa) pass+skesk2Key _ _ = error "FIXME"++hashpp :: (BL.ByteString -> B.ByteString) -> Int -> BL.ByteString -> B.ByteString+hashpp hf keysize pp = snd (execState (hashround `untilM_` bigEnough) (0, B.empty))+    where+        hashround = get >>= \(ctr, bs) -> put (ctr + 1, bs `B.append` hf (nulpad ctr `BL.append` pp))+        nulpad = BL.pack . flip replicate 0+	bigEnough = get >>= \(_, bs) -> return (B.length bs >= keysize)
Codec/Encryption/OpenPGP/Serialize.hs view
@@ -23,6 +23,7 @@ import Data.Set (Set) import qualified Data.Set as Set import Data.Word (Word8, Word32)+import Data.Maybe (fromMaybe)  import Codec.Encryption.OpenPGP.Internal (countBits, beBSToInteger, integerToBEBS, pubkeyToMPIs) import Codec.Encryption.OpenPGP.Types@@ -526,10 +527,8 @@                           symalgo <- getWord8                           s2k <- getS2K                           remainder <- remaining-                          mpib <- getBytes remainder-                          case runGet (many getMPI) mpib of-                              Left _ -> return $ SKESKPkt pv (toFVal symalgo) s2k []-                              Right mpis -> return $ SKESKPkt pv (toFVal symalgo) s2k mpis+                          esk <- getByteString remainder+                          return $ SKESKPkt pv (toFVal symalgo) s2k (if B.null esk then Nothing else Just esk)             | t == 4 = do                           pv <- getWord8                           sigtype <- getWord8@@ -649,10 +648,10 @@     let bs = runPut $ put sp     putPacketLength . fromIntegral . B.length $ bs     putByteString bs-putPkt (SKESKPkt pv symalgo s2k mpis) = do+putPkt (SKESKPkt pv symalgo s2k mesk) = do     putWord8 (0xc0 .|. 3)     let bs2k = fromS2K s2k-    let bsk = runPut $ mapM_ put mpis+    let bsk = fromMaybe B.empty mesk     putPacketLength . fromIntegral $ 2 + (B.length bs2k) + (B.length bsk)     putWord8 pv -- should be 4     putWord8 $ fromIntegral . fromFVal $ symalgo
Codec/Encryption/OpenPGP/Types.hs view
@@ -566,7 +566,7 @@ -- data Pkt = forall a. (Packet a, Show a, Eq a) => Pkt a data Pkt = PKESKPkt PacketVersion EightOctetKeyId PubKeyAlgorithm [MPI]          | SignaturePkt SignaturePayload-         | SKESKPkt PacketVersion SymmetricAlgorithm S2K [MPI]+         | SKESKPkt PacketVersion SymmetricAlgorithm S2K (Maybe B.ByteString)          | OnePassSignaturePkt PacketVersion SigType HashAlgorithm PubKeyAlgorithm EightOctetKeyId NestedFlag          | SecretKeyPkt PKPayload SKAddendum          | PublicKeyPkt PKPayload@@ -591,7 +591,7 @@     , _pkeskMPIs :: [MPI]     } deriving (Data, Eq, Show, Typeable) instance Packet PKESK where-    data PacketType PKESK = PKESKType+    data PacketType PKESK = PKESKType deriving (Show, Eq)     packetType _ = PKESKType     packetCode _ = 1     toPkt (PKESK a b c d) = PKESKPkt a b c d@@ -601,7 +601,7 @@     { _signaturePayload :: SignaturePayload     } deriving (Data, Eq, Show, Typeable) instance Packet Signature where-    data PacketType Signature = SignatureType+    data PacketType Signature = SignatureType deriving (Show, Eq)     packetType _ = SignatureType     packetCode _ = 2     toPkt (Signature a ) = SignaturePkt a@@ -611,10 +611,10 @@     { _skeskPacketVersion :: PacketVersion     , _skeskSymmetricAlgorithm :: SymmetricAlgorithm     , _skeskS2K :: S2K-    , _skeskMPIs :: [MPI]+    , _skeskESK :: Maybe B.ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet SKESK where-    data PacketType SKESK = SKESKType+    data PacketType SKESK = SKESKType deriving (Show, Eq)     packetType _ = SKESKType     packetCode _ = 3     toPkt (SKESK a b c d) = SKESKPkt a b c d@@ -629,7 +629,7 @@     , _onePassSignatureNestedFlag :: NestedFlag     } deriving (Data, Eq, Show, Typeable) instance Packet OnePassSignature where-    data PacketType OnePassSignature = OnePassSignatureType+    data PacketType OnePassSignature = OnePassSignatureType deriving (Show, Eq)     packetType _ = OnePassSignatureType     packetCode _ = 4     toPkt (OnePassSignature a b c d e f) = OnePassSignaturePkt a b c d e f@@ -640,7 +640,7 @@     , _secretKeySKAddendum :: SKAddendum     } deriving (Data, Eq, Show, Typeable) instance Packet SecretKey where-    data PacketType SecretKey = SecretKeyType+    data PacketType SecretKey = SecretKeyType deriving (Show, Eq)     packetType _ = SecretKeyType     packetCode _ = 5     toPkt (SecretKey a b) = SecretKeyPkt a b@@ -650,7 +650,7 @@     { _publicKeyPKPayload :: PKPayload     } deriving (Data, Eq, Show, Typeable) instance Packet PublicKey where-    data PacketType PublicKey = PublicKeyType+    data PacketType PublicKey = PublicKeyType deriving (Show, Eq)     packetType _ = PublicKeyType     packetCode _ = 6     toPkt (PublicKey a) = PublicKeyPkt a@@ -661,7 +661,7 @@     , _secretSubkeySKAddendum :: SKAddendum     } deriving (Data, Eq, Show, Typeable) instance Packet SecretSubkey where-    data PacketType SecretSubkey = SecretSubkeyType+    data PacketType SecretSubkey = SecretSubkeyType deriving (Show, Eq)     packetType _ = SecretSubkeyType     packetCode _ = 7     toPkt (SecretSubkey a b) = SecretSubkeyPkt a b@@ -672,7 +672,7 @@     , _compressedDataPayload :: CompressedDataPayload     } deriving (Data, Eq, Show, Typeable) instance Packet CompressedData where-    data PacketType CompressedData = CompressedDataType+    data PacketType CompressedData = CompressedDataType deriving (Show, Eq)     packetType _ = CompressedDataType     packetCode _ = 8     toPkt (CompressedData a b) = CompressedDataPkt a b@@ -682,7 +682,7 @@     { _symEncDataPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet SymEncData where-    data PacketType SymEncData = SymEncDataType+    data PacketType SymEncData = SymEncDataType deriving (Show, Eq)     packetType _ = SymEncDataType     packetCode _ = 9     toPkt (SymEncData a) = SymEncDataPkt a@@ -692,7 +692,7 @@     { _markerPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet Marker where-    data PacketType Marker = MarkerType+    data PacketType Marker = MarkerType deriving (Show, Eq)     packetType _ = MarkerType     packetCode _ = 10     toPkt (Marker a) = MarkerPkt a@@ -705,7 +705,7 @@     , _literalDataPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet LiteralData where-    data PacketType LiteralData = LiteralDataType+    data PacketType LiteralData = LiteralDataType deriving (Show, Eq)     packetType _ = LiteralDataType     packetCode _ = 11     toPkt (LiteralData a b c d) = LiteralDataPkt a b c d@@ -715,7 +715,7 @@     { _trustPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet Trust where-    data PacketType Trust = TrustType+    data PacketType Trust = TrustType deriving (Show, Eq)     packetType _ = TrustType     packetCode _ = 12     toPkt (Trust a) = TrustPkt a@@ -725,7 +725,7 @@     { _userIdPayload :: String     } deriving (Data, Eq, Show, Typeable) instance Packet UserId where-    data PacketType UserId = UserIdType+    data PacketType UserId = UserIdType deriving (Show, Eq)     packetType _ = UserIdType     packetCode _ = 13     toPkt (UserId a) = UserIdPkt a@@ -735,7 +735,7 @@     { _publicSubkeyPKPayload :: PKPayload     } deriving (Data, Eq, Show, Typeable) instance Packet PublicSubkey where-    data PacketType PublicSubkey = PublicSubkeyType+    data PacketType PublicSubkey = PublicSubkeyType deriving (Show, Eq)     packetType _ = PublicSubkeyType     packetCode _ = 14     toPkt (PublicSubkey a) = PublicSubkeyPkt a@@ -745,7 +745,7 @@     { _userAttributeSubPackets :: [UserAttrSubPacket]     } deriving (Data, Eq, Show, Typeable) instance Packet UserAttribute where-    data PacketType UserAttribute = UserAttributeType+    data PacketType UserAttribute = UserAttributeType deriving (Show, Eq)     packetType _ = UserAttributeType     packetCode _ = 17     toPkt (UserAttribute a) = UserAttributePkt a@@ -756,7 +756,7 @@     , _symEncIntegrityProtectedDataPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet SymEncIntegrityProtectedData where-    data PacketType SymEncIntegrityProtectedData = SymEncIntegrityProtectedDataType+    data PacketType SymEncIntegrityProtectedData = SymEncIntegrityProtectedDataType deriving (Show, Eq)     packetType _ = SymEncIntegrityProtectedDataType     packetCode _ = 18     toPkt (SymEncIntegrityProtectedData a b) = SymEncIntegrityProtectedDataPkt a b@@ -766,7 +766,7 @@     { _modificationDetectionCodePayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet ModificationDetectionCode where-    data PacketType ModificationDetectionCode = ModificationDetectionCodeType+    data PacketType ModificationDetectionCode = ModificationDetectionCodeType deriving (Show, Eq)     packetType _ = ModificationDetectionCodeType     packetCode _ = 19     toPkt (ModificationDetectionCode a) = ModificationDetectionCodePkt a@@ -777,7 +777,7 @@     , _otherPacketPayload :: ByteString     } deriving (Data, Eq, Show, Typeable) instance Packet OtherPacket where-    data PacketType OtherPacket = OtherPacketType+    data PacketType OtherPacket = OtherPacketType deriving (Show, Eq)     packetType _ = OtherPacketType     packetCode _ = undefined -- FIXME     toPkt (OtherPacket a b) = OtherPacketPkt a b
+ Data/Conduit/OpenPGP/Decrypt.hs view
@@ -0,0 +1,72 @@+-- Decrypt.hs: OpenPGP (RFC4880) recursive packet decryption+-- Copyright © 2013  Clint Adams+-- This software is released under the terms of the Expat license.+-- (See the LICENSE file).++{-# LANGUAGE FlexibleContexts #-}++module Data.Conduit.OpenPGP.Decrypt (+   conduitDecrypt+) where++import Control.Monad.IO.Class (MonadIO(..))+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL+import Data.Conduit+import qualified Data.Conduit.Binary as CB+import Data.Conduit.Cereal (conduitGet)+import Data.Conduit.OpenPGP.Compression (conduitDecompress)+import qualified Data.Conduit.List as CL+import Data.Default (Default, def)+import Data.Maybe (fromJust)+import Data.Serialize (get)++import Codec.Encryption.OpenPGP.S2K (skesk2Key)+import Codec.Encryption.OpenPGP.CFB (decrypt, decryptOpenPGPCfb)+import Codec.Encryption.OpenPGP.Types++data RecursorState = RecursorState {+     _depth    :: Int+  , _lastPKESK :: Maybe PKESK+  , _lastSKESK :: Maybe SKESK+} deriving (Eq, Show)++instance Default RecursorState where+    def = RecursorState 0 Nothing Nothing++type InputCallback m = String -> m BL.ByteString++conduitDecrypt :: (MonadBaseControl IO m, MonadResource m) => InputCallback IO -> Conduit Pkt m Pkt+conduitDecrypt = conduitDecrypt' 0++conduitDecrypt' :: (MonadBaseControl IO m, MonadResource m) => Int -> InputCallback IO -> Conduit Pkt m Pkt+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 i s+            | _depth s > 42 = fail "I think we've been quine-attacked"+	    | otherwise = case i of+                       (SKESKPkt {}) -> return (s { _lastSKESK = Just (fromPkt i) }, [])+                       (SymEncDataPkt bs) -> do d <- decryptSEDP (_depth s) cb (fromJust . _lastSKESK $ s) bs+                                                return (s, d)+                       (SymEncIntegrityProtectedDataPkt _ bs) -> do d <- decryptSEIPDP (_depth s) cb (fromJust . _lastSKESK $ s) bs+                                                                    return (s, d)+                       p -> return (s, [p])++decryptSEDP :: (MonadBaseControl IO m, MonadIO m, MonadThrow m, MonadUnsafeIO m) => Int -> InputCallback IO -> SKESK -> B.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) bs key of+                        Left e -> error e+                        Right x -> x+    runResourceT $ CB.sourceLbs (BL.fromStrict decrypted) $= conduitGet get $= conduitDecompress $= conduitDecrypt' depth cb $$ CL.consume++decryptSEIPDP :: (MonadBaseControl IO m, MonadIO m, MonadThrow m, MonadUnsafeIO m) => Int -> InputCallback IO -> SKESK -> B.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) bs key of+                        Left e -> error e+                        Right x -> x+    runResourceT $ CB.sourceLbs (BL.fromStrict decrypted) $= conduitGet get $= conduitDecompress $= conduitDecrypt' depth cb $$ CL.consume
hOpenPGP.cabal view
@@ -1,5 +1,5 @@ Name:                hOpenPGP-Version:             0.8+Version:             0.9 Synopsis:            native Haskell implementation of OpenPGP (RFC4880) Description:         native Haskell implementation of OpenPGP (RFC4880) Homepage:            http://floss.scru.org/hOpenPGP/@@ -122,8 +122,10 @@                      , Codec.Encryption.OpenPGP.Fingerprint                      , Codec.Encryption.OpenPGP.KeyInfo                      , Codec.Encryption.OpenPGP.S2K+                     , Codec.Encryption.OpenPGP.CFB                      , Codec.Encryption.OpenPGP.Signatures                      , Data.Conduit.OpenPGP.Compression+                     , Data.Conduit.OpenPGP.Decrypt                      , Data.Conduit.OpenPGP.Keyring                      , Data.Conduit.OpenPGP.Keyring.Instances                      , Data.Conduit.OpenPGP.Verify@@ -139,13 +141,20 @@                , conduit               >= 0.5    && < 1.1                , containers                , crypto-pubkey         >= 0.1.4+               , cryptocipher+               , crypto-cipher-types                , cryptohash+               , data-default                , ixset                 >= 1.0                , lens                  >= 3.0+               , monad-loops                , mtl+               , nettle                , openpgp-asciiarmor                 >= 0.1+               , securemem                , split                , time                               >= 1.1+               , transformers                , zlib   default-language: Haskell2010 @@ -163,11 +172,19 @@                , conduit                , containers                , crypto-pubkey         >= 0.1.4+               , cryptocipher+               , crypto-cipher-types                , cryptohash+               , data-default                , ixset                 >= 1.0                , lens                  >= 3.0+               , monad-loops+               , mtl+               , nettle+               , securemem                , split                , time                               >= 1.1+               , transformers                , zlib                , HUnit                , test-framework@@ -179,3 +196,8 @@ source-repository head   type:     git   location: git://git.debian.org/users/clint/hOpenPGP.git++source-repository this+  type:     git+  location: git://git.debian.org/users/clint/hOpenPGP.git+  tag:      v0.9
tests/suite.hs view
@@ -16,10 +16,13 @@ import Codec.Encryption.OpenPGP.Types import Data.Conduit.Cereal (conduitGet) import Data.Conduit.OpenPGP.Compression (conduitCompress, conduitDecompress)+import Data.Conduit.OpenPGP.Decrypt (conduitDecrypt) import Data.Conduit.OpenPGP.Keyring (conduitToTKs, conduitToTKsDropping, sinkKeyringMap) import Data.Conduit.OpenPGP.Verify (conduitVerify)+import Codec.Encryption.OpenPGP.Serialize ()  import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as BL import Data.IxSet ((@=), getOne) import Data.Maybe (isJust) import Data.Serialize (get, put)@@ -87,6 +90,28 @@     let verification' = map (fmap (fingerprint . _verificationSigner)) verification     assertEqual (keyring ++ " for " ++ message) (map Right issuers) verification' +-- This needs a lot of work+testSymmetricEncryption :: FilePath -> FilePath -> B.ByteString -> Assertion+testSymmetricEncryption encfile passfile cleartext = do+  passphrase <- BL.readFile $ "tests/data/" ++ passfile+  -- get parse tree+  pt <- DC.runResourceT $ 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+      d = fromPkt.last $ pt+  -- FIXME: these assertions don't currently do anything properly,+  -- because haskell notices the _-prefixed accessor invocations below+  -- 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 <- DC.runResourceT $ CL.sourceList pt DC.$= conduitDecrypt (fakeCallback passphrase) DC.$$ CL.consume+  let payload = _literalDataPayload . fromPkt . head $ decrypted+  assertEqual ("cleartext for " ++ encfile) cleartext payload+      where+          fakeCallback :: BL.ByteString -> String -> IO BL.ByteString+          fakeCallback = const . return+ tests :: [Test] tests = [   testGroup "Serialization group" [@@ -221,6 +246,28 @@    , 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")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 3DES, no MDC" (testSymmetricEncryption "encryption-sym-3des.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 3DES" (testSymmetricEncryption "encryption-sym-3des-mdc-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 3DES" (testSymmetricEncryption "encryption-sym-3des-mdc.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 CAST5, no MDC" (testSymmetricEncryption "encryption-sym-cast5-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 CAST5, no MDC" (testSymmetricEncryption "encryption-sym-cast5.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 CAST5" (testSymmetricEncryption "encryption-sym-cast5-mdc-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 CAST5" (testSymmetricEncryption "encryption-sym-cast5-mdc.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 Blowfish, no MDC" (testSymmetricEncryption "encryption-sym-blowfish-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 Blowfish, no MDC" (testSymmetricEncryption "encryption-sym-blowfish.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 Blowfish" (testSymmetricEncryption "encryption-sym-blowfish-mdc-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 Blowfish" (testSymmetricEncryption "encryption-sym-blowfish-mdc.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 AES128" (testSymmetricEncryption "encryption-sym-aes128-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 AES128" (testSymmetricEncryption "encryption-sym-aes128.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 AES192" (testSymmetricEncryption "encryption-sym-aes192-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 AES192" (testSymmetricEncryption "encryption-sym-aes192.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 AES256" (testSymmetricEncryption "encryption-sym-aes256-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 AES256" (testSymmetricEncryption "encryption-sym-aes256.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption simple S2K SHA1 Twofish" (testSymmetricEncryption "encryption-sym-twofish-s2k0.gpg" "symmetric-password.txt" "test\n")+   , testCase "Symmetric Encryption iterated-salted S2K SHA1 Twofish" (testSymmetricEncryption "encryption-sym-twofish.gpg" "symmetric-password.txt" "test\n")    ]  ]