packages feed

hOpenPGP 0.0.0 → 0.1

raw patch · 8 files changed

+370/−85 lines, 8 filesdep +attoparsecdep +base64-bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: attoparsec, base64-bytestring

API changes (from Hackage documentation)

+ Codec.Encryption.OpenPGP.ASCIIArmor: armor :: (Integral a, Show a) => Armor a -> ByteString
+ Codec.Encryption.OpenPGP.ASCIIArmor: decodeArmor :: (Integral a, Read a, Show a, IsString e) => ByteString -> Either e (Armor a)
+ Codec.Encryption.OpenPGP.ASCIIArmor: parseArmor :: (Integral a, Read a, Show a) => Parser (Armor a)
+ Codec.Encryption.OpenPGP.Types: Armor :: (ArmorType a) -> [ArmorHeader] -> [Packet] -> Armor a
+ Codec.Encryption.OpenPGP.Types: ArmorMessage :: ArmorType a
+ Codec.Encryption.OpenPGP.Types: ArmorPrivateKeyBlock :: ArmorType a
+ Codec.Encryption.OpenPGP.Types: ArmorPublicKeyBlock :: ArmorType a
+ Codec.Encryption.OpenPGP.Types: ArmorSignature :: ArmorType a
+ Codec.Encryption.OpenPGP.Types: ArmorSplitMessage :: a -> a -> ArmorType a
+ Codec.Encryption.OpenPGP.Types: ArmorSplitMessageIndefinite :: a -> ArmorType a
+ Codec.Encryption.OpenPGP.Types: BinaryData :: DataType
+ Codec.Encryption.OpenPGP.Types: OtherData :: Word8 -> DataType
+ Codec.Encryption.OpenPGP.Types: TextData :: DataType
+ Codec.Encryption.OpenPGP.Types: UTF8Data :: DataType
+ Codec.Encryption.OpenPGP.Types: data Armor a
+ Codec.Encryption.OpenPGP.Types: data ArmorType a
+ Codec.Encryption.OpenPGP.Types: data DataType
+ Codec.Encryption.OpenPGP.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Codec.Encryption.OpenPGP.Types.Armor a)
+ Codec.Encryption.OpenPGP.Types: instance GHC.Classes.Eq a => GHC.Classes.Eq (Codec.Encryption.OpenPGP.Types.ArmorType a)
+ Codec.Encryption.OpenPGP.Types: instance GHC.Show.Show a => GHC.Show.Show (Codec.Encryption.OpenPGP.Types.Armor a)
+ Codec.Encryption.OpenPGP.Types: instance GHC.Show.Show a => GHC.Show.Show (Codec.Encryption.OpenPGP.Types.ArmorType a)
+ Codec.Encryption.OpenPGP.Types: type ArmorHeader = (ByteString, ByteString)

Files

