diff options
author | Stephen Paul Weber <singpolyma@singpolyma.net> | 2012-12-29 14:52:53 -0500 |
---|---|---|
committer | Stephen Paul Weber <singpolyma@singpolyma.net> | 2012-12-29 14:56:23 -0500 |
commit | e8e14f2cc9023794dfd2cf77943650ce28e2b36c (patch) | |
tree | ba28453ec08264d06de8c77ad03e5828701f7973 | |
parent | 133b04ccbf83bab6406898b3906c0851d740fa67 (diff) |
Support for better error handling.
Requires binary 0.6.4.0
-rw-r--r-- | Data/OpenPGP.hs | 70 | ||||
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | openpgp.cabal | 4 |
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 | |||
67 | import Control.Monad | 67 | import Control.Monad |
68 | import Control.Arrow | 68 | import Control.Arrow |
69 | import Control.Applicative | 69 | import Control.Applicative |
70 | import Control.Exception (assert) | ||
71 | import Data.Bits | 70 | import Data.Bits |
72 | import Data.Word | 71 | import Data.Word |
73 | import Data.Char | 72 | import Data.Char |
@@ -104,8 +103,10 @@ getSomeByteString = getByteString . fromIntegral | |||
104 | putSomeByteString :: B.ByteString -> Put | 103 | putSomeByteString :: B.ByteString -> Put |
105 | putSomeByteString = putByteString | 104 | putSomeByteString = putByteString |
106 | 105 | ||
107 | unsafeRunGet :: Get a -> B.ByteString -> a | 106 | localGet :: Get a -> B.ByteString -> Get a |
108 | unsafeRunGet g bs = let Right v = runGet g bs in v | 107 | localGet g bs = case runGet g bs of |
108 | Left s -> fail s | ||
109 | Right v -> return v | ||
109 | 110 | ||
110 | compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString | 111 | compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString |
111 | compress algo = toStrictBS . lazyCompress algo . toLazyBS | 112 | compress algo = toStrictBS . lazyCompress algo . toLazyBS |
@@ -128,8 +129,12 @@ getSomeByteString = getLazyByteString . fromIntegral | |||
128 | putSomeByteString :: B.ByteString -> Put | 129 | putSomeByteString :: B.ByteString -> Put |
129 | putSomeByteString = putLazyByteString | 130 | putSomeByteString = putLazyByteString |
130 | 131 | ||
131 | unsafeRunGet :: Get a -> B.ByteString -> a | 132 | localGet :: Get a -> B.ByteString -> Get a |
132 | unsafeRunGet = runGet | 133 | localGet 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 | ||
134 | compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString | 139 | compress :: CompressionAlgorithm -> B.ByteString -> B.ByteString |
135 | compress = lazyCompress | 140 | compress = lazyCompress |
@@ -152,8 +157,10 @@ lazyDecompress ZLIB = Zlib.decompress | |||
152 | lazyDecompress BZip2 = BZip2.decompress | 157 | lazyDecompress BZip2 = BZip2.decompress |
153 | lazyDecompress x = error ("No implementation for " ++ show x) | 158 | lazyDecompress x = error ("No implementation for " ++ show x) |
154 | 159 | ||
155 | assertProp :: (a -> Bool) -> a -> a | 160 | assertProp :: (Monad m, Show a) => (a -> Bool) -> a -> m a |
156 | assertProp f x = assert (f x) x | 161 | assertProp f x |
162 | | f x = return $! x | ||
163 | | otherwise = fail $ "Assertion failed for: " ++ show x | ||
157 | 164 | ||
158 | pad :: Int -> String -> String | 165 | pad :: Int -> String -> String |
159 | pad l s = replicate (l - length s) '0' ++ s | 166 | pad 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 | ||
253 | get_packet_bytes :: Get (Word8, [B.ByteString]) | 260 | get_packet_bytes :: Get (Word8, [B.ByteString]) |
254 | get_packet_bytes = do | 261 | get_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 |
286 | parse_old_length :: Word8 -> Get Word32 | 293 | parse_old_length :: Word8 -> Get (Maybe Word32) |
287 | parse_old_length tag = | 294 | parse_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 | |||
495 | parse_packet :: Word8 -> Get Packet | 502 | parse_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 |
497 | parse_packet 1 = AsymmetricSessionKeyPacket | 504 | parse_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 |
640 | parse_packet 8 = do | 647 | parse_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 |
648 | parse_packet 9 = EncryptedDataPacket 0 <$> getRemainingByteString | 655 | parse_packet 9 = EncryptedDataPacket 0 <$> getRemainingByteString |
@@ -841,9 +848,11 @@ signatures_and_data (Message lst) = | |||
841 | 848 | ||
842 | newtype MPI = MPI Integer deriving (Show, Read, Eq, Ord) | 849 | newtype MPI = MPI Integer deriving (Show, Read, Eq, Ord) |
843 | instance BINARY_CLASS MPI where | 850 | instance 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) |
1017 | put_signature_subpacket (SignatureTargetPacket kalgo halgo hash) = | 1026 | put_signature_subpacket (SignatureTargetPacket kalgo halgo hash) = |
1018 | (B.concat [encode kalgo, encode halgo, hash], 31) | 1027 | (B.concat [encode kalgo, encode halgo, hash], 31) |
1019 | put_signature_subpacket (EmbeddedSignaturePacket packet) = | 1028 | put_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 | ||
1021 | put_signature_subpacket (UnsupportedSignatureSubpacket tag bytes) = | 1031 | put_signature_subpacket (UnsupportedSignatureSubpacket tag bytes) = |
1022 | (bytes, tag) | 1032 | (bytes, tag) |
1023 | 1033 | ||
@@ -48,15 +48,15 @@ endif | |||
48 | 48 | ||
49 | ifdef CEREAL | 49 | ifdef CEREAL |
50 | dist/setup-config: openpgp.cabal | 50 | dist/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 |
53 | else | 53 | else |
54 | dist/setup-config: openpgp.cabal | 54 | dist/setup-config: openpgp.cabal |
55 | cabal configure --enable-tests | 55 | cabal configure --enable-tests |
56 | endif | 56 | endif |
57 | 57 | ||
58 | clean: | 58 | clean: |
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, |