summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Paul Weber <singpolyma@singpolyma.net>2012-12-29 14:52:53 -0500
committerStephen Paul Weber <singpolyma@singpolyma.net>2012-12-29 14:56:23 -0500
commite8e14f2cc9023794dfd2cf77943650ce28e2b36c (patch)
treeba28453ec08264d06de8c77ad03e5828701f7973
parent133b04ccbf83bab6406898b3906c0851d740fa67 (diff)
Support for better error handling.
Requires binary 0.6.4.0
-rw-r--r--Data/OpenPGP.hs70
-rw-r--r--Makefile6
-rw-r--r--openpgp.cabal4
3 files changed, 45 insertions, 35 deletions
diff --git a/Data/OpenPGP.hs b/Data/OpenPGP.hs
index 02b4a1a..a3b7b62 100644
--- a/Data/OpenPGP.hs
+++ b/Data/OpenPGP.hs
@@ -67,7 +67,6 @@ import Numeric
67import Control.Monad 67import Control.Monad
68import Control.Arrow 68import Control.Arrow
69import Control.Applicative 69import Control.Applicative
70import Control.Exception (assert)
71import Data.Bits 70import Data.Bits
72import Data.Word 71import Data.Word
73import Data.Char 72import Data.Char
@@ -104,8 +103,10 @@ getSomeByteString = getByteString . fromIntegral
104putSomeByteString :: B.ByteString -> Put 103putSomeByteString :: B.ByteString -> Put
105putSomeByteString = putByteString 104putSomeByteString = putByteString
106 105
107unsafeRunGet :: Get a -> B.ByteString -> a 106localGet :: Get a -> B.ByteString -> Get a
108unsafeRunGet g bs = let Right v = runGet g bs in v 107localGet g bs = case runGet g bs of
108 Left s -> fail s
109 Right v -> return v
109 110
110compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString 111compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString
111compress algo = toStrictBS . lazyCompress algo . toLazyBS 112compress algo = toStrictBS . lazyCompress algo . toLazyBS
@@ -128,8 +129,12 @@ getSomeByteString = getLazyByteString . fromIntegral
128putSomeByteString :: B.ByteString -> Put 129putSomeByteString :: B.ByteString -> Put
129putSomeByteString = putLazyByteString 130putSomeByteString = putLazyByteString
130 131
131unsafeRunGet :: Get a -> B.ByteString -> a 132localGet :: Get a -> B.ByteString -> Get a
132unsafeRunGet = runGet 133localGet g bs = case runGetOrFail g bs of
134 Left (_,_,s) -> fail s
135 Right (leftover,_,v)
136 | B.null leftover -> return v
137 | otherwise -> fail $ "Leftover in localGet: " ++ show leftover
133 138
134compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString 139compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString
135compress = lazyCompress 140compress = lazyCompress
@@ -152,8 +157,10 @@ lazyDecompress ZLIB = Zlib.decompress
152lazyDecompress BZip2 = BZip2.decompress 157lazyDecompress BZip2 = BZip2.decompress
153lazyDecompress x = error ("No implementation for " ++ show x) 158lazyDecompress x = error ("No implementation for " ++ show x)
154 159
155assertProp :: (a -> Bool) -> a -> a 160assertProp :: (Monad m, Show a) => (a -> Bool) -> a -> m a
156assertProp f x = assert (f x) x 161assertProp f x
162 | f x = return $! x
163 | otherwise = fail $ "Assertion failed for: " ++ show x
157 164
158pad :: Int -> String -> String 165pad :: Int -> String -> String
159pad l s = replicate (l - length s) '0' ++ s 166pad l s = replicate (l - length s) '0' ++ s
@@ -236,7 +243,7 @@ instance BINARY_CLASS Packet where
236 -- First two bits are 1 for new packet format 243 -- First two bits are 1 for new packet format
237 put ((tag .|. 0xC0) :: Word8) 244 put ((tag .|. 0xC0) :: Word8)
238 case tag of 245 case tag of
239 19 -> put (assertProp (<192) blen :: Word8) 246 19 -> put =<< assertProp (<192) (blen :: Word8)
240 _ -> do 247 _ -> do
241 -- Use 5-octet lengths 248 -- Use 5-octet lengths
242 put (255 :: Word8) 249 put (255 :: Word8)
@@ -248,19 +255,19 @@ instance BINARY_CLASS Packet where
248 (body, tag) = put_packet p 255 (body, tag) = put_packet p
249 get = do 256 get = do
250 (t, packet) <- get_packet_bytes 257 (t, packet) <- get_packet_bytes
251 return $ unsafeRunGet (parse_packet t) (B.concat packet) 258 localGet (parse_packet t) (B.concat packet)
252 259
253get_packet_bytes :: Get (Word8, [B.ByteString]) 260get_packet_bytes :: Get (Word8, [B.ByteString])
254get_packet_bytes = do 261get_packet_bytes = do
255 tag <- get 262 tag <- get
256 let (t, l) = 263 let (t, l) =
257 if (tag .&. 64) /= 0 then 264 if (tag .&. 64) /= 0 then
258 (tag .&. 63, parse_new_length) 265 (tag .&. 63, fmap (first Just) parse_new_length)
259 else 266 else
260 ((tag `shiftR` 2) .&. 15, (,) <$> parse_old_length tag <*> pure False) 267 ((tag `shiftR` 2) .&. 15, (,) <$> parse_old_length tag <*> pure False)
261 (len, partial) <- l 268 (len, partial) <- l
262 -- This forces the whole packet to be consumed 269 -- This forces the whole packet to be consumed
263 packet <- getSomeByteString (fromIntegral len) 270 packet <- maybe getRemainingByteString (getSomeByteString . fromIntegral) len
264 if not partial then return (t, [packet]) else 271 if not partial then return (t, [packet]) else
265 (,) t <$> ((packet:) . snd) <$> get_packet_bytes 272 (,) t <$> ((packet:) . snd) <$> get_packet_bytes
266 273
@@ -283,17 +290,17 @@ parse_new_length = do
283 _ -> fail "Unsupported new packet length." 290 _ -> fail "Unsupported new packet length."
284 291
285-- http://tools.ietf.org/html/rfc4880#section-4.2.1 292-- http://tools.ietf.org/html/rfc4880#section-4.2.1
286parse_old_length :: Word8 -> Get Word32 293parse_old_length :: Word8 -> Get (Maybe Word32)
287parse_old_length tag = 294parse_old_length tag =
288 case tag .&. 3 of 295 case tag .&. 3 of
289 -- One octet length 296 -- One octet length
290 0 -> fmap fromIntegral (get :: Get Word8) 297 0 -> fmap (Just . fromIntegral) (get :: Get Word8)
291 -- Two octet length 298 -- Two octet length
292 1 -> fmap fromIntegral (get :: Get Word16) 299 1 -> fmap (Just . fromIntegral) (get :: Get Word16)
293 -- Four octet length 300 -- Four octet length
294 2 -> get 301 2 -> fmap Just get
295 -- Indeterminate length 302 -- Indeterminate length
296 3 -> fmap fromIntegral remaining 303 3 -> return Nothing
297 -- Error 304 -- Error
298 _ -> fail "Unsupported old packet length." 305 _ -> fail "Unsupported old packet length."
299 306
@@ -495,7 +502,7 @@ put_packet x = error ("Unsupported Packet version or type in put_packet: " ++ sh
495parse_packet :: Word8 -> Get Packet 502parse_packet :: Word8 -> Get Packet
496-- AsymmetricSessionKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.1 503-- AsymmetricSessionKeyPacket, http://tools.ietf.org/html/rfc4880#section-5.1
497parse_packet 1 = AsymmetricSessionKeyPacket 504parse_packet 1 = AsymmetricSessionKeyPacket
498 <$> fmap (assertProp (==3)) get 505 <$> (assertProp (==3) =<< get)
499 <*> fmap (pad 16 . map toUpper . flip showHex "") (get :: Get Word64) 506 <*> fmap (pad 16 . map toUpper . flip showHex "") (get :: Get Word64)
500 <*> get 507 <*> get
501 <*> getRemainingByteString 508 <*> getRemainingByteString
@@ -504,7 +511,7 @@ parse_packet 2 = do
504 version <- get 511 version <- get
505 case version of 512 case version of
506 _ | version `elem` [2,3] -> do 513 _ | version `elem` [2,3] -> do
507 _ <- fmap (assertProp (==5)) (get :: Get Word8) 514 _ <- assertProp (==5) =<< (get :: Get Word8)
508 signature_type <- get 515 signature_type <- get
509 creation_time <- get :: Get Word32 516 creation_time <- get :: Get Word32
510 keyid <- get :: Get Word64 517 keyid <- get :: Get Word64
@@ -532,10 +539,10 @@ parse_packet 2 = do
532 hash_algorithm <- get 539 hash_algorithm <- get
533 hashed_size <- fmap fromIntegral (get :: Get Word16) 540 hashed_size <- fmap fromIntegral (get :: Get Word16)
534 hashed_data <- getSomeByteString hashed_size 541 hashed_data <- getSomeByteString hashed_size
535 let hashed = unsafeRunGet listUntilEnd hashed_data 542 hashed <- localGet listUntilEnd hashed_data
536 unhashed_size <- fmap fromIntegral (get :: Get Word16) 543 unhashed_size <- fmap fromIntegral (get :: Get Word16)
537 unhashed_data <- getSomeByteString unhashed_size 544 unhashed_data <- getSomeByteString unhashed_size
538 let unhashed = unsafeRunGet listUntilEnd unhashed_data 545 unhashed <- localGet listUntilEnd unhashed_data
539 hash_head <- get 546 hash_head <- get
540 signature <- listUntilEnd 547 signature <- listUntilEnd
541 return SignaturePacket { 548 return SignaturePacket {
@@ -639,10 +646,10 @@ parse_packet 7 = do
639-- CompressedDataPacket, http://tools.ietf.org/html/rfc4880#section-5.6 646-- CompressedDataPacket, http://tools.ietf.org/html/rfc4880#section-5.6
640parse_packet 8 = do 647parse_packet 8 = do
641 algorithm <- get 648 algorithm <- get
642 message <- getRemainingByteString 649 message <- localGet get =<< (decompress algorithm <$> getRemainingByteString)
643 return CompressedDataPacket { 650 return CompressedDataPacket {
644 compression_algorithm = algorithm, 651 compression_algorithm = algorithm,
645 message = unsafeRunGet get (decompress algorithm message) 652 message = message
646 } 653 }
647-- EncryptedDataPacket, http://tools.ietf.org/html/rfc4880#section-5.7 654-- EncryptedDataPacket, http://tools.ietf.org/html/rfc4880#section-5.7
648parse_packet 9 = EncryptedDataPacket 0 <$> getRemainingByteString 655parse_packet 9 = EncryptedDataPacket 0 <$> getRemainingByteString
@@ -841,9 +848,11 @@ signatures_and_data (Message lst) =
841 848
842newtype MPI = MPI Integer deriving (Show, Read, Eq, Ord) 849newtype MPI = MPI Integer deriving (Show, Read, Eq, Ord)
843instance BINARY_CLASS MPI where 850instance BINARY_CLASS MPI where
844 put (MPI i) = do 851 put (MPI i)
845 put (bitl :: Word16) 852 | i >= 0 = do
846 putSomeByteString bytes 853 put (bitl :: Word16)
854 putSomeByteString bytes
855 | otherwise = fail $ "MPI is less than 0: " ++ show i
847 where 856 where
848 (bytes, bitl) 857 (bytes, bitl)
849 | B.null bytes' = (B.singleton 0, 1) 858 | B.null bytes' = (B.singleton 0, 1)
@@ -855,10 +864,10 @@ instance BINARY_CLASS MPI where
855 bytes' = B.reverse $ B.unfoldr (\x -> 864 bytes' = B.reverse $ B.unfoldr (\x ->
856 if x == 0 then Nothing else 865 if x == 0 then Nothing else
857 Just (fromIntegral x, x `shiftR` 8) 866 Just (fromIntegral x, x `shiftR` 8)
858 ) (assertProp (>=0) i) 867 ) i
859 get = do 868 get = do
860 length <- fmap fromIntegral (get :: Get Word16) 869 length <- fmap fromIntegral (get :: Get Word16)
861 bytes <- getSomeByteString (assertProp (>0) $ (length + 7) `div` 8) 870 bytes <- getSomeByteString =<< assertProp (>0) ((length + 7) `div` 8)
862 return (MPI (B.foldl (\a b -> 871 return (MPI (B.foldl (\a b ->
863 a `shiftL` 8 .|. fromIntegral b) 0 bytes)) 872 a `shiftL` 8 .|. fromIntegral b) 0 bytes))
864 873
@@ -940,7 +949,7 @@ instance BINARY_CLASS SignatureSubpacket where
940 tag <- fmap stripCrit get :: Get Word8 949 tag <- fmap stripCrit get :: Get Word8
941 -- This forces the whole packet to be consumed 950 -- This forces the whole packet to be consumed
942 packet <- getSomeByteString (len-1) 951 packet <- getSomeByteString (len-1)
943 return $ unsafeRunGet (parse_signature_subpacket tag) packet 952 localGet (parse_signature_subpacket tag) packet
944 where 953 where
945 -- TODO: Decide how to actually encode the "is critical" data 954 -- TODO: Decide how to actually encode the "is critical" data
946 -- instead of just ignoring it 955 -- instead of just ignoring it
@@ -1016,8 +1025,9 @@ put_signature_subpacket (FeaturesPacket supports_mdc) =
1016 (B.singleton $ if supports_mdc then 0x01 else 0x00, 30) 1025 (B.singleton $ if supports_mdc then 0x01 else 0x00, 30)
1017put_signature_subpacket (SignatureTargetPacket kalgo halgo hash) = 1026put_signature_subpacket (SignatureTargetPacket kalgo halgo hash) =
1018 (B.concat [encode kalgo, encode halgo, hash], 31) 1027 (B.concat [encode kalgo, encode halgo, hash], 31)
1019put_signature_subpacket (EmbeddedSignaturePacket packet) = 1028put_signature_subpacket (EmbeddedSignaturePacket packet)
1020 (fst $ put_packet (assertProp isSignaturePacket packet), 32) 1029 | isSignaturePacket packet = (fst $ put_packet packet, 32)
1030 | otherwise = error $ "Tried to put non-SignaturePacket in EmbeddedSignaturePacket: " ++ show packet
1021put_signature_subpacket (UnsupportedSignatureSubpacket tag bytes) = 1031put_signature_subpacket (UnsupportedSignatureSubpacket tag bytes) =
1022 (bytes, tag) 1032 (bytes, tag)
1023 1033
diff --git a/Makefile b/Makefile
index 929d0c4..9cb9cf4 100644
--- a/Makefile
+++ b/Makefile
@@ -48,15 +48,15 @@ endif
48 48
49ifdef CEREAL 49ifdef CEREAL
50dist/setup-config: openpgp.cabal 50dist/setup-config: openpgp.cabal
51 -printf '1c\nname: openpgp-cereal\n.\n,s/binary,$$/cereal,/g\nw\nq\n' | ed openpgp.cabal 51 -printf '1c\nname: openpgp-cereal\n.\n,s/binary >= 0.6.4.0,$$/cereal,/g\nw\nq\n' | ed openpgp.cabal
52 cabal configure 52 cabal configure --enable-tests
53else 53else
54dist/setup-config: openpgp.cabal 54dist/setup-config: openpgp.cabal
55 cabal configure --enable-tests 55 cabal configure --enable-tests
56endif 56endif
57 57
58clean: 58clean:
59 -printf '1c\nname: openpgp\n.\n,s/cereal,$$/binary,/g\nw\nq\n' | ed openpgp.cabal 59 -printf '1c\nname: openpgp\n.\n,s/cereal,$$/binary >= 0.6.4.0,/g\nw\nq\n' | ed openpgp.cabal
60 find -name '*.o' -o -name '*.hi' | xargs $(RM) 60 find -name '*.o' -o -name '*.hi' | xargs $(RM)
61 $(RM) sign verify keygen tests/suite 61 $(RM) sign verify keygen tests/suite
62 $(RM) -r dist dist-ghc 62 $(RM) -r dist dist-ghc
diff --git a/openpgp.cabal b/openpgp.cabal
index 84a099a..c20dc2e 100644
--- a/openpgp.cabal
+++ b/openpgp.cabal
@@ -134,7 +134,7 @@ library
134 base == 4.*, 134 base == 4.*,
135 bytestring, 135 bytestring,
136 utf8-string, 136 utf8-string,
137 binary, 137 binary >= 0.6.4.0,
138 zlib, 138 zlib,
139 bzlib 139 bzlib
140 140
@@ -149,7 +149,7 @@ test-suite tests
149 base == 4.*, 149 base == 4.*,
150 bytestring, 150 bytestring,
151 utf8-string, 151 utf8-string,
152 binary, 152 binary >= 0.6.4.0,
153 zlib, 153 zlib,
154 bzlib, 154 bzlib,
155 HUnit, 155 HUnit,