+ Codec/Encryption/OpenPGP/ASCIIArmor.hs view
@@ -0,0 +1,13 @@+-- ASCIIArmor.hs: OpenPGP (RFC4880) ASCII armor implementation+-- Copyright Ⓒ 2012  Clint Adams+-- This software is released under the terms of the Expat (MIT) license.+-- (See the LICENSE file).++module Codec.Encryption.OpenPGP.ASCIIArmor (+   armor+ , decodeArmor+ , parseArmor+) where++import Codec.Encryption.OpenPGP.ASCIIArmor.Encode (armor)+import Codec.Encryption.OpenPGP.ASCIIArmor.Decode (decodeArmor, parseArmor)
+ Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs view
@@ -0,0 +1,128 @@+{-# LANGUAGE OverloadedStrings #-}+-- ASCIIArmor/Decode.hs: OpenPGP (RFC4880) ASCII armor implementation+-- Copyright Ⓒ 2012  Clint Adams+-- This software is released under the terms of the Expat (MIT) license.+-- (See the LICENSE file).++module Codec.Encryption.OpenPGP.ASCIIArmor.Decode (+   parseArmor+ , decodeArmor+) where++import Codec.Encryption.OpenPGP.Serialize (getPackets)+import Codec.Encryption.OpenPGP.Types+import Control.Applicative (many, (<|>), (<$>))+import Data.Attoparsec.ByteString (Parser, many1, string, inClass, notInClass, satisfy, word8, (<?>), parse, IResult(..))+import Data.Attoparsec.ByteString.Char8 (isDigit_w8)+import Data.Bits (shiftL)+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC8+import qualified Data.ByteString.Base64 as Base64+import Data.Digest.CRC24 (crc24)+import Data.Serialize.Get (Get, runGet, getWord8)+import Data.Serialize.Put (runPut, putWord32be)+import Data.String (IsString, fromString)+import Data.Word (Word32)++decodeArmor :: (Integral a, Read a, Show a, IsString e) => ByteString -> Either e (Armor a)+decodeArmor bs = case parse parseArmor bs of+                     Fail t c e -> Left (fromString e)+                     Partial _ -> Left (fromString "what")+                     Done _ r -> Right r++parseArmor :: (Integral a, Read a, Show a) => Parser (Armor a)+parseArmor = do+    atype <- beginLine <?> "begin line"+    headers <- armorHeaders <?> "headers"+    blankishLine <?> "blank line"+    payload <- base64Data <?> "base64 data"+    endLine atype <?> "end line"+    case runGet getPackets payload of+        Left err -> fail err+        Right packets -> return $ Armor atype headers packets++beginLine :: (Integral a, Read a, Show a) => Parser (ArmorType a)+beginLine = do+    string "-----BEGIN PGP "+    atype <- message <|> pubkey <|> privkey<|> parts <|> signature+    string "-----"+    many (satisfy (inClass " \t"))+    lineEnding+    return atype+    where+        message = string "MESSAGE" >> return ArmorMessage+        pubkey = string "PUBLIC KEY BLOCK" >> return ArmorPublicKeyBlock+        privkey = string "PRIVATE KEY BLOCK" >> return ArmorPrivateKeyBlock+        signature = string "SIGNATURE" >> return ArmorSignature+        parts = do+            string "MESSAGE, PART "+            firstnum <- read . BC8.unpack . B.pack <$> many1 (satisfy isDigit_w8)+            return $ ArmorSplitMessageIndefinite firstnum++lineEnding :: Parser ByteString+lineEnding = string "\n" <|> string "\r\n"++armorHeaders :: Parser [ArmorHeader]+armorHeaders = many armorHeader++armorHeader :: Parser ArmorHeader+armorHeader = do+    key <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"))+    string ": "+    val <- many1 (satisfy (notInClass "\n\r"))+    lineEnding+    return (B.pack key, B.pack val)++blankishLine ::  Parser ByteString+blankishLine = many (satisfy (inClass " \t")) >> lineEnding++endLine :: (Integral a, Read a, Show a) => ArmorType a -> Parser ByteString+endLine atype = do+    string $ "-----END PGP " `B.append` aType atype `B.append` "-----"+    lineEnding++aType :: (Integral a, Read a, Show a) => ArmorType a -> ByteString+aType (ArmorMessage) = BC8.pack "MESSAGE"+aType (ArmorPublicKeyBlock) = BC8.pack "PUBLIC KEY BLOCK"+aType (ArmorPrivateKeyBlock) = BC8.pack "PRIVATE KEY BLOCK"+aType (ArmorSplitMessage x y) = BC8.pack $ "MESSAGE, PART " ++ show x ++ "/" ++ show y+aType (ArmorSplitMessageIndefinite x) = BC8.pack $ "MESSAGE, PART " ++ show x+aType (ArmorSignature) = BC8.pack "SIGNATURE"++base64Data :: Parser ByteString+base64Data = do+    ls <- many1 base64Line+    cksum <- checksumLine+    let payload = B.concat ls+    let ourcksum = crc24 payload+    case runGet d24 cksum of+        Left err -> fail err+        Right theircksum -> if theircksum == ourcksum then return payload else fail ("CRC24 mismatch: " ++ show (B.unpack cksum) ++  "/" ++ show theircksum ++ " vs. " ++ show ourcksum)+    where+        base64Line :: Parser ByteString+        base64Line = do+                        b64 <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"))+                        pad <- many (word8 (fromIntegral . fromEnum $ '='))+                        lineEnding+                        let line = B.pack b64 `B.append` B.pack pad+                        case Base64.decode line of+                            Left err -> fail err+                            Right bs -> return bs+        checksumLine :: Parser ByteString+        checksumLine = do+                        word8 (fromIntegral . fromEnum $ '=')+                        b64 <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"))+                        lineEnding+                        let line = B.pack b64+                        case Base64.decode line of+                            Left err -> fail err+                            Right bs -> return bs+++d24 :: Get Word32+d24 = do+    a <- getWord8+    b <- getWord8+    c <- getWord8+    return $ shiftL (fromIntegral a :: Word32) 16 + shiftL (fromIntegral b :: Word32) 8 + (fromIntegral c :: Word32)
+ Codec/Encryption/OpenPGP/ASCIIArmor/Encode.hs view
@@ -0,0 +1,58 @@+-- ASCIIArmor/Encode.hs: OpenPGP (RFC4880) ASCII armor implementation+-- Copyright Ⓒ 2012  Clint Adams+-- This software is released under the terms of the Expat (MIT) license.+-- (See the LICENSE file).++module Codec.Encryption.OpenPGP.ASCIIArmor.Encode (+   armor+) where++import Codec.Encryption.OpenPGP.Serialize (putPackets)+import Codec.Encryption.OpenPGP.Types+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Char8 as BC8+import qualified Data.ByteString.Base64 as Base64+import Data.Digest.CRC24 (crc24)+import Data.Serialize.Put (runPut, putWord32be)+import Data.String (IsString, fromString)++armor :: (Integral a, Show a) => Armor a -> ByteString+armor (Armor atype ahs ps) = beginLine atype `B.append` armorHeaders ahs `B.append` blankLine `B.append` armorData (opgpStream ps) `B.append` armorChecksum (opgpStream ps) `B.append` endLine atype++blankLine :: ByteString+blankLine = BC8.singleton '\n'++beginLine :: (Integral a, Show a) => ArmorType a -> ByteString+beginLine atype = BC8.pack "-----BEGIN PGP " `B.append` aType atype `B.append` BC8.pack "-----\n"++endLine :: (Integral a, Show a) => ArmorType a -> ByteString+endLine atype = BC8.pack "-----END PGP " `B.append` aType atype `B.append` BC8.pack "-----\n"++aType :: (Integral a, Show a) => ArmorType a -> ByteString+aType (ArmorMessage) = BC8.pack "MESSAGE"+aType (ArmorPublicKeyBlock) = BC8.pack "PUBLIC KEY BLOCK"+aType (ArmorPrivateKeyBlock) = BC8.pack "PRIVATE KEY BLOCK"+aType (ArmorSplitMessage x y) = BC8.pack $ "MESSAGE, PART " ++ show x ++ "/" ++ show y+aType (ArmorSplitMessageIndefinite x) = BC8.pack $ "MESSAGE, PART " ++ show x+aType (ArmorSignature) = BC8.pack "SIGNATURE"++armorHeaders :: [ArmorHeader] -> ByteString+armorHeaders ahs = BC8.unlines . map armorHeader $ ahs+    where+        armorHeader :: ArmorHeader -> ByteString+        armorHeader (k, v) = k `B.append` BC8.pack ": " `B.append` v++opgpStream :: [Packet] -> ByteString+opgpStream = runPut . putPackets++armorData :: ByteString -> ByteString+armorData = BC8.unlines . wrap76 . Base64.encode++wrap76 :: ByteString -> [ByteString]+wrap76 bs+    | B.null bs = []+    | otherwise = B.take 76 bs : wrap76 (B.drop 76 bs)++armorChecksum :: ByteString -> ByteString+armorChecksum = BC8.cons '=' . armorData . B.tail . runPut . putWord32be . crc24
Codec/Encryption/OpenPGP/Types.hs view
@@ -19,10 +19,14 @@  , ImageFormat(..)  , PKPayload(..)  , SKAddendum(..)+ , ArmorType(..)+ , Armor(Armor)+ , DataType(..)  , EightOctetKeyId  , SessionKey  , FutureFlag  , FutureVal+ , ArmorHeader  , fromFVal  , fromFFlag  , toFVal@@ -517,4 +521,15 @@     toFVal 0x50 = ThirdPartyConfirmationSig     toFVal o = OtherSig o +data ArmorType a = ArmorMessage+                 | ArmorPublicKeyBlock+                 | ArmorPrivateKeyBlock+                 | ArmorSplitMessage a a+                 | ArmorSplitMessageIndefinite a+                 | ArmorSignature+    deriving (Show, Eq) +type ArmorHeader = (ByteString, ByteString)++data Armor a = Armor (ArmorType a) [ArmorHeader] [Packet]+    deriving (Show, Eq)
+ Data/Digest/CRC24.hs view
@@ -0,0 +1,25 @@+-- CRC24.hs: OpenPGP (RFC4880) CRC24 implementation+-- Copyright Ⓒ 2012  Clint Adams+-- This software is released under the terms of the Expat (MIT) license.+-- (See the LICENSE file).++module Data.Digest.CRC24 (+  crc24+) where++import Data.Bits (shiftL, (.&.), xor)+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Word (Word8, Word32)++crc24_init :: Word32+crc24_init = 0xB704CE++crc24_poly :: Word32+crc24_poly = 0x1864CFB++crc24_update :: Word32 -> Word8 -> Word32+crc24_update c b = (last . take 9 $ iterate (\x -> if (shiftL x 1) .&. 0x1000000 == 0x1000000 then shiftL x 1 `xor` crc24_poly else shiftL x 1) (c `xor` shiftL (fromIntegral b) 16)) .&. 0xFFFFFF++crc24 :: ByteString -> Word32+crc24 bs = B.foldl crc24_update crc24_init bs
hOpenPGP.cabal view
@@ -1,5 +1,5 @@ Name:                hOpenPGP-Version:             0.0.0+Version:             0.1 Synopsis:            native Haskell implementation of OpenPGP (RFC4880) Description:         native Haskell implementation of OpenPGP (RFC4880) Homepage:            http://floss.scru.org/hOpenPGP/@@ -8,7 +8,7 @@ Author:              Clint Adams Maintainer:          Clint Adams <clint@debian.org> Copyright:           2012, Clint Adams-Category:            Codec+Category:            Codec, Data Build-type:          Simple Extra-source-files: tests/suite.hs   , tests/data/000001-006.public_key@@ -91,13 +91,21 @@   , tests/data/000078-012.ring_trust   , tests/data/pubring.gpg   , tests/data/secring.gpg+  , tests/data/msg1.asc  Cabal-version:       >= 1.10   Library-  Exposed-modules:     Codec.Encryption.OpenPGP.Types, Codec.Encryption.OpenPGP.Serialize-  Build-depends: base             > 4       && < 5+  Exposed-modules:     Codec.Encryption.OpenPGP.Types+                     , Codec.Encryption.OpenPGP.Serialize+                     , Codec.Encryption.OpenPGP.ASCIIArmor+  Other-Modules: Data.Digest.CRC24+               , Codec.Encryption.OpenPGP.ASCIIArmor.Decode+               , Codec.Encryption.OpenPGP.ASCIIArmor.Encode+  Build-depends: attoparsec+               , base                  > 4       && < 5+               , base64-bytestring                , bytestring                , cereal                , containers@@ -107,7 +115,9 @@ Test-Suite tests   type:       exitcode-stdio-1.0   main-is:    tests/suite.hs-  Build-depends: base             > 4       && < 5+  Build-depends: attoparsec+               , base                  > 4       && < 5+               , base64-bytestring                , bytestring                , cereal                , containers
+ tests/data/msg1.asc view
@@ -0,0 +1,7 @@+-----BEGIN PGP MESSAGE-----+Version: OpenPrivacy 0.99++yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS+vBSFjNSiVHsuAA==+=njUN+-----END PGP MESSAGE-----
tests/suite.hs view
@@ -6,9 +6,14 @@ import Test.HUnit  import Codec.Encryption.OpenPGP.Serialize (getPackets, putPackets)+import Codec.Encryption.OpenPGP.ASCIIArmor (armor, decodeArmor)+import Codec.Encryption.OpenPGP.Types+import Data.ByteString (ByteString) import qualified Data.ByteString as B+import Data.Digest.CRC24 (crc24) import Data.Serialize.Get (runGet) import Data.Serialize.Put (runPut)+import Data.Word (Word32)  testSerialization :: FilePath -> Assertion testSerialization fp = do@@ -20,87 +25,111 @@                           let secondpass = runGet getPackets roundtrip                           assertEqual ("for " ++ fp) firstpass secondpass +testCRC24 :: ByteString -> Word32 -> Assertion+testCRC24 bs crc = assertEqual "crc24" crc (crc24 bs)++testArmorDecode :: FilePath -> Armor Int -> Assertion+testArmorDecode fp target = do+    bs <- B.readFile $ "tests/data/" ++ fp+    case decodeArmor bs of+        Left err -> assertFailure $ "Decode failed on " ++ fp+        Right arm -> do assertEqual ("for " ++ fp) target arm++testArmorEncode :: Armor Int -> ByteString -> Assertion+testArmorEncode arm target = assertEqual ("literaldata") (armor arm) target+ 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")+  testGroup "Serialization group" [+     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")+   ],+  testGroup "CRC24 group" [+     testCase "CRC24: A" (testCRC24 "A" 16680698)+   , testCase "CRC24: Haskell" (testCRC24 "Haskell" 15612750)+   , testCase "CRC24: hOpenPGP and friends" (testCRC24 "hOpenPGP and friends" 11940960)+   ],+  testGroup "ASCII armor group" [+     testCase "Decode sample armor" (testArmorDecode "msg1.asc" (Armor ArmorMessage [("Version","OpenPrivacy 0.99")] [CompressedData ZIP ";m\150\196\DC1\239\236\239\ETB\236\239\227\202\NUL\EOT\206\137y\234%\n\137y\149\249y\169\n\217\169\169\ENQ\n\137\n\197\169\201E\169@\193\162\252\210\188\DC4\133\140\212\162T{.\NUL"]))+   , testCase "Encode sample armor" (testArmorEncode (Armor ArmorMessage [("Comment", "Test")] [LiteralData UTF8Data "notlob.txt" 12345 "These are the days of literal data.\n"]) "-----BEGIN PGP MESSAGE-----\nComment: Test\n\nyzR1Cm5vdGxvYi50eHQAADA5VGhlc2UgYXJlIHRoZSBkYXlzIG9mIGxpdGVyYWwgZGF0YS4K\n=AAKY\n-----END PGP MESSAGE-----\n")+   ]  ]  -- main = runTestTT tests