openpgp-asciiarmor 0.1 → 0.1.1
raw patch · 11 files changed
+121/−87 lines, 11 filesdep +binarydep +criteriondep +openpgp-asciiarmordep −HUnitdep −cerealdep −test-frameworkdep ~basedep ~bytestring
Dependencies added: binary, criterion, openpgp-asciiarmor, tasty, tasty-hunit
Dependencies removed: HUnit, cereal, test-framework, test-framework-hunit
Dependency ranges changed: base, bytestring
Files
- Codec/Encryption/OpenPGP/ASCIIArmor.hs +1/−1
- Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs +47/−49
- Codec/Encryption/OpenPGP/ASCIIArmor/Encode.hs +8/−11
- Codec/Encryption/OpenPGP/ASCIIArmor/Multipart.hs +3/−2
- Codec/Encryption/OpenPGP/ASCIIArmor/Types.hs +1/−1
- Codec/Encryption/OpenPGP/ASCIIArmor/Utils.hs +1/−1
- Data/Digest/CRC24.hs +1/−1
- LICENSE +1/−1
- bench/mark.hs +16/−0
- openpgp-asciiarmor.cabal +27/−11
- tests/suite.hs +15/−9
Codec/Encryption/OpenPGP/ASCIIArmor.hs view
@@ -1,5 +1,5 @@ -- ASCIIArmor.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file).
Codec/Encryption/OpenPGP/ASCIIArmor/Decode.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings #-} -- ASCIIArmor/Decode.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012-2018 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file). @@ -12,36 +12,34 @@ import Codec.Encryption.OpenPGP.ASCIIArmor.Types import Codec.Encryption.OpenPGP.ASCIIArmor.Utils-import Control.Applicative (many, (<|>), (<$>), Alternative, (<*), (<*>), (*>), optional)+import Control.Applicative (many, (<|>), (<$>), (<*), (<*>), (*>), optional) import Data.Attoparsec.ByteString (Parser, many1, string, inClass, notInClass, satisfy, word8, (<?>)) import qualified Data.Attoparsec.ByteString as AS import qualified Data.Attoparsec.ByteString.Lazy as AL import Data.Attoparsec.ByteString.Char8 (isDigit_w8, anyChar)-import Data.Attoparsec.Combinator (manyTill) import Data.Bits (shiftL)-import Data.ByteString (ByteString)+import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Char8 as BC8 import qualified Data.ByteString.Base64 as Base64 import Data.Digest.CRC24 (crc24)-import Data.Serialize (get)-import Data.Serialize.Get (Get, runGet, getWord8)-import Data.Serialize.Put (runPut, putWord32be)+import Data.Binary.Get (Get, runGetOrFail, getWord8)+import Data.Functor (($>)) import Data.String (IsString, fromString) import Data.Word (Word32) -decode :: IsString e => ByteString -> Either e [Armor]+decode :: IsString e => B.ByteString -> Either e [Armor] decode bs = go (AS.parse parseArmors bs) where- go (AS.Fail t c e) = Left (fromString e)+ go (AS.Fail _ _ e) = Left (fromString e) go (AS.Partial cont) = go (cont B.empty) go (AS.Done _ r) = Right r decodeLazy :: IsString e => BL.ByteString -> Either e [Armor] decodeLazy bs = go (AL.parse parseArmors bs) where- go (AL.Fail t c e) = Left (fromString e)+ go (AL.Fail _ _ e) = Left (fromString e) go (AL.Done _ r) = Right r parseArmors :: Parser [Armor]@@ -52,46 +50,46 @@ clearsigned :: Parser Armor clearsigned = do- string "-----BEGIN PGP SIGNED MESSAGE-----" <?> "clearsign header"- lineEnding <?> "line ending"+ _ <- string "-----BEGIN PGP SIGNED MESSAGE-----" <?> "clearsign header"+ _ <- lineEnding <?> "line ending" headers <- armorHeaders <?> "clearsign headers"- blankishLine <?> "blank line"+ _ <- blankishLine <?> "blank line" cleartext <- dashEscapedCleartext sig <- armor- return $ ClearSigned headers (BL.fromChunks [cleartext]) sig+ return $ ClearSigned headers cleartext sig armor :: Parser Armor armor = do atype <- beginLine <?> "begin line" headers <- armorHeaders <?> "headers"- blankishLine <?> "blank line"+ _ <- blankishLine <?> "blank line" payload <- base64Data <?> "base64 data"- endLine atype <?> "end line"- return $ Armor atype headers (BL.fromChunks [payload])+ _ <- endLine atype <?> "end line"+ return $ Armor atype headers payload beginLine :: Parser ArmorType beginLine = do- string "-----BEGIN PGP " <?> "leading minus-hyphens"+ _ <- string "-----BEGIN PGP " <?> "leading minus-hyphens" atype <- pubkey <|> privkey <|> parts <|> message <|> signature- string "-----" <?> "trailing minus-hyphens"- many (satisfy (inClass " \t")) <?> "whitespace"- lineEnding <?> "line ending"+ _ <- string "-----" <?> "trailing minus-hyphens"+ _ <- many (satisfy (inClass " \t")) <?> "whitespace"+ _ <- lineEnding <?> "line ending" 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+ message = string "MESSAGE" $> ArmorMessage+ pubkey = string "PUBLIC KEY BLOCK" $> ArmorPublicKeyBlock+ privkey = string "PRIVATE KEY BLOCK" $> ArmorPrivateKeyBlock+ signature = string "SIGNATURE" $> ArmorSignature parts = string "MESSAGE, PART " *> (partsdef <|> partsindef) partsdef = do firstnum <- num- word8 (fromIntegral . fromEnum $ '/')+ _ <- word8 (fromIntegral . fromEnum $ '/') secondnum <- num return $ ArmorSplitMessage (BL.pack firstnum) (BL.pack secondnum) partsindef = ArmorSplitMessageIndefinite . BL.pack <$> num num = many1 (satisfy isDigit_w8) <?> "number" -lineEnding :: Parser ByteString+lineEnding :: Parser B.ByteString lineEnding = string "\n" <|> string "\r\n" armorHeaders :: Parser [(String, String)]@@ -100,30 +98,30 @@ armorHeader :: Parser (String, String) armorHeader = do key <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"))- string ": "+ _ <- string ": " val <- many1 (satisfy (notInClass "\n\r"))- lineEnding+ _ <- lineEnding return (w8sToString key, w8sToString val) where w8sToString = BC8.unpack . B.pack -blankishLine :: Parser ByteString+blankishLine :: Parser B.ByteString blankishLine = many (satisfy (inClass " \t")) *> lineEnding -endLine :: ArmorType -> Parser ByteString+endLine :: ArmorType -> Parser B.ByteString endLine atype = do- string $ "-----END PGP " `B.append` aType atype `B.append` "-----"+ _ <- string $ "-----END PGP " `B.append` aType atype `B.append` "-----" lineEnding -aType :: ArmorType -> ByteString-aType (ArmorMessage) = BC8.pack "MESSAGE"-aType (ArmorPublicKeyBlock) = BC8.pack "PUBLIC KEY BLOCK"-aType (ArmorPrivateKeyBlock) = BC8.pack "PRIVATE KEY BLOCK"+aType :: ArmorType -> B.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 " `B.append` l2s x `B.append` BC8.singleton '/' `B.append` l2s y aType (ArmorSplitMessageIndefinite x) = BC8.pack "MESSAGE, PART " `B.append` l2s x-aType (ArmorSignature) = BC8.pack "SIGNATURE"+aType ArmorSignature = BC8.pack "SIGNATURE" -l2s :: BL.ByteString -> ByteString+l2s :: BL.ByteString -> B.ByteString l2s = B.concat . BL.toChunks base64Data :: Parser ByteString@@ -132,24 +130,24 @@ 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)+ case runGetOrFail d24 (BL.fromStrict cksum) of+ Left (_,_,err) -> fail err+ Right (_,_,theircksum) -> if theircksum == ourcksum then return (BL.fromStrict payload) else fail ("CRC24 mismatch: " ++ show (B.unpack cksum) ++ "/" ++ show theircksum ++ " vs. " ++ show ourcksum) where- base64Line :: Parser ByteString+ base64Line :: Parser B.ByteString base64Line = do b64 <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/")) pad <- many (word8 (fromIntegral . fromEnum $ '='))- lineEnding+ _ <- 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 :: Parser B.ByteString checksumLine = do- word8 (fromIntegral . fromEnum $ '=')+ _ <- word8 (fromIntegral . fromEnum $ '=') b64 <- many1 (satisfy (inClass "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"))- lineEnding+ _ <- lineEnding let line = B.pack b64 case Base64.decode line of Left err -> fail err@@ -169,9 +167,9 @@ dashEscapedCleartext :: Parser ByteString dashEscapedCleartext = do ls <- many1 ((deLine <|> unescapedLine) <* lineEnding)- return $ crlfUnlines ls+ return . BL.fromStrict $ crlfUnlines ls where- deLine :: Parser ByteString+ deLine :: Parser B.ByteString deLine = B.pack <$> (string "- " *> many (satisfy (notInClass "\n\r")))- unescapedLine :: Parser ByteString+ unescapedLine :: Parser B.ByteString unescapedLine = maybe B.empty B.pack <$> optional ((:) <$> satisfy (notInClass "-\n\r") <*> many (satisfy (notInClass "\n\r")))
Codec/Encryption/OpenPGP/ASCIIArmor/Encode.hs view
@@ -1,5 +1,5 @@ -- ASCIIArmor/Encode.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012-2018 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file). @@ -12,13 +12,10 @@ import Data.ByteString.Lazy (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as BL-import qualified Data.ByteString.Char8 as BC8 import qualified Data.ByteString.Lazy.Char8 as BLC8 import qualified Data.ByteString.Base64 as Base64 import Data.Digest.CRC24 (crc24Lazy)-import Data.Serialize (put)-import Data.Serialize.Put (runPutLazy, putWord32be)-import Data.String (IsString, fromString)+import Data.Binary.Put (runPut, putWord32be) encode :: [Armor] -> B.ByteString encode = B.concat . BL.toChunks . encodeLazy@@ -40,15 +37,15 @@ endLine atype = BLC8.pack "-----END PGP " `BL.append` aType atype `BL.append` BLC8.pack "-----\n" aType :: ArmorType -> ByteString-aType (ArmorMessage) = BLC8.pack "MESSAGE"-aType (ArmorPublicKeyBlock) = BLC8.pack "PUBLIC KEY BLOCK"-aType (ArmorPrivateKeyBlock) = BLC8.pack "PRIVATE KEY BLOCK"+aType ArmorMessage = BLC8.pack "MESSAGE"+aType ArmorPublicKeyBlock = BLC8.pack "PUBLIC KEY BLOCK"+aType ArmorPrivateKeyBlock = BLC8.pack "PRIVATE KEY BLOCK" aType (ArmorSplitMessage x y) = BLC8.pack $ "MESSAGE, PART " ++ show x ++ "/" ++ show y aType (ArmorSplitMessageIndefinite x) = BLC8.pack $ "MESSAGE, PART " ++ show x-aType (ArmorSignature) = BLC8.pack "SIGNATURE"+aType ArmorSignature = BLC8.pack "SIGNATURE" armorHeaders :: [(String, String)] -> ByteString-armorHeaders ahs = BLC8.unlines . map armorHeader $ ahs+armorHeaders = BLC8.unlines . map armorHeader where armorHeader :: (String, String) -> ByteString armorHeader (k, v) = BLC8.pack k `BL.append` BLC8.pack ": " `BL.append` BLC8.pack v@@ -63,7 +60,7 @@ | otherwise = BL.take (fromIntegral lw) bs : wordWrap lw (BL.drop (fromIntegral lw) bs) armorChecksum :: ByteString -> ByteString-armorChecksum = BLC8.cons '=' . armorData . BL.tail . runPutLazy . putWord32be . crc24Lazy+armorChecksum = BLC8.cons '=' . armorData . BL.tail . runPut . putWord32be . crc24Lazy dashEscape :: ByteString -> ByteString dashEscape = BLC8.unlines . map escapeLine . BLC8.lines
Codec/Encryption/OpenPGP/ASCIIArmor/Multipart.hs view
@@ -1,5 +1,5 @@ -- ASCIIArmor/Multipart.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file). @@ -13,11 +13,12 @@ import qualified Data.ByteString.Lazy as BL multipartMerge :: [Armor] -> Armor-multipartMerge as = go as (Armor ArmorMessage [] BL.empty)+multipartMerge as' = go as' (Armor ArmorMessage [] BL.empty) where go :: [Armor] -> Armor -> Armor go [] state = state go (Armor at hs bs:as) state = go as (go' at hs bs state)+ go _ _ = error "This shouldn't happen." go' :: ArmorType -> [(String,String)] -> ByteString -> Armor -> Armor go' (ArmorSplitMessage _ _) hs bs (Armor _ ohs obs) = Armor ArmorMessage (ohs ++ hs) (obs `BL.append` bs) go' (ArmorSplitMessageIndefinite _) hs bs (Armor _ ohs obs) = Armor ArmorMessage (ohs ++ hs) (obs `BL.append` bs)
Codec/Encryption/OpenPGP/ASCIIArmor/Types.hs view
@@ -1,5 +1,5 @@ -- ASCIIArmor/Decode.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file).
Codec/Encryption/OpenPGP/ASCIIArmor/Utils.hs view
@@ -1,5 +1,5 @@ -- ASCIIArmor/Utils.hs: OpenPGP (RFC4880) ASCII armor implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file).
Data/Digest/CRC24.hs view
@@ -1,5 +1,5 @@ -- CRC24.hs: OpenPGP (RFC4880) CRC24 implementation--- Copyright Ⓒ 2012 Clint Adams+-- Copyright © 2012 Clint Adams -- This software is released under the terms of the ISC license. -- (See the LICENSE file).
LICENSE view
@@ -1,4 +1,4 @@-Copyright Ⓒ 2012 Clint Adams <clint@debian.org>+Copyright © 2012 Clint Adams <clint@debian.org> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided
+ bench/mark.hs view
@@ -0,0 +1,16 @@+-- mark.hs: openpgp-asciiarmor benchmark suite+-- Copyright © 2018 Clint Adams+-- This software is released under the terms of the ISC license.+-- (See the LICENSE file).++{-# LANGUAGE FlexibleContexts, OverloadedStrings #-}++import Criterion.Main++import Data.Digest.CRC24++main :: IO ()+main = defaultMain [+ bgroup "crc" [ bench "crc24" $ whnf crc24 "test"+ ]+ ]
openpgp-asciiarmor.cabal view
@@ -1,13 +1,13 @@ Name: openpgp-asciiarmor-Version: 0.1+Version: 0.1.1 Synopsis: OpenPGP (RFC4880) ASCII Armor codec Description: OpenPGP (RFC4880) ASCII Armor codec Homepage: http://floss.scru.org/openpgp-asciiarmor-License: OtherLicense+License: ISC License-file: LICENSE Author: Clint Adams Maintainer: Clint Adams <clint@debian.org>-Copyright: 2012, Clint Adams+Copyright: 2012-2018 Clint Adams Category: Codec, Data Build-type: Simple Extra-source-files: tests/suite.hs@@ -36,27 +36,43 @@ Other-Modules: Data.Digest.CRC24 , Codec.Encryption.OpenPGP.ASCIIArmor.Multipart , Codec.Encryption.OpenPGP.ASCIIArmor.Utils+ Ghc-options: -Wall Build-depends: attoparsec , base > 4 && < 5 , base64-bytestring+ , binary , bytestring- , cereal- default-language: Haskell98+ default-language: Haskell2010 Test-Suite tests type: exitcode-stdio-1.0 main-is: tests/suite.hs+ Ghc-options: -Wall Build-depends: attoparsec , base > 4 && < 5 , base64-bytestring+ , binary , bytestring- , cereal- , HUnit- , test-framework- , test-framework-hunit- default-language: Haskell98+ , tasty+ , tasty-hunit+ default-language: Haskell2010 +Benchmark benchmark+ type: exitcode-stdio-1.0+ main-is: bench/mark.hs+ Ghc-options: -Wall+ Build-depends: openpgp-asciiarmor+ , base+ , bytestring+ , criterion+ default-language: Haskell2010+ source-repository head type: git- location: git://git.debian.org/users/clint/openpgp-asciiarmor.git+ location: https://salsa.debian.org/clint/openpgp-asciiarmor.git++source-repository this+ type: git+ location: https://salsa.debian.org/clint/openpgp-asciiarmor.git+ tag: openpgp-asciiarmor/v0.1.1
tests/suite.hs view
@@ -1,7 +1,5 @@-import Test.Framework (defaultMain, testGroup)-import Test.Framework.Providers.HUnit--import Test.HUnit+import Test.Tasty (defaultMain, testGroup, TestTree)+import Test.Tasty.HUnit (Assertion, assertEqual, assertFailure, testCase) import Codec.Encryption.OpenPGP.ASCIIArmor (decode, decodeLazy, encode, encodeLazy, multipartMerge) import Codec.Encryption.OpenPGP.ASCIIArmor.Types@@ -27,6 +25,7 @@ Right as -> assertEqual ("for " ++ fp) tbss (map getPayload as) where getPayload (Armor _ _ pl) = pl+ getPayload _ = error "This should not happen." testArmorMultipartDecode :: FilePath -> FilePath -> Assertion testArmorMultipartDecode fp target = do@@ -37,6 +36,7 @@ Right as -> assertEqual ("for " ++ fp) tbs (getPayload (multipartMerge as)) where getPayload (Armor _ _ pl) = pl+ getPayload _ = error "This should not happen." testClearsignedDecodeBody :: FilePath -> FilePath -> Assertion testClearsignedDecodeBody fp target = do@@ -45,8 +45,10 @@ case decodeLazy bs of Left e -> assertFailure $ "Decode failed (" ++ e ++ ") on " ++ fp Right [a] -> assertEqual ("for " ++ fp) (convertEndings tbs) (getBody a)+ _ -> assertFailure "This shouldn't happen." where getBody (ClearSigned _ txt _) = txt+ getBody _ = error "This should not happen." convertEndings = crlfUnlinesLazy . BLC8.lines testClearsignedDecodeSig :: FilePath -> FilePath -> Assertion@@ -56,34 +58,37 @@ case decodeLazy bs of Left e -> assertFailure $ "Decode failed (" ++ e ++ ") on " ++ fp Right [a] -> assertEqual ("for " ++ fp) tbs (getSig a)+ _ -> assertFailure "This shouldn't happen." where getSig (ClearSigned _ _ (Armor _ _ sig)) = sig+ getSig _ = error "This should not happen." testArmorEncode :: [FilePath] -> FilePath -> Assertion testArmorEncode fps target = do bss <- mapM (\fp -> BL.readFile $ "tests/data/" ++ fp) fps tbs <- BL.readFile $ "tests/data/" ++ target- assertEqual ("literaldata") tbs (encodeLazy (map (\bs -> Armor ArmorMessage [("Version","OpenPrivacy 0.99")] bs) bss))+ assertEqual "literaldata" tbs (encodeLazy (map (Armor ArmorMessage [("Version","OpenPrivacy 0.99")]) bss)) testClearsignedEncode :: FilePath -> FilePath -> FilePath -> Assertion testClearsignedEncode ftxt fsig ftarget = do txt <- BL.readFile $ "tests/data/" ++ ftxt sig <- BL.readFile $ "tests/data/" ++ fsig target <- BL.readFile $ "tests/data/" ++ ftarget- assertEqual ("clearsigned encode") target (encodeLazy [ClearSigned [("Hash","SHA1")] txt (Armor ArmorSignature [("Version","OpenPrivacy 0.99")] sig)])+ assertEqual "clearsigned encode" target (encodeLazy [ClearSigned [("Hash","SHA1")] txt (Armor ArmorSignature [("Version","OpenPrivacy 0.99")] sig)]) testStrictDecode :: FilePath -> Assertion testStrictDecode fp = do bs <- BL.readFile $ "tests/data/" ++ fp- assertEqual ("strict decode") (decodeLazy bs :: Either String [Armor]) (decode (B.concat . BL.toChunks $ bs) :: Either String [Armor])+ assertEqual "strict decode" (decodeLazy bs :: Either String [Armor]) (decode (B.concat . BL.toChunks $ bs) :: Either String [Armor]) testStrictEncode :: FilePath -> Assertion testStrictEncode fp = do bs <- BL.readFile $ "tests/data/" ++ fp let fakearmors = [Armor ArmorMessage [("Version","OpenPrivacy 0.99")] bs, ClearSigned [("Hash","SHA1")] bs (Armor ArmorSignature [("Version","OpenPrivacy 0.99")] bs)]- assertEqual ("strict encode") (encodeLazy fakearmors) (BL.fromChunks [(encode fakearmors)])+ assertEqual "strict encode" (encodeLazy fakearmors) (BL.fromChunks [encode fakearmors]) -tests = [+tests :: TestTree+tests = testGroup "openpgp-asciiarmor" [ testGroup "CRC24" [ testCase "CRC24: A" (testCRC24 (BC8.pack "A") 16680698) , testCase "CRC24: Haskell" (testCRC24 (BC8.pack "Haskell") 15612750)@@ -105,4 +110,5 @@ ] ] +main :: IO () main = defaultMain tests