summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClint Adams <clint@debian.org>2012-04-25 19:10:27 -0400
committerClint Adams <clint@debian.org>2012-04-25 19:10:27 -0400
commitf06c3f713f99c9ea09a76728ffce2c6e1c957070 (patch)
tree647c5c500b0713ff73533988944eef3bb4ca1bf8
parent5ed645493e10190f7cddd753bb058e8487037549 (diff)
Handle decoding multiple ASCII armor messages from a single bytestream.
-rw-r--r--Codec/Encryption/OpenPGP/ASCIIArmor.hs4
-rw-r--r--Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs26
-rw-r--r--openpgp-asciiarmor.cabal2
-rw-r--r--tests/data/msg1a.asc11
-rw-r--r--tests/data/msg1b.asc28
-rw-r--r--tests/suite.hs18
6 files changed, 71 insertions, 18 deletions
diff --git a/Codec/Encryption/OpenPGP/ASCIIArmor.hs b/Codec/Encryption/OpenPGP/ASCIIArmor.hs
index 74c8c69..075a481 100644
--- a/Codec/Encryption/OpenPGP/ASCIIArmor.hs
+++ b/Codec/Encryption/OpenPGP/ASCIIArmor.hs
@@ -5,9 +5,9 @@
5 5
6module Codec.Encryption.OpenPGP.ASCIIArmor ( 6module Codec.Encryption.OpenPGP.ASCIIArmor (
7 armor 7 armor
8 , decodeArmor 8 , decode
9 , parseArmor 9 , parseArmor
10) where 10) where
11 11
12import Codec.Encryption.OpenPGP.ASCIIArmor.Encode (armor) 12import Codec.Encryption.OpenPGP.ASCIIArmor.Encode (armor)
13import Codec.Encryption.OpenPGP.ASCIIArmor.Decode (decodeArmor, parseArmor) 13import Codec.Encryption.OpenPGP.ASCIIArmor.Decode (decode, parseArmor)
diff --git a/Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs b/Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs
index 8c2a8a3..e69087c 100644
--- a/Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs
+++ b/Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs
@@ -6,13 +6,14 @@
6 6
7module Codec.Encryption.OpenPGP.ASCIIArmor.Decode ( 7module Codec.Encryption.OpenPGP.ASCIIArmor.Decode (
8 parseArmor 8 parseArmor
9 , decodeArmor 9 , decode
10) where 10) where
11 11
12import Codec.Encryption.OpenPGP.ASCIIArmor.Types 12import Codec.Encryption.OpenPGP.ASCIIArmor.Types
13import Control.Applicative (many, (<|>), (<$>)) 13import Control.Applicative (many, (<|>), (<$>), Alternative, (*>))
14import Data.Attoparsec.ByteString (Parser, many1, string, inClass, notInClass, satisfy, word8, (<?>), parse, IResult(..)) 14import Data.Attoparsec.ByteString (Parser, many1, string, inClass, notInClass, satisfy, word8, (<?>), parse, IResult(..))
15import Data.Attoparsec.ByteString.Char8 (isDigit_w8) 15import Data.Attoparsec.ByteString.Char8 (isDigit_w8, anyChar)
16import Data.Attoparsec.Combinator (manyTill)
16import Data.Bits (shiftL) 17import Data.Bits (shiftL)
17import Data.ByteString (ByteString) 18import Data.ByteString (ByteString)
18import qualified Data.ByteString as B 19import qualified Data.ByteString as B
@@ -25,15 +26,19 @@ import Data.Serialize.Put (runPut, putWord32be)
25import Data.String (IsString, fromString) 26import Data.String (IsString, fromString)
26import Data.Word (Word32) 27import Data.Word (Word32)
27 28
28decodeArmor :: (Integral a, Read a, Show a, IsString e) => ByteString -> Either e (Armor a) 29decode :: (Integral a, Read a, Show a, IsString e) => ByteString -> Either e ([Armor a])
29decodeArmor bs = case parse parseArmor bs of 30decode bs = go (parse parseArmors bs)
30 Fail t c e -> Left (fromString e) 31 where
31 Partial _ -> Left (fromString "what") 32 go (Fail t c e) = Left (fromString e)
32 Done _ r -> Right r 33 go (Partial cont) = go (cont B.empty)
34 go (Done _ r) = Right r
35
36parseArmors :: (Integral a, Read a, Show a) => Parser ([Armor a])
37parseArmors = many parseArmor
33 38
34parseArmor :: (Integral a, Read a, Show a) => Parser (Armor a) 39parseArmor :: (Integral a, Read a, Show a) => Parser (Armor a)
35parseArmor = do 40parseArmor = do
36 atype <- beginLine <?> "begin line" 41 atype <- prefixed beginLine <?> "begin line"
37 headers <- armorHeaders <?> "headers" 42 headers <- armorHeaders <?> "headers"
38 blankishLine <?> "blank line" 43 blankishLine <?> "blank line"
39 payload <- base64Data <?> "base64 data" 44 payload <- base64Data <?> "base64 data"
@@ -126,3 +131,6 @@ d24 = do
126 b <- getWord8 131 b <- getWord8
127 c <- getWord8 132 c <- getWord8
128 return $ shiftL (fromIntegral a :: Word32) 16 + shiftL (fromIntegral b :: Word32) 8 + (fromIntegral c :: Word32) 133 return $ shiftL (fromIntegral a :: Word32) 16 + shiftL (fromIntegral b :: Word32) 8 + (fromIntegral c :: Word32)
134
135prefixed :: Parser a -> Parser a
136prefixed end = end <|> anyChar *> prefixed end
diff --git a/openpgp-asciiarmor.cabal b/openpgp-asciiarmor.cabal
index 4b7e25d..ff2289a 100644
--- a/openpgp-asciiarmor.cabal
+++ b/openpgp-asciiarmor.cabal
@@ -12,6 +12,8 @@ Category: Codec, Data
12Build-type: Simple 12Build-type: Simple
13Extra-source-files: tests/suite.hs 13Extra-source-files: tests/suite.hs
14 , tests/data/msg1.asc 14 , tests/data/msg1.asc
15 , tests/data/msg1a.asc
16 , tests/data/msg1b.asc
15 , tests/data/msg1.gpg 17 , tests/data/msg1.gpg
16 18
17Cabal-version: >= 1.10 19Cabal-version: >= 1.10
diff --git a/tests/data/msg1a.asc b/tests/data/msg1a.asc
new file mode 100644
index 0000000..64dc079
--- /dev/null
+++ b/tests/data/msg1a.asc
@@ -0,0 +1,11 @@
1This file contains an ASCII-armored PGP message enclosed between
2
3-----BEGIN PGP MESSAGE-----
4Version: OpenPrivacy 0.99
5
6yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
7vBSFjNSiVHsuAA==
8=njUN
9-----END PGP MESSAGE-----
10
11two lines of arbitrary text.
diff --git a/tests/data/msg1b.asc b/tests/data/msg1b.asc
new file mode 100644
index 0000000..1176420
--- /dev/null
+++ b/tests/data/msg1b.asc
@@ -0,0 +1,28 @@
1This file contains three ASCII-armored PGP messages interspersed
2
3-----BEGIN PGP MESSAGE-----
4Version: OpenPrivacy 0.99
5
6yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
7vBSFjNSiVHsuAA==
8=njUN
9-----END PGP MESSAGE-----
10-----BEGIN PGP MESSAGE-----
11Version: OpenPrivacy 0.99
12
13yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
14vBSFjNSiVHsuAA==
15=njUN
16-----END PGP MESSAGE-----
17
18with arbitrary text.
19
20-----BEGIN PGP MESSAGE-----
21Version: OpenPrivacy 0.99
22
23yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
24vBSFjNSiVHsuAA==
25=njUN
26-----END PGP MESSAGE-----
27
28All three messages are identical.
diff --git a/tests/suite.hs b/tests/suite.hs
index 272295a..45a2fb2 100644
--- a/tests/suite.hs
+++ b/tests/suite.hs
@@ -3,7 +3,7 @@ import Test.Framework.Providers.HUnit
3 3
4import Test.HUnit 4import Test.HUnit
5 5
6import Codec.Encryption.OpenPGP.ASCIIArmor (armor, decodeArmor) 6import Codec.Encryption.OpenPGP.ASCIIArmor (armor, decode)
7import Codec.Encryption.OpenPGP.ASCIIArmor.Types 7import Codec.Encryption.OpenPGP.ASCIIArmor.Types
8 8
9import Data.ByteString (ByteString) 9import Data.ByteString (ByteString)
@@ -15,13 +15,15 @@ import Data.Word (Word32)
15testCRC24 :: ByteString -> Word32 -> Assertion 15testCRC24 :: ByteString -> Word32 -> Assertion
16testCRC24 bs crc = assertEqual "crc24" crc (crc24 bs) 16testCRC24 bs crc = assertEqual "crc24" crc (crc24 bs)
17 17
18testArmorDecode :: FilePath -> FilePath -> Assertion 18testArmorDecode :: FilePath -> [FilePath] -> Assertion
19testArmorDecode fp target = do 19testArmorDecode fp targets = do
20 bs <- B.readFile $ "tests/data/" ++ fp 20 bs <- B.readFile $ "tests/data/" ++ fp
21 tbs <- B.readFile $ "tests/data/" ++ target 21 tbss <- mapM (\target -> B.readFile $ "tests/data/" ++ target) targets
22 case decodeArmor bs of 22 case decode bs of
23 Left e -> assertFailure $ "Decode failed (" ++ e ++ ") on " ++ fp 23 Left e -> assertFailure $ "Decode failed (" ++ e ++ ") on " ++ fp
24 Right (Armor at hdrs pl) -> do assertEqual ("for " ++ fp) tbs pl 24 Right as -> do assertEqual ("for " ++ fp) tbss (map getPayload as)
25 where
26 getPayload (Armor _ _ pl) = pl
25 27
26testArmorEncode :: FilePath -> FilePath -> Assertion 28testArmorEncode :: FilePath -> FilePath -> Assertion
27testArmorEncode fp target = do 29testArmorEncode fp target = do
@@ -36,7 +38,9 @@ tests = [
36 , testCase "CRC24: hOpenPGP and friends" (testCRC24 (BC8.pack "hOpenPGP and friends") 11940960) 38 , testCase "CRC24: hOpenPGP and friends" (testCRC24 (BC8.pack "hOpenPGP and friends") 11940960)
37 ] 39 ]
38 , testGroup "ASCII armor" [ 40 , testGroup "ASCII armor" [
39 testCase "Decode sample armor" (testArmorDecode "msg1.asc" "msg1.gpg") 41 testCase "Decode sample armor" (testArmorDecode "msg1.asc" ["msg1.gpg"])
42 , testCase "Decode sample armor with cruft" (testArmorDecode "msg1a.asc" ["msg1.gpg"])
43 , testCase "Decode multiple sample armors" (testArmorDecode "msg1b.asc" ["msg1.gpg","msg1.gpg","msg1.gpg"])
40 , testCase "Encode sample armor" (testArmorEncode "msg1.gpg" "msg1.asc") 44 , testCase "Encode sample armor" (testArmorEncode "msg1.gpg" "msg1.asc")
41 ] 45 ]
42 ] 46 ]