diff --git a/Codec/Encryption/OpenPGP/Serialize.hs b/Codec/Encryption/OpenPGP/Serialize.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Encryption/OpenPGP/Serialize.hs
@@ -0,0 +1,823 @@
+-- Serialize.hs: OpenPGP (RFC4880) serialization (using cereal)
+-- Copyright Ⓒ 2012  Clint Adams
+-- This software is released under the terms of the Expat (MIT) license.
+-- (See the LICENSE file).
+
+module Codec.Encryption.OpenPGP.Serialize (
+    getPackets
+  , putPackets
+) where
+
+import Control.Applicative ((<$>),(<*>))
+import Control.Monad (replicateM, mplus, when)
+import Data.Bits ((.&.), (.|.), shiftL, shiftR)
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Char8 as BC8
+import Data.List (mapAccumL)
+import Data.Maybe (isJust, fromJust)
+import Data.Serialize (Serialize, get, put)
+import Data.Serialize.Get (Get, getWord8, getWord16be, getWord32be, getBytes, getByteString, getWord16le, runGet, remaining)
+import Data.Serialize.Put (Put, putWord8, putWord16be, putWord32be, putByteString, putWord16le, runPut)
+import Data.Set (Set)
+import qualified Data.Set as Set
+import Data.Word (Word8)
+
+import Codec.Encryption.OpenPGP.Types
+
+instance Serialize SigSubPacket where
+    get = getSigSubPacket
+    put = putSigSubPacket
+
+-- instance Serialize (Set NotationFlag) where
+--     put = putNotationFlagSet
+
+instance Serialize CompressionAlgorithm where
+    get = getWord8 >>= return . toFVal
+    put = putWord8 . fromFVal
+
+instance Serialize PubKeyAlgorithm where
+    get = getWord8 >>= return . toFVal
+    put = putWord8 . fromFVal
+
+instance Serialize HashAlgorithm where
+    get = getWord8 >>= return . toFVal
+    put = putWord8 . fromFVal
+
+instance Serialize SymmetricAlgorithm where
+    get = getWord8 >>= return . toFVal
+    put = putWord8 . fromFVal
+
+instance Serialize MPI where
+    get = getMPI
+    put = putMPI
+
+instance Serialize SigType where
+    get = getWord8 >>= return . toFVal
+    put = putWord8 . fromFVal
+
+instance Serialize UserAttrSubPacket where
+    get = getUserAttrSubPacket
+    put = putUserAttrSubPacket
+
+instance Serialize S2K where
+    get = getS2K
+    put = putS2K
+
+getSigSubPacket :: Get SigSubPacket
+getSigSubPacket = do
+    l <- getSubPacketLength
+    (crit, pt) <- getSigSubPacketType
+    getSigSubPacket' pt crit l
+    where
+        getSigSubPacket' :: Word8 -> Bool -> Int -> Get SigSubPacket
+        getSigSubPacket' pt crit l
+            | pt == 2 = do
+                       et <- getWord32be
+                       return $ SigCreationTime crit et
+            | pt == 3 = do
+                       et <- getWord32be
+                       return $ SigExpirationTime crit et
+            | pt == 4 = do
+                       e <- get
+                       return $ ExportableCertification crit e
+            | pt == 5 = do
+                       tl <- getWord8
+                       ta <- getWord8
+                       return $ TrustSignature crit tl ta
+            | pt == 6 = do
+                       apdre <- getByteString (l - 2)
+                       return $ RegularExpression crit (B.copy apdre)
+            | pt == 7 = do
+                       r <- get
+                       return $ Revocable crit r
+            | pt == 9 = do
+                       et <- getWord32be
+                       return $ KeyExpirationTime crit et
+            | pt == 11 = do
+                       sa <- replicateM (l - 1) get
+                       return $ PreferredSymmetricAlgorithms crit sa
+            | pt == 12 = do
+                       rclass <- getWord8
+                       algid <- get
+                       fp <- getByteString 20
+                       return $ RevocationKey crit (bsToFFSet . B.singleton $ rclass) algid fp
+            | pt == 16 = do
+                       keyid <- getByteString (l - 1)
+                       return $ Issuer crit keyid
+            | pt == 20 = do
+                       flags <- getByteString 4
+                       nl <- getWord16be
+                       vl <- getWord16be
+                       nd <- getByteString (fromIntegral nl)
+                       nv <- getByteString (fromIntegral vl)
+                       return $ NotationData crit (bsToFFSet flags) nd nv
+            | pt == 21 = do
+                       ha <- replicateM (l - 1) get
+                       return $ PreferredHashAlgorithms crit ha
+            | pt == 22 = do
+                       ca <- replicateM (l - 1) get
+                       return $ PreferredCompressionAlgorithms crit ca
+            | pt == 23 = do
+                       ksps <- getByteString (l - 1)
+                       return $ KeyServerPreferences crit (bsToFFSet ksps)
+            | pt == 24 = do
+                       pks <- getByteString (l - 1)
+                       return $ PreferredKeyServer crit pks
+            | pt == 25 = do
+                       primacy <- get
+                       return $ PreferredKeyServer crit primacy
+            | pt == 26 = do
+                       url <- getByteString (l - 1)
+                       return $ PolicyURL crit url
+            | pt == 27 = do
+                       kfs <- getByteString (l - 1)
+                       return $ KeyFlags crit (bsToFFSet kfs)
+            | pt == 28 = do
+                       uid <- getByteString (l - 1)
+                       return $ SignersUserId crit uid
+            | pt == 29 = do
+                       rcode <- getWord8
+                       rreason <- getByteString (l - 2)
+                       return $ ReasonForRevocation crit (toFVal rcode) rreason
+            | pt == 30 = do
+                       fbs <- getByteString (l - 1)
+                       return $ Features crit (bsToFFSet fbs)
+            | pt == 31 = do
+                       pka <- get
+                       ha <- get
+                       hash <- getByteString (l - 3)
+                       return $ SignatureTarget crit pka ha hash
+            | pt == 32 = do
+                       spb <- getByteString (l - 1)
+                       return $ EmbeddedSignature crit spb -- FIXME: should be sigpacket
+            | pt > 99 && pt < 111 = do
+                       payload <- getByteString (l - 1)
+                       return $ UserDefinedSigSub crit pt payload
+            | otherwise = do
+                       payload <- getByteString (l - 1)
+                       return $ OtherSigSub crit pt payload
+
+putSigSubPacket :: SigSubPacket -> Put
+putSigSubPacket (SigCreationTime crit et) = do
+    putSubPacketLength 5
+    putSigSubPacketType crit 2
+    putWord32be et
+putSigSubPacket (SigExpirationTime crit et) = do
+    putSubPacketLength 5
+    putSigSubPacketType crit 3
+    putWord32be et
+putSigSubPacket (ExportableCertification crit e) = do
+    putSubPacketLength 2
+    putSigSubPacketType crit 4
+    put e
+putSigSubPacket (TrustSignature crit tl ta) = do
+    putSubPacketLength 3
+    putSigSubPacketType crit 5
+    put tl
+    put ta
+putSigSubPacket (RegularExpression crit apdre) = do
+    putSubPacketLength (2 + B.length apdre)
+    putSigSubPacketType crit 6
+    putByteString apdre
+    putWord8 0
+putSigSubPacket (Revocable crit r) = do
+    putSubPacketLength 2
+    putSigSubPacketType crit 7
+    put r
+putSigSubPacket (KeyExpirationTime crit et) = do
+    putSubPacketLength 5
+    putSigSubPacketType crit 9
+    putWord32be et
+putSigSubPacket (PreferredSymmetricAlgorithms crit ess) = do
+    putSubPacketLength (1 + length ess)
+    putSigSubPacketType crit 11
+    mapM_ put ess
+putSigSubPacket (RevocationKey crit rclass algid fp) = do
+    putSubPacketLength 23
+    putSigSubPacketType crit 12
+    putByteString . ffSetToFixedLengthBS 1 $ rclass
+    put algid
+    putByteString fp -- 20 octets
+putSigSubPacket (Issuer crit keyid) = do
+    putSubPacketLength 9
+    putSigSubPacketType crit 16
+    putByteString keyid -- 8 octets
+putSigSubPacket (NotationData crit nfs nn nv) = do
+    putSubPacketLength (9 + B.length nn + B.length nv)
+    putSigSubPacketType crit 20
+    putByteString . ffSetToFixedLengthBS 4 $ nfs
+    putWord16be . fromIntegral . B.length $ nn
+    putWord16be . fromIntegral . B.length $ nv
+    putByteString nn
+    putByteString nv
+putSigSubPacket (PreferredHashAlgorithms crit ehs) = do
+    putSubPacketLength (1 + length ehs)
+    putSigSubPacketType crit 21
+    mapM_ put ehs
+putSigSubPacket (PreferredCompressionAlgorithms crit ecs) = do
+    putSubPacketLength (1 + length ecs)
+    putSigSubPacketType crit 22
+    mapM_ put ecs
+putSigSubPacket (KeyServerPreferences crit ksps) = do
+    let kbs = ffSetToBS ksps
+    putSubPacketLength (1 + B.length kbs)
+    putSigSubPacketType crit 23
+    putByteString kbs
+putSigSubPacket (PreferredKeyServer crit ks) = do
+    putSubPacketLength (1 + B.length ks)
+    putSigSubPacketType crit 24
+    putByteString ks
+putSigSubPacket (PrimaryUserId crit primacy) = do
+    putSubPacketLength 2
+    putSigSubPacketType crit 25
+    put primacy
+putSigSubPacket (PolicyURL crit url) = do
+    putSubPacketLength (1 + B.length url)
+    putSigSubPacketType crit 26
+    putByteString url
+putSigSubPacket (KeyFlags crit kfs) = do
+    let kbs = ffSetToBS kfs
+    putSubPacketLength (1 + B.length kbs)
+    putSigSubPacketType crit 27
+    putByteString kbs
+putSigSubPacket (SignersUserId crit userid) = do
+    putSubPacketLength (1 + B.length userid)
+    putSigSubPacketType crit 28
+    putByteString userid
+putSigSubPacket (ReasonForRevocation crit rcode rreason) = do
+    putSubPacketLength (2 + B.length rreason)
+    putSigSubPacketType crit 29
+    putWord8 . fromFVal $ rcode
+    putByteString rreason
+putSigSubPacket (Features crit fs) = do
+    let fbs = ffSetToBS fs
+    putSubPacketLength (1 + B.length fbs)
+    putSigSubPacketType crit 30
+    putByteString fbs
+putSigSubPacket (SignatureTarget crit pka ha hash) = do
+    putSubPacketLength (3 + B.length hash)
+    putSigSubPacketType crit 31
+    put pka
+    put ha
+    putByteString hash
+putSigSubPacket (EmbeddedSignature crit spb) = do
+    putSubPacketLength (1 + B.length spb)
+    putSigSubPacketType crit 32
+    putByteString spb
+putSigSubPacket (UserDefinedSigSub crit ptype payload) = do
+    putSubPacketLength (1 + B.length payload)
+    putSigSubPacketType crit ptype
+    putByteString payload
+putSigSubPacket (OtherSigSub crit ptype payload) = do
+    putSubPacketLength (1 + B.length payload)
+    putSigSubPacketType crit ptype
+    putByteString payload
+
+getSubPacketLength :: Integral a => Get a
+getSubPacketLength = getSubPacketLength' =<< getWord8
+    where
+        getSubPacketLength' :: Integral a => Word8 -> Get a
+        getSubPacketLength' f
+            | f < 192 = return . fromIntegral $ f
+            | f < 224 = do
+                           secondOctet <- getWord8
+                           return . fromIntegral $ shiftL (fromIntegral (f - 192) :: Int) 8 + (fromIntegral secondOctet :: Int) + 192
+            | f == 255 = do
+                           len <- getWord32be
+                           return . fromIntegral $ len
+            | otherwise = fail "Partial body length invalid."
+
+putSubPacketLength :: Integral a => a -> Put
+putSubPacketLength l
+    | l < 192 = putWord8 (fromIntegral l)
+    | l < 8384 = putWord8 (fromIntegral ((fromIntegral (l - 192) `shiftR` 8) + 192 :: Int)) >> putWord8 (fromIntegral (l - 192) .&. 0xff)
+    | l < 0x100000000 = putWord8 255 >> putWord32be (fromIntegral l)
+    | otherwise = fail "too big"
+
+getSigSubPacketType :: Get (Bool, Word8)
+getSigSubPacketType = do
+                         x <- getWord8
+                         if x .&. 0x80 == 0x80 then return (True, x .&. 0x7f) else return (False, x)
+
+putSigSubPacketType :: Bool -> Word8 -> Put
+putSigSubPacketType False sst = putWord8 sst
+putSigSubPacketType True sst = putWord8 (sst .|. 0x80)
+
+bsToFFSet :: FutureFlag a => ByteString -> Set a
+bsToFFSet bs = Set.fromAscList .  concat . snd $ mapAccumL (\acc y -> (acc+8, concatMap (\x -> if y .&. (shiftR 128 x) == (shiftR 128 x) then [toFFlag (acc + x)] else []) [0..7])) 0 (B.unpack bs)
+
+ffSetToFixedLengthBS :: (Integral a, FutureFlag b) => a -> Set b -> ByteString
+ffSetToFixedLengthBS len ffs = B.take (fromIntegral len) (B.append (ffSetToBS ffs) (B.pack (replicate 5 0)))
+
+ffSetToBS :: FutureFlag a => Set a -> ByteString
+ffSetToBS = B.pack . ffSetToBS'
+    where
+        ffSetToBS' :: FutureFlag a => Set a -> [Word8]
+        ffSetToBS' ks = map (foldl (.|.) 0 . map (shiftR 128 . flip mod 8 . fromFFlag) . Set.toAscList) (map (\x -> Set.filter (\y -> fromFFlag y `div` 8 == x) ks) [0..(fromFFlag $ Set.findMax ks) `div` 8])
+
+fromS2K :: S2K -> ByteString
+fromS2K (Simple hashalgo) = B.pack [0, fromIntegral . fromFVal $ hashalgo]
+fromS2K (Salted hashalgo salt)
+    | B.length salt == 8 = B.pack [1, fromIntegral . fromFVal $ hashalgo] `B.append` salt
+fromS2K (IteratedSalted hashalgo salt count)
+    | B.length salt == 8 = B.pack [3, fromIntegral . fromFVal $ hashalgo] `B.append` salt `B.snoc` (encodeIterationCount count)
+fromS2K (OtherS2K t bs) = bs
+
+
+getPacketLength :: Integral a => Get a
+getPacketLength = do
+    firstOctet <- getWord8
+    getPacketLength' firstOctet
+    where
+        getPacketLength' :: Integral a => Word8 -> Get a
+        getPacketLength' f
+            | f < 192 = return . fromIntegral $ f
+            | f < 224 = do
+                           secondOctet <- getWord8
+                           return . fromIntegral $ shiftL (fromIntegral (f - 192) :: Int) 8 + (fromIntegral secondOctet :: Int) + 192
+            | f == 255 = do
+                           len <- getWord32be
+                           return . fromIntegral $ len
+            | otherwise = fail "Partial body length support missing." --FIXME
+
+putPacketLength :: Integral a => a -> Put
+putPacketLength l
+    | l < 192 = putWord8 (fromIntegral l)
+    | l < 8384 = putWord8 (fromIntegral ((fromIntegral (l - 192) `shiftR` 8) + 192 :: Int)) >> putWord8 (fromIntegral (l - 192) .&. 0xff)
+    | l < 0x100000000 = putWord8 255 >> putWord32be (fromIntegral l)
+    | otherwise = fail "partial body length support needed" -- FIXME
+
+getS2K :: Get S2K
+getS2K = getS2K' =<< getWord8
+    where
+        getS2K' :: Word8 -> Get S2K
+        getS2K' t
+            | t == 0 = do
+                          ha <- getWord8
+                          return $ Simple (toFVal ha)
+            | t == 1 = do
+                          ha <- getWord8
+                          salt <- getByteString 8
+                          return $ Salted (toFVal ha) salt
+            | t == 3 = do
+                          ha <- getWord8
+                          salt <- getByteString 8
+                          count <- getWord8
+                          return $ IteratedSalted (toFVal ha) salt (decodeIterationCount count)
+            | otherwise = error "Unknown S2K"
+
+putS2K :: S2K -> Put
+putS2K (IteratedSalted ha salt count) = do
+    putWord8 3
+    put ha
+    putByteString salt
+    putWord8 $ encodeIterationCount count
+
+getPacketTypeAndLength :: Get (Word8, Int)
+getPacketTypeAndLength = do
+    tag <- getWord8 -- FIXME: bit 7 must be 1, check?
+    case tag .&. 0x40 of
+        0x00 -> do
+                   let t = shiftR (tag .&. 0x3c) 2
+                   case tag .&. 0x03 of
+                       0 -> do len <- getWord8
+                               return (t, fromIntegral len)
+                       1 -> do len <- getWord16be
+                               return (t, fromIntegral len)
+                       2 -> do len <- getWord32be
+                               return (t, fromIntegral len)
+                       3 -> return (t, maxBound)  -- FIXME: express this better
+        0x40 -> do
+                   len <- getPacketLength
+                   return (tag .&. 0x3f, len)
+
+getPacket :: Get Packet
+getPacket = do
+               (t, len) <- getPacketTypeAndLength
+               getPacket' t len
+    where
+        getPacket' :: Word8 -> Int -> Get Packet
+        getPacket' t len
+            | t == 1 = do
+                          pv <- getWord8
+                          eokeyid <- getByteString 8
+                          pkalgo <- getWord8
+                          sk <- getByteString (len - 10)
+                          return $ PKESK pv eokeyid (toFVal pkalgo) sk
+            | t == 2 = do
+                          pv <- getWord8
+                          case pv of
+                              3 -> do
+                                      hashlen <- getWord8 -- must be 5
+                                      st <- getWord8
+                                      ctime <- getWord32be
+                                      eok <- getByteString 8
+                                      pka <- get
+                                      ha <- get
+                                      left16 <- getWord16be
+                                      mpib <- getBytes (len - 19)
+                                      case runGet (many getMPI) mpib of
+                                          Left e -> error e
+                                          Right mpis -> return $ Signature $ SigV3 (toFVal st) ctime eok (toFVal pka) (toFVal ha) left16 mpis
+                              4 -> do
+                                      st <- getWord8
+                                      pka <- get
+                                      ha <- get
+                                      hlen <- getWord16be
+                                      hb <- getBytes (fromIntegral hlen)
+                                      let hashed = case runGet (many getSigSubPacket) hb of
+                                                        Left err -> error err
+                                                        Right h -> h
+                                      ulen <- getWord16be
+                                      ub <- getBytes (fromIntegral ulen)
+                                      let unhashed = case runGet (many getSigSubPacket) ub of
+                                                        Left err -> error err
+                                                        Right u -> u
+                                      left16 <- getWord16be
+                                      mpib <- getBytes (len - (10 + (fromIntegral hlen) + (fromIntegral ulen)))
+                                      case runGet (many getMPI) mpib of
+                                          Left e -> error e
+                                          Right mpis -> return $ Signature $ SigV4 (toFVal st) (toFVal pka) (toFVal ha) hashed unhashed left16 mpis
+                              otherwise -> do
+                                               bs <- getByteString (len - 1)
+                                               return $ Signature $ SigVOther pv bs
+            | t == 3 = do
+                          pv <- getWord8
+                          symalgo <- getWord8
+                          s2k <- getS2K
+                          let partiallen = case s2k of
+                                               Simple _ -> 1 + 3
+                                               Salted _ _ -> 9 + 3
+                                               IteratedSalted _ _ _ -> 10 + 3
+                          msk <- getMSK len partiallen
+                          return $ SKESK pv (toFVal symalgo) s2k msk
+            | t == 4 = do
+                          pv <- getWord8
+                          sigtype <- getWord8
+                          ha <- getWord8
+                          pka <- getWord8
+                          skeyid <- getByteString 8
+                          nested <- getWord8
+                          return $ OnePassSignature pv (toFVal sigtype) (toFVal ha) (toFVal pka) skeyid (nested == 0)
+            | t == 5 = do
+                          bs <- getBytes len
+                          let ps = flip runGet bs $ do pkp <- getPKPayload
+                                                       ska <- getSKAddendum (pubKeyAlgo pkp)
+                                                       return $ SecretKey pkp ska
+                          case ps of
+                              Left err -> error err
+                              Right key -> return key
+            | t == 6 = do
+                          pkp <- getPKPayload
+                          return $ PublicKey pkp
+            | t == 7 = do
+                          bs <- getBytes len
+                          let ps = flip runGet bs $ do pkp <- getPKPayload
+                                                       ska <- getSKAddendum (pubKeyAlgo pkp)
+                                                       return $ SecretSubkey pkp ska
+                          case ps of
+                              Left err -> error err
+                              Right key -> return key
+            | t == 8 = do
+                          ca <- getWord8
+                          cdata <- getByteString (len - 1)
+                          return $ CompressedData (toFVal ca) cdata
+            | t == 9 = do
+                          sdata <- getByteString len
+                          return $ SymEncData sdata
+            | t == 10 = do
+                          marker <- getByteString len
+                          return $ Marker marker
+            | t == 11 = do
+                          dt <- getWord8
+                          flen <- getWord8
+                          fn <- getByteString (fromIntegral flen)
+                          ts <- getWord32be
+                          ldata <- getByteString (len - (6 + (fromIntegral flen)))
+                          return $ LiteralData (toFVal dt) fn ts ldata
+            | t == 12 = do
+                          tdata <- getByteString len
+                          return $ Trust tdata
+            | t == 13 = do
+                          udata <- getBytes len
+                          return $ UserId (BC8.unpack udata)
+            | t == 14 = do
+                          pkp <- getPKPayload
+                          return $ PublicSubkey pkp
+            | t == 17 = do
+                        bs <- getBytes len
+                        case runGet (many getUserAttrSubPacket) bs of
+                            Left err -> error err
+                            Right uas -> return $ UserAttribute uas
+            | t == 18 = do
+                          pv <- getWord8 -- should be 1
+                          b <- getByteString (len - 1)
+                          return $ SymEncIntegrityProtectedData pv b
+            | t == 19 = do
+                          hash <- getByteString 20
+                          return $ ModificationDetectionCode hash
+            | otherwise = do
+                          payload <- getByteString len
+                          return $ OtherPacket t payload
+            where
+                          getMSK :: Int -> Int -> Get (Maybe SessionKey)
+                          getMSK len partiallen
+                              | len == partiallen = return Nothing
+                              | len > partiallen = do
+                                                      sk <- getByteString (len - partiallen)
+                                                      return $ Just sk
+                              | otherwise = error "Overread"
+
+getUserAttrSubPacket :: Get UserAttrSubPacket
+getUserAttrSubPacket = do
+    l <- getSubPacketLength
+    t <- getWord8
+    getUserAttrSubPacket' t l
+        where
+            getUserAttrSubPacket' :: Word8 -> Int -> Get UserAttrSubPacket
+            getUserAttrSubPacket' t l
+                | t == 1 = do
+                              ihlen <- getWord16le
+                              hver <- getWord8 -- should be 1
+                              iformat <- getWord8
+                              _ <- getBytes 12 -- should be NULs
+                              bs <- getByteString (l - 17)
+                              return $ ImageAttribute (ImageHV1 (toFVal iformat)) bs
+                | otherwise = do
+                                 bs <- getByteString (l - 1)
+                                 return $ OtherUASub t bs
+
+putUserAttrSubPacket :: UserAttrSubPacket -> Put
+putUserAttrSubPacket ua = do
+    let sp = runPut $ putUserAttrSubPacket' ua
+    putSubPacketLength . B.length $ sp
+    putByteString sp
+    where
+        putUserAttrSubPacket' (ImageAttribute (ImageHV1 iformat) idata) = do
+            putWord8 1
+            putWord16le 16
+            putWord8 1
+            putWord8 (fromFVal iformat)
+            mapM_ putWord8 $ replicate 12 0
+            putByteString idata
+        putUserAttrSubPacket' (OtherUASub t bs) = do
+            putWord8 t
+            putByteString bs
+
+putPacket :: Packet -> Put
+putPacket (PKESK pv eokeyid pkalgo sk) = do
+    putWord8 (0xc0 .|. 1)
+    let bsk = sk
+    putPacketLength $ 10 + (B.length bsk)
+    putWord8 pv -- must be 3
+    putByteString eokeyid -- must be 8 octets
+    putWord8 $ fromIntegral . fromFVal $ pkalgo
+    putByteString bsk
+putPacket (Signature (SigV3 st ctime eok pka ha left16 mpis)) = do
+    putWord8 (0xc0 .|. 2)
+    let bs = runPut $ do
+                        putWord8 3
+                        put st
+                        putWord32be ctime
+                        putByteString eok
+                        put pka
+                        put ha
+                        putWord16be left16
+                        mapM_ put mpis
+    putPacketLength . B.length $ bs
+    putByteString bs
+putPacket (Signature (SigV4 st pka ha hashed unhashed left16 mpis)) = do
+    putWord8 (0xc0 .|. 2)
+    let bs = runPut $ do
+                        putWord8 4
+                        put st
+                        put pka
+                        put ha
+                        let hb = runPut $ mapM_ put hashed
+                        putWord16be . fromIntegral . B.length $ hb
+                        putByteString hb
+                        let ub = runPut $ mapM_ put unhashed
+                        putWord16be . fromIntegral . B.length $ ub
+                        putByteString ub
+                        putWord16be left16
+                        mapM_ put mpis
+    putPacketLength . B.length $ bs
+    putByteString bs
+putPacket (SKESK pv symalgo s2k msk) = do
+    putWord8 (0xc0 .|. 3)
+    let bs2k = fromS2K s2k
+    putPacketLength $ 2 + (B.length bs2k) + (maybe 0 B.length msk)
+    putWord8 pv -- should be 4
+    putWord8 $ fromIntegral . fromFVal $ symalgo
+    putByteString bs2k
+    when (isJust msk) $ putByteString (fromJust msk)
+putPacket (OnePassSignature pv sigtype ha pka skeyid nested) = do
+    putWord8 (0xc0 .|. 4)
+    let bs = runPut $ do
+                putWord8 pv -- should be 3
+                putWord8 $ fromIntegral . fromFVal $ sigtype
+                putWord8 $ fromIntegral . fromFVal $ ha
+                putWord8 $ fromIntegral . fromFVal $ pka
+                putByteString skeyid
+                putWord8 . fromIntegral . fromEnum $ nested -- FIXME: what do other values mean?
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (SecretKey pkp ska) = do
+    putWord8 (0xc0 .|. 5)
+    let bs = runPut (putPKPayload pkp >> putSKAddendum ska)
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (PublicKey pkp) = do
+    putWord8 (0xc0 .|. 6)
+    let bs = runPut $ putPKPayload pkp
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (SecretSubkey pkp ska) = do
+    putWord8 (0xc0 .|. 7)
+    let bs = runPut (putPKPayload pkp >> putSKAddendum ska)
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (CompressedData ca cdata) = do
+    putWord8 (0xc0 .|. 8)
+    let bs = runPut $ do
+                         putWord8 $ fromIntegral . fromFVal $ ca
+                         putByteString cdata
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (SymEncData b) = do
+    putWord8 (0xc0 .|. 9)
+    putPacketLength $ B.length b
+    putByteString b
+putPacket (Marker b) = do
+    putWord8 (0xc0 .|. 10)
+    putPacketLength $ B.length b
+    putByteString b
+putPacket (LiteralData dt fn ts b) = do
+    putWord8 (0xc0 .|. 11)
+    let bs = runPut $ do
+                        putWord8 $ fromIntegral . fromFVal $ dt
+                        putWord8 $ fromIntegral . B.length $ fn
+                        putByteString fn
+                        putWord32be ts
+                        putByteString b
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (Trust b) = do
+    putWord8 (0xc0 .|. 12)
+    putPacketLength . B.length $ b
+    putByteString b
+putPacket (UserId u) = do
+    putWord8 (0xc0 .|. 13)
+    let bs = BC8.pack u
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (PublicSubkey pkp) = do
+    putWord8 (0xc0 .|. 14)
+    let bs = runPut $ putPKPayload pkp
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (UserAttribute us) = do
+    putWord8 (0xc0 .|. 17)
+    let bs = runPut $ mapM_ put us
+    putPacketLength $ B.length bs
+    putByteString bs
+putPacket (SymEncIntegrityProtectedData pv b) = do
+    putWord8 (0xc0 .|. 18)
+    putPacketLength $ (B.length b) + 1
+    putWord8 pv -- should be 1
+    putByteString b
+putPacket (ModificationDetectionCode hash) = do
+    putWord8 (0xc0 .|. 19)
+    putPacketLength . B.length $ hash
+    putByteString hash
+putPacket (OtherPacket t payload) = do
+    putWord8 (0xc0 .|. t) -- FIXME: restrict t
+    putPacketLength . B.length $ payload
+    putByteString payload
+
+
+getMPI :: Get MPI
+getMPI = do mpilen <- getWord16be
+            bs <- getByteString ((fromIntegral (mpilen - 1) `div` 8) + 1)
+            return $ MPI bs
+
+getMPIs :: PubKeyAlgorithm -> Get [MPI]
+getMPIs RSA = replicateM 2 get
+getMPIs RSAEncryptOnly = replicateM 2 get
+getMPIs RSASignOnly = replicateM 2 get
+getMPIs DSA = replicateM 4 get
+getMPIs ElgamalEncryptOnly = replicateM 3 get
+getMPIs Elgamal = replicateM 3 get
+
+getSecretMPIs :: PubKeyAlgorithm -> Get [MPI]
+getSecretMPIs RSA = replicateM 4 get
+getSecretMPIs RSAEncryptOnly = replicateM 4 get
+getSecretMPIs RSASignOnly = replicateM 1 get
+getSecretMPIs DSA = replicateM 1 get
+getSecretMPIs ElgamalEncryptOnly = replicateM 1 get
+getSecretMPIs Elgamal = replicateM 1 get
+
+indefiniteMPIs :: ByteString -> [MPI]
+indefiniteMPIs bs = do
+    case runGet (many getMPI) bs of
+        Left e -> error e
+        Right mpis -> mpis
+
+putMPI :: MPI -> Put
+putMPI (MPI bs) = do putWord16be . (*8) . fromIntegral . B.length $ bs -- FIXME: odd bits
+                     putByteString bs
+
+getPackets :: Get [Packet]
+getPackets = many getPacket
+
+putPackets :: [Packet] -> Put
+putPackets = mapM_ putPacket
+
+getPKPayload :: Get PKPayload
+getPKPayload = do
+    version <- getWord8
+    ctime <- getWord32be
+    case version `elem` [2,3] of
+                               True -> do v3exp <-  getWord16be
+                                          pka <- get
+                                          mpis <- getMPIs pka
+                                          return $ PubV3 ctime v3exp pka mpis
+                               False -> do
+                                          pka <- get
+                                          mpis <- getMPIs pka
+                                          return $ PubV4 ctime pka mpis
+
+putPKPayload :: PKPayload -> Put
+putPKPayload (PubV3 ctime v3exp pka mpis) = do
+    putWord8 3
+    putWord32be ctime
+    putWord16be v3exp
+    put pka
+    mapM_ put mpis
+putPKPayload (PubV4 ctime pka mpis) = do
+    putWord8 4
+    putWord32be ctime
+    put pka
+    mapM_ put mpis
+
+pubKeyAlgo :: PKPayload -> PubKeyAlgorithm
+pubKeyAlgo (PubV3 _ _ pka _) = pka
+pubKeyAlgo (PubV4 _ pka _) = pka
+
+getSKAddendum :: PubKeyAlgorithm -> Get SKAddendum
+getSKAddendum pka = do
+    s2kusage <- getWord8
+    case s2kusage of
+        0 -> do mpis <- getSecretMPIs pka
+                checksum <- getWord16be
+                return $ SUUnencrypted mpis checksum
+        255 -> do symenc <- getWord8
+                  s2k <- getS2K
+                  iv <- getByteString (symEncBlockSize . toFVal $ symenc)
+                  remainder <- remaining
+                  encryptedblock <- getByteString remainder
+                  return $ SUS16bit (toFVal symenc) s2k iv encryptedblock
+        254 -> do symenc <- getWord8
+                  s2k <- getS2K
+                  iv <- getByteString (symEncBlockSize . toFVal $ symenc)
+                  remainder <- remaining
+                  encryptedblock <- getByteString remainder
+                  return $ SUSSHA1 (toFVal symenc) s2k iv encryptedblock
+        symenc -> do iv <- getByteString (symEncBlockSize . toFVal $ symenc)
+                     remainder <- remaining
+                     encryptedblock <- getByteString remainder
+                     return $ SUSym (toFVal symenc) iv encryptedblock
+
+putSKAddendum :: SKAddendum -> Put
+putSKAddendum (SUSSHA1 symenc s2k iv encryptedblock) = do
+    putWord8 254
+    put symenc
+    put s2k
+    putByteString iv
+    putByteString encryptedblock
+
+symEncBlockSize :: SymmetricAlgorithm -> Int
+symEncBlockSize (Plaintext) = 0
+symEncBlockSize (IDEA) = 8
+symEncBlockSize (TripleDES) = 8
+symEncBlockSize (CAST5) = 8
+symEncBlockSize (Blowfish) = 8
+symEncBlockSize (AES128) = 16
+symEncBlockSize (AES192) = 16
+symEncBlockSize (AES256) = 16
+symEncBlockSize (Twofish) = 16
+symEncBlockSize x = 8 -- FIXME
+
+decodeIterationCount :: Word8 -> Int
+decodeIterationCount c = fromIntegral $ (16 + (c .&. 15)) `shiftL` ((fromIntegral c `shiftR` 4) + 6)
+
+encodeIterationCount :: Int -> Word8
+encodeIterationCount c = fromIntegral c -- FIXME
+
+-- Stolen from Axman6
+many :: Get a -> Get [a]
+many p = many1 p `mplus` return []
+
+many1 :: Get a -> Get [a]
+many1 p = (:) <$> p <*> many p
diff --git a/Codec/Encryption/OpenPGP/Types.hs b/Codec/Encryption/OpenPGP/Types.hs
new file mode 100644
--- /dev/null
+++ b/Codec/Encryption/OpenPGP/Types.hs
@@ -0,0 +1,520 @@
+-- Types.hs: OpenPGP (RFC4880) data types
+-- Copyright Ⓒ 2012  Clint Adams
+-- This software is released under the terms of the Expat (MIT) license.
+-- (See the LICENSE file).
+
+module Codec.Encryption.OpenPGP.Types (
+   SigSubPacket(..)
+ , CompressionAlgorithm(..)
+ , HashAlgorithm(..)
+ , PubKeyAlgorithm(..)
+ , SymmetricAlgorithm(..)
+ , MPI(MPI)
+ , Packet(..)
+ , S2K(..)
+ , SignaturePayload(..)
+ , UserAttrSubPacket(..)
+ , SigType(..)
+ , ImageHeader(..)
+ , ImageFormat(..)
+ , PKPayload(..)
+ , SKAddendum(..)
+ , EightOctetKeyId
+ , SessionKey
+ , FutureFlag
+ , FutureVal
+ , fromFVal
+ , fromFFlag
+ , toFVal
+ , toFFlag
+) where
+
+import Data.ByteString (ByteString)
+import Data.Set (Set)
+import Data.Word (Word8, Word16, Word32)
+
+type TimeStamp = Word32
+type Exportability = Bool
+type TrustLevel = Word8
+type TrustAmount = Word8
+type AlmostPublicDomainRegex = ByteString
+type Revocability = Bool
+type RevocationReason = ByteString
+type TwentyOctetFingerprint = ByteString
+type EightOctetKeyId = ByteString
+type KeyServer = ByteString
+type URL = ByteString
+type NotationName = ByteString
+type NotationValue = ByteString
+type UserId = ByteString
+type SignaturePacketBody = ByteString
+type SignatureHash = ByteString
+type PacketVersion = Word8
+type SessionKey = ByteString
+type Salt = ByteString
+type Count = Int
+type V3Expiration = Word16
+type CompressedDataPayload = ByteString
+type FileName = ByteString
+type ImageData = ByteString
+type NestedFlag = Bool
+type IV = ByteString
+
+data SymmetricAlgorithm = Plaintext
+                        | IDEA
+                        | TripleDES
+                        | CAST5
+                        | Blowfish
+                        | ReservedSAFER
+                        | ReservedDES
+                        | AES128
+                        | AES192
+                        | AES256
+                        | Twofish
+                        | OtherSA Word8
+     deriving (Show)
+
+instance Eq SymmetricAlgorithm where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord SymmetricAlgorithm where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal SymmetricAlgorithm where
+    fromFVal Plaintext = 0
+    fromFVal IDEA = 1
+    fromFVal TripleDES = 2
+    fromFVal CAST5 = 3
+    fromFVal Blowfish = 4
+    fromFVal AES128 = 7
+    fromFVal AES192 = 8
+    fromFVal AES256 = 9
+    fromFVal Twofish = 10
+    fromFVal (OtherSA o) = o
+    toFVal 0 = Plaintext
+    toFVal 1 = IDEA
+    toFVal 2 = TripleDES
+    toFVal 3 = CAST5
+    toFVal 4 = Blowfish
+    toFVal 7 = AES128
+    toFVal 8 = AES192
+    toFVal 9 = AES256
+    toFVal 10 = Twofish
+    toFVal o = OtherSA o
+
+data NotationFlag = HumanReadable
+                  | OtherNF Int
+     deriving (Show)
+
+instance Eq NotationFlag where
+    (==) a b = fromFFlag a == fromFFlag b
+
+instance Ord NotationFlag where
+    compare a b = fromFFlag a `compare` fromFFlag b
+
+instance FutureFlag NotationFlag where
+    fromFFlag HumanReadable = 0
+    fromFFlag (OtherNF o) = fromIntegral o
+
+    toFFlag 0 = HumanReadable
+    toFFlag o = OtherNF (fromIntegral o)
+
+data SigSubPacket = SigCreationTime Bool TimeStamp
+                  | SigExpirationTime Bool TimeStamp
+                  | ExportableCertification Bool Exportability
+                  | TrustSignature Bool TrustLevel TrustAmount
+                  | RegularExpression Bool AlmostPublicDomainRegex
+                  | Revocable Bool Revocability
+                  | KeyExpirationTime Bool TimeStamp
+                  | PreferredSymmetricAlgorithms Bool [SymmetricAlgorithm]
+                  | RevocationKey Bool (Set RevocationClass) PubKeyAlgorithm TwentyOctetFingerprint
+                  | Issuer Bool EightOctetKeyId
+                  | NotationData Bool (Set NotationFlag) NotationName NotationValue
+                  | PreferredHashAlgorithms Bool [HashAlgorithm]
+                  | PreferredCompressionAlgorithms Bool [CompressionAlgorithm]
+                  | KeyServerPreferences Bool (Set KSPFlag)
+                  | PreferredKeyServer Bool KeyServer
+                  | PrimaryUserId Bool Bool
+                  | PolicyURL Bool URL
+                  | KeyFlags Bool (Set KeyFlag)
+                  | SignersUserId Bool UserId
+                  | ReasonForRevocation Bool RevocationCode RevocationReason
+                  | Features Bool (Set FeatureFlag)
+                  | SignatureTarget Bool PubKeyAlgorithm HashAlgorithm SignatureHash
+                  | EmbeddedSignature Bool SignaturePacketBody
+                  | UserDefinedSigSub Bool Word8 ByteString
+                  | OtherSigSub Bool Word8 ByteString
+    deriving (Show, Eq) -- FIXME
+
+data HashAlgorithm = DeprecatedMD5
+                   | SHA1
+                   | RIPEMD160
+                   | SHA256
+                   | SHA384
+                   | SHA512
+                   | SHA224
+                   | OtherHA Word8
+    deriving (Show)
+
+instance Eq HashAlgorithm where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord HashAlgorithm where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal HashAlgorithm where
+    fromFVal DeprecatedMD5 = 1
+    fromFVal SHA1 = 2
+    fromFVal RIPEMD160 = 3
+    fromFVal SHA256 = 8
+    fromFVal SHA384 = 9
+    fromFVal SHA512 = 10
+    fromFVal SHA224 = 11
+    fromFVal (OtherHA o) = o
+    toFVal 1 = DeprecatedMD5
+    toFVal 2 = SHA1
+    toFVal 3 = RIPEMD160
+    toFVal 8 = SHA256
+    toFVal 9 = SHA384
+    toFVal 10 = SHA512
+    toFVal 11 = SHA224
+    toFVal o = OtherHA o
+
+data CompressionAlgorithm = Uncompressed
+                          | ZIP
+                          | ZLIB
+                          | BZip2
+                          | OtherCA Word8
+    deriving (Show)
+
+instance Eq CompressionAlgorithm where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord CompressionAlgorithm where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal CompressionAlgorithm where
+    fromFVal Uncompressed = 0
+    fromFVal ZIP = 1
+    fromFVal ZLIB = 2
+    fromFVal BZip2 = 3
+    fromFVal (OtherCA o) = o
+    toFVal 0 = Uncompressed
+    toFVal 1 = ZIP
+    toFVal 2 = ZLIB
+    toFVal 3 = BZip2
+    toFVal o = OtherCA o
+
+class (Eq a, Ord a) => FutureVal a where
+   fromFVal :: a -> Word8
+   toFVal :: Word8 -> a
+
+data PubKeyAlgorithm = RSA
+                     | RSAEncryptOnly
+                     | RSASignOnly
+                     | ElgamalEncryptOnly
+                     | DSA
+                     | EC
+                     | ECDSA
+                     | Elgamal
+                     | DH
+                     | OtherPKA Word8
+    deriving (Show)
+
+instance Eq PubKeyAlgorithm where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord PubKeyAlgorithm where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal PubKeyAlgorithm where
+    fromFVal RSA = 1
+    fromFVal RSAEncryptOnly = 2
+    fromFVal RSASignOnly = 3
+    fromFVal ElgamalEncryptOnly = 16
+    fromFVal DSA = 17
+    fromFVal EC = 18
+    fromFVal ECDSA = 19
+    fromFVal Elgamal = 20
+    fromFVal DH = 21
+    fromFVal (OtherPKA o) = o
+    toFVal 1 = RSA
+    toFVal 2 = RSAEncryptOnly
+    toFVal 3 = RSASignOnly
+    toFVal 16 = ElgamalEncryptOnly
+    toFVal 17 = DSA
+    toFVal 18 = EC
+    toFVal 19 = ECDSA
+    toFVal 20 = Elgamal
+    toFVal 21 = DH
+    toFVal o = OtherPKA o
+
+class (Eq a, Ord a) => FutureFlag a where
+    fromFFlag :: Integral b => a -> b
+    toFFlag :: Integral b => b-> a
+
+data KSPFlag = NoModify
+             | KSPOther Int
+    deriving (Show)
+
+instance Eq KSPFlag where
+    (==) a b = fromFFlag a == fromFFlag b
+
+instance Ord KSPFlag where
+    compare a b = fromFFlag a `compare` fromFFlag b
+
+instance FutureFlag KSPFlag where
+    fromFFlag NoModify = 0
+    fromFFlag (KSPOther i) = fromIntegral i
+
+    toFFlag 0 = NoModify
+    toFFlag i = KSPOther (fromIntegral i)
+
+data KeyFlag = GroupKey
+             | AuthKey
+             | SplitKey
+             | EncryptStorageKey
+             | EncryptCommunicationsKey
+             | SignDataKey
+             | CertifyKeysKey
+             | KFOther Int
+    deriving (Show)
+
+instance Eq KeyFlag where
+    (==) a b = fromFFlag a == fromFFlag b
+
+instance Ord KeyFlag where
+    compare a b = fromFFlag a `compare` fromFFlag b
+
+instance FutureFlag KeyFlag where
+    fromFFlag GroupKey = 0
+    fromFFlag AuthKey = 2
+    fromFFlag SplitKey = 3
+    fromFFlag EncryptStorageKey = 4
+    fromFFlag EncryptCommunicationsKey = 5
+    fromFFlag SignDataKey = 6
+    fromFFlag CertifyKeysKey = 7
+    fromFFlag (KFOther i) = fromIntegral i
+
+    toFFlag 0 = GroupKey
+    toFFlag 2 = AuthKey
+    toFFlag 3 = SplitKey
+    toFFlag 4 = EncryptStorageKey
+    toFFlag 5 = EncryptCommunicationsKey
+    toFFlag 6 = SignDataKey
+    toFFlag 7 = CertifyKeysKey
+    toFFlag i = KFOther (fromIntegral i)
+
+data RevocationClass = SensitiveRK
+                     | RClOther Int
+    deriving (Show)
+
+instance Eq RevocationClass where
+    (==) a b = fromFFlag a == fromFFlag b
+
+instance Ord RevocationClass where
+    compare a b = fromFFlag a `compare` fromFFlag b
+
+instance FutureFlag RevocationClass where
+    fromFFlag SensitiveRK = 1
+    fromFFlag (RClOther i) = fromIntegral i
+
+    toFFlag 1 = SensitiveRK
+    toFFlag i = RClOther (fromIntegral i)
+
+data RevocationCode = NoReason
+                    | KeySuperseded
+                    | KeyMaterialCompromised
+                    | KeyRetiredAndNoLongerUsed
+                    | UserIdInfoNoLongerValid
+                    | RCoOther Word8
+    deriving (Show)
+
+instance Eq RevocationCode where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord RevocationCode where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal RevocationCode where
+    fromFVal NoReason = 0
+    fromFVal KeySuperseded = 1
+    fromFVal KeyMaterialCompromised = 2
+    fromFVal KeyRetiredAndNoLongerUsed = 3
+    fromFVal UserIdInfoNoLongerValid = 32
+    fromFVal (RCoOther o) = o
+    toFVal 0 = NoReason
+    toFVal 1 = KeySuperseded
+    toFVal 2 = KeyMaterialCompromised
+    toFVal 3 = KeyRetiredAndNoLongerUsed
+    toFVal 32 = UserIdInfoNoLongerValid
+    toFVal o = RCoOther o
+
+data FeatureFlag = ModificationDetection
+                 | FeatureOther Int
+    deriving (Show)
+
+instance Eq FeatureFlag where
+    (==) a b = fromFFlag a == fromFFlag b
+
+instance Ord FeatureFlag where
+    compare a b = fromFFlag a `compare` fromFFlag b
+
+instance FutureFlag FeatureFlag where
+    fromFFlag ModificationDetection = 7
+    fromFFlag (FeatureOther i) = fromIntegral i
+
+    toFFlag 7 = ModificationDetection
+    toFFlag i = FeatureOther (fromIntegral i)
+
+data MPI = MPI ByteString
+    deriving (Show, Eq)
+
+data SignaturePayload = SigV3 SigType Word32 EightOctetKeyId PubKeyAlgorithm HashAlgorithm Word16 [MPI]
+                      | SigV4 SigType PubKeyAlgorithm HashAlgorithm [SigSubPacket] [SigSubPacket] Word16 [MPI]
+                      | SigVOther Word8 ByteString
+    deriving (Show, Eq) -- FIXME
+
+data PKPayload = PubV3 TimeStamp V3Expiration PubKeyAlgorithm [MPI]
+               | PubV4 TimeStamp PubKeyAlgorithm [MPI]
+    deriving (Show, Eq)
+
+data SKAddendum = SUS16bit SymmetricAlgorithm S2K IV ByteString
+                | SUSSHA1 SymmetricAlgorithm S2K IV ByteString
+                | SUSym SymmetricAlgorithm IV ByteString
+                | SUUnencrypted [MPI] Word16
+    deriving (Show, Eq)
+
+data DataType = BinaryData
+              | TextData
+              | UTF8Data
+              | OtherData Word8
+    deriving (Show)
+
+instance Eq DataType where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord DataType where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal DataType where
+    fromFVal BinaryData = fromIntegral . fromEnum $ 'b'
+    fromFVal TextData = fromIntegral . fromEnum $ 't'
+    fromFVal UTF8Data = fromIntegral . fromEnum $ 'u'
+    fromFVal (OtherData o) = o
+
+    toFVal 0x62 = BinaryData
+    toFVal 0x74 = TextData
+    toFVal 0x75 = UTF8Data
+    toFVal o = OtherData o
+
+data Packet = PKESK PacketVersion EightOctetKeyId PubKeyAlgorithm SessionKey
+            | Signature SignaturePayload
+            | SKESK PacketVersion SymmetricAlgorithm S2K (Maybe SessionKey)
+            | OnePassSignature PacketVersion SigType HashAlgorithm PubKeyAlgorithm EightOctetKeyId NestedFlag
+            | SecretKey PKPayload SKAddendum
+            | PublicKey PKPayload
+            | SecretSubkey PKPayload SKAddendum
+            | CompressedData CompressionAlgorithm CompressedDataPayload
+            | SymEncData ByteString
+            | Marker ByteString
+            | LiteralData DataType FileName TimeStamp ByteString
+            | Trust ByteString
+            | UserId String
+            | PublicSubkey PKPayload
+            | UserAttribute [UserAttrSubPacket]
+            | SymEncIntegrityProtectedData PacketVersion ByteString
+            | ModificationDetectionCode ByteString
+            | OtherPacket Word8 ByteString
+    deriving (Show, Eq) -- FIXME
+
+data S2K = Simple HashAlgorithm
+         | Salted HashAlgorithm Salt
+         | IteratedSalted HashAlgorithm Salt Count
+         | OtherS2K Word8 ByteString
+    deriving (Show, Eq) -- FIXME
+
+data UserAttrSubPacket = ImageAttribute ImageHeader ImageData
+                       | OtherUASub Word8 ByteString
+    deriving (Show, Eq) -- FIXME
+
+data ImageHeader = ImageHV1 ImageFormat
+    deriving (Show, Eq)
+
+data ImageFormat = JPEG
+                 | OtherImage Word8
+    deriving (Show)
+
+instance Eq ImageFormat where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord ImageFormat where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal ImageFormat where
+    fromFVal JPEG = 1
+    fromFVal (OtherImage o) = o
+
+    toFVal 1 = JPEG
+    toFVal o = OtherImage o
+
+data SigType = BinarySig
+             | CanonicalTextSig
+             | StandaloneSig
+             | GenericCert
+             | PersonaCert
+             | CasualCert
+             | PositiveCert
+             | SubkeyBindingSig
+             | PrimaryKeyBindingSig
+             | SignatureDirectlyOnAKey
+             | KeyRevocationSig
+             | SubkeyRevocationSig
+             | CertRevocationSig
+             | TimestampSig
+             | ThirdPartyConfirmationSig
+             | OtherSig Word8
+    deriving (Show)
+
+instance Eq SigType where
+    (==) a b = fromFVal a == fromFVal b
+
+instance Ord SigType where
+    compare a b = fromFVal a `compare` fromFVal b
+
+instance FutureVal SigType where
+    fromFVal BinarySig = 0x00
+    fromFVal CanonicalTextSig = 0x01
+    fromFVal StandaloneSig = 0x02
+    fromFVal GenericCert = 0x10
+    fromFVal PersonaCert = 0x11
+    fromFVal CasualCert = 0x12
+    fromFVal PositiveCert = 0x13
+    fromFVal SubkeyBindingSig = 0x18
+    fromFVal PrimaryKeyBindingSig = 0x19
+    fromFVal SignatureDirectlyOnAKey = 0x1F
+    fromFVal KeyRevocationSig = 0x20
+    fromFVal SubkeyRevocationSig = 0x28
+    fromFVal CertRevocationSig = 0x30
+    fromFVal TimestampSig = 0x40
+    fromFVal ThirdPartyConfirmationSig = 0x50
+    fromFVal (OtherSig o) = o
+
+    toFVal 0x00 = BinarySig
+    toFVal 0x01 = CanonicalTextSig
+    toFVal 0x02 = StandaloneSig
+    toFVal 0x10 = GenericCert
+    toFVal 0x11 = PersonaCert
+    toFVal 0x12 = CasualCert
+    toFVal 0x13 = PositiveCert
+    toFVal 0x18 = SubkeyBindingSig
+    toFVal 0x19 = PrimaryKeyBindingSig
+    toFVal 0x1F = SignatureDirectlyOnAKey
+    toFVal 0x20 = KeyRevocationSig
+    toFVal 0x28 = SubkeyRevocationSig
+    toFVal 0x30 = CertRevocationSig
+    toFVal 0x40 = TimestampSig
+    toFVal 0x50 = ThirdPartyConfirmationSig
+    toFVal o = OtherSig o
+
+
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+Copyright Ⓒ 2012 Clint Adams
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/hOpenPGP.cabal b/hOpenPGP.cabal
new file mode 100644
--- /dev/null
+++ b/hOpenPGP.cabal
@@ -0,0 +1,121 @@
+Name:                hOpenPGP
+Version:             0.0.0
+Synopsis:            native Haskell implementation of OpenPGP (RFC4880)
+Description:         native Haskell implementation of OpenPGP (RFC4880)
+Homepage:            http://floss.scru.org/hOpenPGP/
+License:             MIT
+License-file:        LICENSE
+Author:              Clint Adams
+Maintainer:          Clint Adams <clint@debian.org>
+Copyright:           2012, Clint Adams
+Category:            Codec
+Build-type:          Simple
+Extra-source-files: tests/suite.hs
+  , tests/data/000001-006.public_key
+  , tests/data/000002-013.user_id
+  , tests/data/000003-002.sig
+  , tests/data/000004-012.ring_trust
+  , tests/data/000005-002.sig
+  , tests/data/000006-012.ring_trust
+  , tests/data/000007-002.sig
+  , tests/data/000008-012.ring_trust
+  , tests/data/000009-002.sig
+  , tests/data/000010-012.ring_trust
+  , tests/data/000011-002.sig
+  , tests/data/000012-012.ring_trust
+  , tests/data/000013-014.public_subkey
+  , tests/data/000014-002.sig
+  , tests/data/000015-012.ring_trust
+  , tests/data/000016-006.public_key
+  , tests/data/000017-002.sig
+  , tests/data/000018-012.ring_trust
+  , tests/data/000019-013.user_id
+  , tests/data/000020-002.sig
+  , tests/data/000021-012.ring_trust
+  , tests/data/000022-002.sig
+  , tests/data/000023-012.ring_trust
+  , tests/data/000024-014.public_subkey
+  , tests/data/000025-002.sig
+  , tests/data/000026-012.ring_trust
+  , tests/data/000027-006.public_key
+  , tests/data/000028-002.sig
+  , tests/data/000029-012.ring_trust
+  , tests/data/000030-013.user_id
+  , tests/data/000031-002.sig
+  , tests/data/000032-012.ring_trust
+  , tests/data/000033-002.sig
+  , tests/data/000034-012.ring_trust
+  , tests/data/000035-006.public_key
+  , tests/data/000036-013.user_id
+  , tests/data/000037-002.sig
+  , tests/data/000038-012.ring_trust
+  , tests/data/000039-002.sig
+  , tests/data/000040-012.ring_trust
+  , tests/data/000041-017.attribute
+  , tests/data/000042-002.sig
+  , tests/data/000043-012.ring_trust
+  , tests/data/000044-014.public_subkey
+  , tests/data/000045-002.sig
+  , tests/data/000046-012.ring_trust
+  , tests/data/000047-005.secret_key
+  , tests/data/000048-013.user_id
+  , tests/data/000049-002.sig
+  , tests/data/000050-012.ring_trust
+  , tests/data/000051-007.secret_subkey
+  , tests/data/000052-002.sig
+  , tests/data/000053-012.ring_trust
+  , tests/data/000054-005.secret_key
+  , tests/data/000055-002.sig
+  , tests/data/000056-012.ring_trust
+  , tests/data/000057-013.user_id
+  , tests/data/000058-002.sig
+  , tests/data/000059-012.ring_trust
+  , tests/data/000060-007.secret_subkey
+  , tests/data/000061-002.sig
+  , tests/data/000062-012.ring_trust
+  , tests/data/000063-005.secret_key
+  , tests/data/000064-002.sig
+  , tests/data/000065-012.ring_trust
+  , tests/data/000066-013.user_id
+  , tests/data/000067-002.sig
+  , tests/data/000068-012.ring_trust
+  , tests/data/000069-005.secret_key
+  , tests/data/000070-013.user_id
+  , tests/data/000071-002.sig
+  , tests/data/000072-012.ring_trust
+  , tests/data/000073-017.attribute
+  , tests/data/000074-002.sig
+  , tests/data/000075-012.ring_trust
+  , tests/data/000076-007.secret_subkey
+  , tests/data/000077-002.sig
+  , tests/data/000078-012.ring_trust
+  , tests/data/pubring.gpg
+  , tests/data/secring.gpg
+
+Cabal-version:       >= 1.10
+
+
+Library
+  Exposed-modules:     Codec.Encryption.OpenPGP.Types, Codec.Encryption.OpenPGP.Serialize
+  Build-depends: base             > 4       && < 5
+               , bytestring
+               , cereal
+               , containers
+  default-language: Haskell2010
+
+
+Test-Suite tests
+  type:       exitcode-stdio-1.0
+  main-is:    tests/suite.hs
+  Build-depends: base             > 4       && < 5
+               , bytestring
+               , cereal
+               , containers
+               , HUnit
+               , test-framework
+               , test-framework-hunit
+  default-language: Haskell2010
+
+source-repository head
+  type:     git
+  location: git://git.debian.org/users/clint/hOpenPGP.git
diff --git a/tests/data/000001-006.public_key b/tests/data/000001-006.public_key
new file mode 100644
Binary files /dev/null and b/tests/data/000001-006.public_key differ
diff --git a/tests/data/000002-013.user_id b/tests/data/000002-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000002-013.user_id
@@ -0,0 +1,1 @@
+´$Test Key (RSA) <testkey@example.org>
diff --git a/tests/data/000003-002.sig b/tests/data/000003-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000003-002.sig differ
diff --git a/tests/data/000004-012.ring_trust b/tests/data/000004-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000004-012.ring_trust differ
diff --git a/tests/data/000005-002.sig b/tests/data/000005-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000005-002.sig differ
diff --git a/tests/data/000006-012.ring_trust b/tests/data/000006-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000006-012.ring_trust differ
diff --git a/tests/data/000007-002.sig b/tests/data/000007-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000007-002.sig differ
diff --git a/tests/data/000008-012.ring_trust b/tests/data/000008-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000008-012.ring_trust differ
diff --git a/tests/data/000009-002.sig b/tests/data/000009-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000009-002.sig differ
diff --git a/tests/data/000010-012.ring_trust b/tests/data/000010-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000010-012.ring_trust differ
diff --git a/tests/data/000011-002.sig b/tests/data/000011-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000011-002.sig differ
diff --git a/tests/data/000012-012.ring_trust b/tests/data/000012-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000012-012.ring_trust differ
diff --git a/tests/data/000013-014.public_subkey b/tests/data/000013-014.public_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000013-014.public_subkey differ
diff --git a/tests/data/000014-002.sig b/tests/data/000014-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000014-002.sig differ
diff --git a/tests/data/000015-012.ring_trust b/tests/data/000015-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000015-012.ring_trust differ
diff --git a/tests/data/000016-006.public_key b/tests/data/000016-006.public_key
new file mode 100644
Binary files /dev/null and b/tests/data/000016-006.public_key differ
diff --git a/tests/data/000017-002.sig b/tests/data/000017-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000017-002.sig differ
diff --git a/tests/data/000018-012.ring_trust b/tests/data/000018-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000018-012.ring_trust differ
diff --git a/tests/data/000019-013.user_id b/tests/data/000019-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000019-013.user_id
@@ -0,0 +1,1 @@
+´$Test Key (DSA) <testkey@example.com>
diff --git a/tests/data/000020-002.sig b/tests/data/000020-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000020-002.sig differ
diff --git a/tests/data/000021-012.ring_trust b/tests/data/000021-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000021-012.ring_trust differ
diff --git a/tests/data/000022-002.sig b/tests/data/000022-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000022-002.sig differ
diff --git a/tests/data/000023-012.ring_trust b/tests/data/000023-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000023-012.ring_trust differ
diff --git a/tests/data/000024-014.public_subkey b/tests/data/000024-014.public_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000024-014.public_subkey differ
diff --git a/tests/data/000025-002.sig b/tests/data/000025-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000025-002.sig differ
diff --git a/tests/data/000026-012.ring_trust b/tests/data/000026-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000026-012.ring_trust differ
diff --git a/tests/data/000027-006.public_key b/tests/data/000027-006.public_key
new file mode 100644
Binary files /dev/null and b/tests/data/000027-006.public_key differ
diff --git a/tests/data/000028-002.sig b/tests/data/000028-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000028-002.sig differ
diff --git a/tests/data/000029-012.ring_trust b/tests/data/000029-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000029-012.ring_trust differ
diff --git a/tests/data/000030-013.user_id b/tests/data/000030-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000030-013.user_id
@@ -0,0 +1,1 @@
+´+Test Key (DSA sign-only) <test@example.net>
diff --git a/tests/data/000031-002.sig b/tests/data/000031-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000031-002.sig differ
diff --git a/tests/data/000032-012.ring_trust b/tests/data/000032-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000032-012.ring_trust differ
diff --git a/tests/data/000033-002.sig b/tests/data/000033-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000033-002.sig differ
diff --git a/tests/data/000034-012.ring_trust b/tests/data/000034-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000034-012.ring_trust differ
diff --git a/tests/data/000035-006.public_key b/tests/data/000035-006.public_key
new file mode 100644
Binary files /dev/null and b/tests/data/000035-006.public_key differ
diff --git a/tests/data/000036-013.user_id b/tests/data/000036-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000036-013.user_id
@@ -0,0 +1,1 @@
+´.Test Key (RSA sign-only) <testkey@example.net>
diff --git a/tests/data/000037-002.sig b/tests/data/000037-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000037-002.sig differ
diff --git a/tests/data/000038-012.ring_trust b/tests/data/000038-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000038-012.ring_trust differ
diff --git a/tests/data/000039-002.sig b/tests/data/000039-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000039-002.sig differ
diff --git a/tests/data/000040-012.ring_trust b/tests/data/000040-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000040-012.ring_trust differ
diff --git a/tests/data/000041-017.attribute b/tests/data/000041-017.attribute
new file mode 100644
Binary files /dev/null and b/tests/data/000041-017.attribute differ
diff --git a/tests/data/000042-002.sig b/tests/data/000042-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000042-002.sig differ
diff --git a/tests/data/000043-012.ring_trust b/tests/data/000043-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000043-012.ring_trust differ
diff --git a/tests/data/000044-014.public_subkey b/tests/data/000044-014.public_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000044-014.public_subkey differ
diff --git a/tests/data/000045-002.sig b/tests/data/000045-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000045-002.sig differ
diff --git a/tests/data/000046-012.ring_trust b/tests/data/000046-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000046-012.ring_trust differ
diff --git a/tests/data/000047-005.secret_key b/tests/data/000047-005.secret_key
new file mode 100644
Binary files /dev/null and b/tests/data/000047-005.secret_key differ
diff --git a/tests/data/000048-013.user_id b/tests/data/000048-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000048-013.user_id
@@ -0,0 +1,1 @@
+´$Test Key (RSA) <testkey@example.org>
diff --git a/tests/data/000049-002.sig b/tests/data/000049-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000049-002.sig differ
diff --git a/tests/data/000050-012.ring_trust b/tests/data/000050-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000050-012.ring_trust differ
diff --git a/tests/data/000051-007.secret_subkey b/tests/data/000051-007.secret_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000051-007.secret_subkey differ
diff --git a/tests/data/000052-002.sig b/tests/data/000052-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000052-002.sig differ
diff --git a/tests/data/000053-012.ring_trust b/tests/data/000053-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000053-012.ring_trust differ
diff --git a/tests/data/000054-005.secret_key b/tests/data/000054-005.secret_key
new file mode 100644
Binary files /dev/null and b/tests/data/000054-005.secret_key differ
diff --git a/tests/data/000055-002.sig b/tests/data/000055-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000055-002.sig differ
diff --git a/tests/data/000056-012.ring_trust b/tests/data/000056-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000056-012.ring_trust differ
diff --git a/tests/data/000057-013.user_id b/tests/data/000057-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000057-013.user_id
@@ -0,0 +1,1 @@
+´$Test Key (DSA) <testkey@example.com>
diff --git a/tests/data/000058-002.sig b/tests/data/000058-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000058-002.sig differ
diff --git a/tests/data/000059-012.ring_trust b/tests/data/000059-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000059-012.ring_trust differ
diff --git a/tests/data/000060-007.secret_subkey b/tests/data/000060-007.secret_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000060-007.secret_subkey differ
diff --git a/tests/data/000061-002.sig b/tests/data/000061-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000061-002.sig differ
diff --git a/tests/data/000062-012.ring_trust b/tests/data/000062-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000062-012.ring_trust differ
diff --git a/tests/data/000063-005.secret_key b/tests/data/000063-005.secret_key
new file mode 100644
Binary files /dev/null and b/tests/data/000063-005.secret_key differ
diff --git a/tests/data/000064-002.sig b/tests/data/000064-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000064-002.sig differ
diff --git a/tests/data/000065-012.ring_trust b/tests/data/000065-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000065-012.ring_trust differ
diff --git a/tests/data/000066-013.user_id b/tests/data/000066-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000066-013.user_id
@@ -0,0 +1,1 @@
+´+Test Key (DSA sign-only) <test@example.net>
diff --git a/tests/data/000067-002.sig b/tests/data/000067-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000067-002.sig differ
diff --git a/tests/data/000068-012.ring_trust b/tests/data/000068-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000068-012.ring_trust differ
diff --git a/tests/data/000069-005.secret_key b/tests/data/000069-005.secret_key
new file mode 100644
Binary files /dev/null and b/tests/data/000069-005.secret_key differ
diff --git a/tests/data/000070-013.user_id b/tests/data/000070-013.user_id
new file mode 100644
--- /dev/null
+++ b/tests/data/000070-013.user_id
@@ -0,0 +1,1 @@
+´.Test Key (RSA sign-only) <testkey@example.net>
diff --git a/tests/data/000071-002.sig b/tests/data/000071-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000071-002.sig differ
diff --git a/tests/data/000072-012.ring_trust b/tests/data/000072-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000072-012.ring_trust differ
diff --git a/tests/data/000073-017.attribute b/tests/data/000073-017.attribute
new file mode 100644
Binary files /dev/null and b/tests/data/000073-017.attribute differ
diff --git a/tests/data/000074-002.sig b/tests/data/000074-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000074-002.sig differ
diff --git a/tests/data/000075-012.ring_trust b/tests/data/000075-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000075-012.ring_trust differ
diff --git a/tests/data/000076-007.secret_subkey b/tests/data/000076-007.secret_subkey
new file mode 100644
Binary files /dev/null and b/tests/data/000076-007.secret_subkey differ
diff --git a/tests/data/000077-002.sig b/tests/data/000077-002.sig
new file mode 100644
Binary files /dev/null and b/tests/data/000077-002.sig differ
diff --git a/tests/data/000078-012.ring_trust b/tests/data/000078-012.ring_trust
new file mode 100644
Binary files /dev/null and b/tests/data/000078-012.ring_trust differ
diff --git a/tests/data/pubring.gpg b/tests/data/pubring.gpg
new file mode 100644
Binary files /dev/null and b/tests/data/pubring.gpg differ
diff --git a/tests/data/secring.gpg b/tests/data/secring.gpg
new file mode 100644
Binary files /dev/null and b/tests/data/secring.gpg differ
diff --git a/tests/suite.hs b/tests/suite.hs
new file mode 100644
--- /dev/null
+++ b/tests/suite.hs
@@ -0,0 +1,159 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+import Test.Framework (defaultMain, testGroup)
+import Test.Framework.Providers.HUnit
+
+import Test.HUnit
+
+import Codec.Encryption.OpenPGP.Serialize (getPackets, putPackets)
+import qualified Data.ByteString as B
+import Data.Serialize.Get (runGet)
+import Data.Serialize.Put (runPut)
+
+testSerialization :: FilePath -> Assertion
+testSerialization fp = do
+    bs <- B.readFile $ "tests/data/" ++ fp
+    let firstpass = runGet getPackets bs
+    case firstpass of
+        Left err -> assertFailure $ "First pass failed on " ++ fp
+        Right packs -> do let roundtrip = runPut $ putPackets packs
+                          let secondpass = runGet getPackets roundtrip
+                          assertEqual ("for " ++ fp) firstpass secondpass
+
+tests = [
+   testCase "000001-006.public_key" (testSerialization "000001-006.public_key")
+ , testCase "000002-013.user_id" (testSerialization "000002-013.user_id")
+ , testCase "000003-002.sig" (testSerialization "000003-002.sig")
+ , testCase "000004-012.ring_trust" (testSerialization "000004-012.ring_trust")
+ , testCase "000005-002.sig" (testSerialization "000005-002.sig")
+ , testCase "000006-012.ring_trust" (testSerialization "000006-012.ring_trust")
+ , testCase "000007-002.sig" (testSerialization "000007-002.sig")
+ , testCase "000008-012.ring_trust" (testSerialization "000008-012.ring_trust")
+ , testCase "000009-002.sig" (testSerialization "000009-002.sig")
+ , testCase "000010-012.ring_trust" (testSerialization "000010-012.ring_trust")
+ , testCase "000011-002.sig" (testSerialization "000011-002.sig")
+ , testCase "000012-012.ring_trust" (testSerialization "000012-012.ring_trust")
+ , testCase "000013-014.public_subkey" (testSerialization "000013-014.public_subkey")
+ , testCase "000014-002.sig" (testSerialization "000014-002.sig")
+ , testCase "000015-012.ring_trust" (testSerialization "000015-012.ring_trust")
+ , testCase "000016-006.public_key" (testSerialization "000016-006.public_key")
+ , testCase "000017-002.sig" (testSerialization "000017-002.sig")
+ , testCase "000018-012.ring_trust" (testSerialization "000018-012.ring_trust")
+ , testCase "000019-013.user_id" (testSerialization "000019-013.user_id")
+ , testCase "000020-002.sig" (testSerialization "000020-002.sig")
+ , testCase "000021-012.ring_trust" (testSerialization "000021-012.ring_trust")
+ , testCase "000022-002.sig" (testSerialization "000022-002.sig")
+ , testCase "000023-012.ring_trust" (testSerialization "000023-012.ring_trust")
+ , testCase "000024-014.public_subkey" (testSerialization "000024-014.public_subkey")
+ , testCase "000025-002.sig" (testSerialization "000025-002.sig")
+ , testCase "000026-012.ring_trust" (testSerialization "000026-012.ring_trust")
+ , testCase "000027-006.public_key" (testSerialization "000027-006.public_key")
+ , testCase "000028-002.sig" (testSerialization "000028-002.sig")
+ , testCase "000029-012.ring_trust" (testSerialization "000029-012.ring_trust")
+ , testCase "000030-013.user_id" (testSerialization "000030-013.user_id")
+ , testCase "000031-002.sig" (testSerialization "000031-002.sig")
+ , testCase "000032-012.ring_trust" (testSerialization "000032-012.ring_trust")
+ , testCase "000033-002.sig" (testSerialization "000033-002.sig")
+ , testCase "000034-012.ring_trust" (testSerialization "000034-012.ring_trust")
+ , testCase "000035-006.public_key" (testSerialization "000035-006.public_key")
+ , testCase "000036-013.user_id" (testSerialization "000036-013.user_id")
+ , testCase "000037-002.sig" (testSerialization "000037-002.sig")
+ , testCase "000038-012.ring_trust" (testSerialization "000038-012.ring_trust")
+ , testCase "000039-002.sig" (testSerialization "000039-002.sig")
+ , testCase "000040-012.ring_trust" (testSerialization "000040-012.ring_trust")
+ , testCase "000041-017.attribute" (testSerialization "000041-017.attribute")
+ , testCase "000042-002.sig" (testSerialization "000042-002.sig")
+ , testCase "000043-012.ring_trust" (testSerialization "000043-012.ring_trust")
+ , testCase "000044-014.public_subkey" (testSerialization "000044-014.public_subkey")
+ , testCase "000045-002.sig" (testSerialization "000045-002.sig")
+ , testCase "000046-012.ring_trust" (testSerialization "000046-012.ring_trust")
+ , testCase "000047-005.secret_key" (testSerialization "000047-005.secret_key")
+ , testCase "000048-013.user_id" (testSerialization "000048-013.user_id")
+ , testCase "000049-002.sig" (testSerialization "000049-002.sig")
+ , testCase "000050-012.ring_trust" (testSerialization "000050-012.ring_trust")
+ , testCase "000051-007.secret_subkey" (testSerialization "000051-007.secret_subkey")
+ , testCase "000052-002.sig" (testSerialization "000052-002.sig")
+ , testCase "000053-012.ring_trust" (testSerialization "000053-012.ring_trust")
+ , testCase "000054-005.secret_key" (testSerialization "000054-005.secret_key")
+ , testCase "000055-002.sig" (testSerialization "000055-002.sig")
+ , testCase "000056-012.ring_trust" (testSerialization "000056-012.ring_trust")
+ , testCase "000057-013.user_id" (testSerialization "000057-013.user_id")
+ , testCase "000058-002.sig" (testSerialization "000058-002.sig")
+ , testCase "000059-012.ring_trust" (testSerialization "000059-012.ring_trust")
+ , testCase "000060-007.secret_subkey" (testSerialization "000060-007.secret_subkey")
+ , testCase "000061-002.sig" (testSerialization "000061-002.sig")
+ , testCase "000062-012.ring_trust" (testSerialization "000062-012.ring_trust")
+ , testCase "000063-005.secret_key" (testSerialization "000063-005.secret_key")
+ , testCase "000064-002.sig" (testSerialization "000064-002.sig")
+ , testCase "000065-012.ring_trust" (testSerialization "000065-012.ring_trust")
+ , testCase "000066-013.user_id" (testSerialization "000066-013.user_id")
+ , testCase "000067-002.sig" (testSerialization "000067-002.sig")
+ , testCase "000068-012.ring_trust" (testSerialization "000068-012.ring_trust")
+ , testCase "000069-005.secret_key" (testSerialization "000069-005.secret_key")
+ , testCase "000070-013.user_id" (testSerialization "000070-013.user_id")
+ , testCase "000071-002.sig" (testSerialization "000071-002.sig")
+ , testCase "000072-012.ring_trust" (testSerialization "000072-012.ring_trust")
+ , testCase "000073-017.attribute" (testSerialization "000073-017.attribute")
+ , testCase "000074-002.sig" (testSerialization "000074-002.sig")
+ , testCase "000075-012.ring_trust" (testSerialization "000075-012.ring_trust")
+ , testCase "000076-007.secret_subkey" (testSerialization "000076-007.secret_subkey")
+ , testCase "000077-002.sig" (testSerialization "000077-002.sig")
+ , testCase "000078-012.ring_trust" (testSerialization "000078-012.ring_trust")
+ , testCase "pubring.gpg" (testSerialization "pubring.gpg")
+ , testCase "secring.gpg" (testSerialization "secring.gpg")
+ ]
+
+-- main = runTestTT tests
+
+-- specs :: Specs
+-- specs = describe "Round-trip serialization" [it "000001-006.public_key" (testSerialization "000001-006.public_key")
+--   , it "000002-013.user_id" (testSerialization "000002-013.user_id")
+--   , it "000003-002.sig" (testSerialization "000003-002.sig")
+--   , it "000004-012.ring_trust" (testSerialization "000004-012.ring_trust")
+--   , it "000005-014.public_subkey" (testSerialization "000005-014.public_subkey")
+--   , it "000006-002.sig" (testSerialization "000006-002.sig")
+--   , it "000007-012.ring_trust" (testSerialization "000007-012.ring_trust")
+--   , it "000008-006.public_key" (testSerialization "000008-006.public_key")
+--   , it "000009-013.user_id" (testSerialization "000009-013.user_id")
+--   , it "000010-002.sig" (testSerialization "000010-002.sig")
+--   , it "000011-012.ring_trust" (testSerialization "000011-012.ring_trust")
+--   , it "000012-014.public_subkey" (testSerialization "000012-014.public_subkey")
+--   , it "000013-002.sig" (testSerialization "000013-002.sig")
+--   , it "000014-012.ring_trust" (testSerialization "000014-012.ring_trust")
+--   , it "000015-006.public_key" (testSerialization "000015-006.public_key")
+--   , it "000016-013.user_id" (testSerialization "000016-013.user_id")
+--   , it "000017-002.sig" (testSerialization "000017-002.sig")
+--   , it "000018-012.ring_trust" (testSerialization "000018-012.ring_trust")
+--   , it "000019-006.public_key" (testSerialization "000019-006.public_key")
+--   , it "000020-013.user_id" (testSerialization "000020-013.user_id")
+--   , it "000021-002.sig" (testSerialization "000021-002.sig")
+--   , it "000022-012.ring_trust" (testSerialization "000022-012.ring_trust")
+--   , it "000023-005.secret_key" (testSerialization "000023-005.secret_key")
+--   , it "000024-013.user_id" (testSerialization "000024-013.user_id")
+--   , it "000025-002.sig" (testSerialization "000025-002.sig")
+--   , it "000026-012.ring_trust" (testSerialization "000026-012.ring_trust")
+--   , it "000027-007.secret_subkey" (testSerialization "000027-007.secret_subkey")
+--   , it "000028-002.sig" (testSerialization "000028-002.sig")
+--   , it "000029-012.ring_trust" (testSerialization "000029-012.ring_trust")
+--   , it "000030-005.secret_key" (testSerialization "000030-005.secret_key")
+--   , it "000031-013.user_id" (testSerialization "000031-013.user_id")
+--   , it "000032-002.sig" (testSerialization "000032-002.sig")
+--   , it "000033-012.ring_trust" (testSerialization "000033-012.ring_trust")
+--   , it "000034-007.secret_subkey" (testSerialization "000034-007.secret_subkey")
+--   , it "000035-002.sig" (testSerialization "000035-002.sig")
+--   , it "000036-012.ring_trust" (testSerialization "000036-012.ring_trust")
+--   , it "000037-005.secret_key" (testSerialization "000037-005.secret_key")
+--   , it "000038-013.user_id" (testSerialization "000038-013.user_id")
+--   , it "000039-002.sig" (testSerialization "000039-002.sig")
+--   , it "000040-012.ring_trust" (testSerialization "000040-012.ring_trust")
+--   , it "000041-005.secret_key" (testSerialization "000041-005.secret_key")
+--   , it "000042-013.user_id" (testSerialization "000042-013.user_id")
+--   , it "000043-002.sig" (testSerialization "000043-002.sig")
+--   , it "000044-012.ring_trust" (testSerialization "000044-012.ring_trust")
+--   , it "pubring.gpg" (testSerialization "pubring.gpg")
+--   , it "secring.gpg" (testSerialization "secring.gpg")
+--   ]
+
+-- main = hspec specs
+
+main = defaultMain tests
