packages feed

asn1-encoding 0.9.5 → 0.9.6

raw patch · 4 files changed

+124/−12 lines, 4 filesdep −textdep ~basePVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies removed: text

Dependency ranges changed: base

API changes (from Hackage documentation)

- Data.ASN1.Error: instance GHC.Exception.Exception Data.ASN1.Error.ASN1Error
+ Data.ASN1.Error: instance GHC.Exception.Type.Exception Data.ASN1.Error.ASN1Error
+ Data.ASN1.Prim: ASN1String :: ASN1CharacterString -> ASN1
+ Data.ASN1.Prim: ASN1Time :: ASN1TimeType -> DateTime -> Maybe TimezoneOffset -> ASN1
+ Data.ASN1.Prim: BitString :: BitArray -> ASN1
+ Data.ASN1.Prim: Boolean :: Bool -> ASN1
+ Data.ASN1.Prim: Container :: ASN1Class -> ASN1Tag -> ASN1ConstructionType
+ Data.ASN1.Prim: End :: ASN1ConstructionType -> ASN1
+ Data.ASN1.Prim: Enumerated :: Integer -> ASN1
+ Data.ASN1.Prim: IntVal :: Integer -> ASN1
+ Data.ASN1.Prim: Null :: ASN1
+ Data.ASN1.Prim: OID :: OID -> ASN1
+ Data.ASN1.Prim: OctetString :: ByteString -> ASN1
+ Data.ASN1.Prim: Other :: ASN1Class -> ASN1Tag -> ByteString -> ASN1
+ Data.ASN1.Prim: Real :: Double -> ASN1
+ Data.ASN1.Prim: Sequence :: ASN1ConstructionType
+ Data.ASN1.Prim: Set :: ASN1ConstructionType
+ Data.ASN1.Prim: Start :: ASN1ConstructionType -> ASN1
+ Data.ASN1.Prim: data ASN1
+ Data.ASN1.Prim: data ASN1ConstructionType
+ Data.ASN1.Prim: decodePrimitive :: ASN1Header -> ByteString -> ASN1Ret
+ Data.ASN1.Prim: encodeConstructed :: ASN1 -> [ASN1] -> (Int, [ASN1Event])
+ Data.ASN1.Prim: encodeHeader :: Bool -> ASN1Length -> ASN1 -> ASN1Header
+ Data.ASN1.Prim: encodeList :: [ASN1] -> (Int, [ASN1Event])
+ Data.ASN1.Prim: encodeOne :: ASN1 -> (Int, [ASN1Event])
+ Data.ASN1.Prim: encodePrimitive :: ASN1 -> (Int, [ASN1Event])
+ Data.ASN1.Prim: encodePrimitiveHeader :: ASN1Length -> ASN1 -> ASN1Header
+ Data.ASN1.Prim: getBitString :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getBoolean :: Bool -> ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getDouble :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getInteger :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getNull :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getOID :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getOctetString :: ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: getTime :: ASN1TimeType -> ByteString -> Either ASN1Error ASN1
+ Data.ASN1.Prim: mkSmallestLength :: Int -> ASN1Length
+ Data.ASN1.Prim: putBitString :: BitArray -> ByteString
+ Data.ASN1.Prim: putDouble :: Double -> ByteString
+ Data.ASN1.Prim: putInteger :: Integer -> ByteString
+ Data.ASN1.Prim: putOID :: [Integer] -> ByteString
+ Data.ASN1.Prim: putString :: ByteString -> ByteString
+ Data.ASN1.Prim: putTime :: ASN1TimeType -> DateTime -> Maybe TimezoneOffset -> ByteString
- Data.ASN1.BinaryEncoding.Raw: ASN1Header :: ~ASN1Class -> ~ASN1Tag -> ~Bool -> ~ASN1Length -> ASN1Header
+ Data.ASN1.BinaryEncoding.Raw: ASN1Header :: !ASN1Class -> !ASN1Tag -> !Bool -> !ASN1Length -> ASN1Header
- Data.ASN1.BinaryEncoding.Raw: Primitive :: ~ByteString -> ASN1Event
+ Data.ASN1.BinaryEncoding.Raw: Primitive :: !ByteString -> ASN1Event
- Data.ASN1.BinaryEncoding.Raw: data ASN1Class :: *
+ Data.ASN1.BinaryEncoding.Raw: data ASN1Class
- Data.ASN1.BinaryEncoding.Raw: data ASN1Event :: *
+ Data.ASN1.BinaryEncoding.Raw: data ASN1Event
- Data.ASN1.BinaryEncoding.Raw: data ASN1Header :: *
+ Data.ASN1.BinaryEncoding.Raw: data ASN1Header
- Data.ASN1.BinaryEncoding.Raw: data ASN1Length :: *
+ Data.ASN1.BinaryEncoding.Raw: data ASN1Length

Files

Data/ASN1/Get.hs view
@@ -16,6 +16,7 @@ -- case for asn1 and augmented by a position. -- {-# LANGUAGE Rank2Types #-}+{-# LANGUAGE CPP #-} module Data.ASN1.Get     ( Result(..)     , Input@@ -103,7 +104,10 @@         let ks' s1 b1 m1 p1 a = unGet (g a) s1 b1 m1 p1 kf ks          in unGet m s0 b0 m0 p0 kf ks' -    fail     = failDesc+#if MIN_VERSION_base(4,13,0)+instance MonadFail Get where+#endif+    fail = failDesc  instance MonadPlus Get where     mzero     = failDesc "mzero"
Data/ASN1/Prim.hs view
@@ -8,6 +8,7 @@ -- Tools to read ASN1 primitive (e.g. boolean, int) -- +{-# LANGUAGE CPP #-} {-# LANGUAGE ViewPatterns #-} module Data.ASN1.Prim     (@@ -27,6 +28,7 @@     -- * marshall an ASN1 type from a val struct or a bytestring     , getBoolean     , getInteger+    , getDouble     , getBitString     , getOctetString     , getNull@@ -36,6 +38,7 @@     -- * marshall an ASN1 type to a bytestring     , putTime     , putInteger+    , putDouble     , putBitString     , putString     , putOID@@ -49,15 +52,18 @@ import Data.ASN1.Error import Data.ASN1.Serialize import Data.Bits+import Data.Monoid import Data.Word import Data.List (unfoldr) import Data.ByteString (ByteString) import Data.Char (ord, isDigit) import qualified Data.ByteString as B import qualified Data.ByteString.Char8 as BC+import qualified Data.ByteString.Unsafe as B import Data.Hourglass-import Control.Applicative import Control.Arrow (first)+import Control.Applicative+import Control.Monad  encodeHeader :: Bool -> ASN1Length -> ASN1 -> ASN1Header encodeHeader pc len (Boolean _)                = ASN1Header Universal 0x1 pc len@@ -99,7 +105,7 @@ encodePrimitiveData (OctetString b)     = putString b encodePrimitiveData Null                = B.empty encodePrimitiveData (OID oidv)          = putOID oidv-encodePrimitiveData (Real _)            = B.empty -- not implemented+encodePrimitiveData (Real d)            = putDouble d encodePrimitiveData (Enumerated i)      = putInteger $ fromIntegral i encodePrimitiveData (ASN1String cs)     = getCharacterStringRawData cs encodePrimitiveData (ASN1Time ty ti tz) = putTime ty ti tz@@ -164,7 +170,7 @@ decodePrimitive (ASN1Header Universal 0x6 _ _) p   = getOID p decodePrimitive (ASN1Header Universal 0x7 _ _) _   = Left $ TypeNotImplemented "Object Descriptor" decodePrimitive (ASN1Header Universal 0x8 _ _) _   = Left $ TypeNotImplemented "External"-decodePrimitive (ASN1Header Universal 0x9 _ _) _   = Left $ TypeNotImplemented "real"+decodePrimitive (ASN1Header Universal 0x9 _ _) p   = getDouble p decodePrimitive (ASN1Header Universal 0xa _ _) p   = getEnumerated p decodePrimitive (ASN1Header Universal 0xb _ _) _   = Left $ TypeNotImplemented "EMBEDDED PDV" decodePrimitive (ASN1Header Universal 0xc _ _) p   = getCharacterString UTF8 p@@ -219,6 +225,50 @@             v1 = s `B.index` 0             v2 = s `B.index` 1 +getDouble :: ByteString -> Either ASN1Error ASN1+getDouble s = Real <$> getDoubleRaw s++getDoubleRaw :: ByteString -> Either ASN1Error Double+getDoubleRaw s+  | B.null s  = Right 0+getDoubleRaw s@(B.unsafeHead -> h)+  | h == 0x40 = Right $! (1/0)  -- Infinity+  | h == 0x41 = Right $! (-1/0) -- -Infinity+  | h == 0x42 = Right $! (0/0)  -- NaN+  | otherwise = do+      let len = B.length s+      base <- case (h `testBit` 5, h `testBit` 4) of+                -- extract bits 5,4 for the base+                (False, False) -> return 2+                (False, True)  -> return 8+                (True,  False) -> return 16+                _              -> Left . TypeDecodingFailed $ "real: invalid base detected"+      -- check bit 6 for the sign+      let mkSigned = if h `testBit` 6 then negate else id+      -- extract bits 3,2 for the scaling factor+      let scaleFactor = (h .&. 0x0c) `shiftR` 2+      expLength <- getExponentLength len h s+      -- 1 byte for the header, expLength for the exponent, and at least 1 byte for the mantissa+      unless (len > 1 + fromIntegral expLength) $+        Left . TypeDecodingFailed $ "real: not enough input for exponent and mantissa"+      let (_, exp'') = intOfBytes $ B.unsafeTake (fromIntegral expLength) $ B.unsafeDrop 1 s+      let exp' = case base :: Int of+                   2 -> exp''+                   8 -> 3 * exp''+                   _ -> 4 * exp'' -- must be 16+          exponent = exp' - fromIntegral scaleFactor+          -- whatever is leftover is the mantissa, unsigned+          (_, mantissa) = uintOfBytes $ B.unsafeDrop (1 + fromIntegral expLength) s+      Right $! encodeFloat (mkSigned $ toInteger mantissa) (fromIntegral exponent)++getExponentLength :: Int -> Word8 -> ByteString -> Either ASN1Error Word8+getExponentLength len h s =+  case h .&. 0x03 of+    l | l == 0x03 -> do+          unless (len > 1) $ Left . TypeDecodingFailed $ "real: not enough input to decode exponent length"+          return $ B.unsafeIndex s 1+      | otherwise -> return $ l + 1+ getBitString :: ByteString -> Either ASN1Error ASN1 getBitString s =     let toSkip = B.head s in@@ -362,3 +412,46 @@   where         encode x | x == 0    = B.singleton 0                  | otherwise = putVarEncodingIntegral x++putDouble :: Double -> ByteString+putDouble d+  | d == 0 = B.pack []+  | d == (1/0) = B.pack [0x40]+  | d == negate (1/0) = B.pack [0x41]+  | isNaN d = B.pack [0x42]+  | otherwise = B.cons (header .|. (expLen - 1)) -- encode length of exponent+                (expBS <> manBS)+  where+  (mkUnsigned, header)+    | d < 0     = (negate, bINARY_NEGATIVE_NUMBER_ID)+    | otherwise = (id, bINARY_POSITIVE_NUMBER_ID)+  (man, exp) = decodeFloat d+  (mantissa, exponent) = normalize (fromIntegral $ mkUnsigned man, exp)+  expBS = putInteger (fromIntegral exponent)+  expLen = fromIntegral (B.length expBS)+  manBS = putInteger (fromIntegral mantissa)++-- | Normalize the mantissa and adjust the exponent.+--+-- DER requires the mantissa to either be 0 or odd, so we right-shift it+-- until the LSB is 1, and then add the shift amount to the exponent.+--+-- TODO: handle denormal numbers+normalize :: (Word64, Int) -> (Word64, Int)+normalize (mantissa, exponent) = (mantissa `shiftR` sh, exponent + sh)+  where+    sh = countTrailingZeros mantissa++#if !(MIN_VERSION_base(4,8,0))+    countTrailingZeros :: FiniteBits b => b -> Int+    countTrailingZeros x = go 0+      where+        go i | i >= w      = i+             | testBit x i = i+             | otherwise   = go (i+1)+        w = finiteBitSize x+#endif++bINARY_POSITIVE_NUMBER_ID, bINARY_NEGATIVE_NUMBER_ID :: Word8+bINARY_POSITIVE_NUMBER_ID = 0x80+bINARY_NEGATIVE_NUMBER_ID = 0xc0
asn1-encoding.cabal view
@@ -1,5 +1,5 @@ Name:                asn1-encoding-Version:             0.9.5+Version:             0.9.6 Synopsis:            ASN1 data reader and writer in RAW, BER and DER forms Description:     ASN1 data reader and writer in raw form with supports for high level forms of ASN1 (BER, and DER).@@ -12,7 +12,7 @@ stability:           experimental Build-Type:          Simple Cabal-Version:       >=1.10-Homepage:            http://github.com/vincenthz/hs-asn1+Homepage:            https://github.com/vincenthz/hs-asn1  Library   Exposed-modules:   Data.ASN1.Error@@ -21,8 +21,8 @@                      Data.ASN1.Encoding                      Data.ASN1.Stream                      Data.ASN1.Object-  other-modules:     Data.ASN1.Prim-                     Data.ASN1.BinaryEncoding.Parse+                     Data.ASN1.Prim+  other-modules:     Data.ASN1.BinaryEncoding.Parse                      Data.ASN1.BinaryEncoding.Writer                      Data.ASN1.Internal                      Data.ASN1.Serialize@@ -40,7 +40,6 @@   Main-Is:           Tests.hs   Build-depends:     base >= 3 && < 7                    , bytestring-                   , text                    , mtl                    , tasty                    , tasty-quickcheck@@ -52,5 +51,5 @@  source-repository head   type:     git-  location: git://github.com/vincenthz/hs-asn1-  subdir:   asn1-encoding+  location: https://github.com/vincenthz/hs-asn1+  subdir:   encoding
tests/Tests.hs view
@@ -160,7 +160,7 @@                 , liftM OctetString arbitrary                 , return Null                 , liftM OID arbitraryOID-                --, Real Double+                , liftM Real arbitrary                 -- , return Enumerated                 , ASN1String <$> arbitrary                 , ASN1Time <$> arbitrary <*> arbitrary <*> arbitrary@@ -192,15 +192,31 @@ prop_event_marshalling_id :: ASN1Events -> Bool prop_event_marshalling_id (ASN1Events e) = (parseLBS $ toLazyByteString e) == Right e +prop_asn1_der_marshalling_id :: [ASN1] -> Bool prop_asn1_der_marshalling_id v = (decodeASN1 DER . encodeASN1 DER) v `assertEq` Right v     where assertEq got expected                  | got /= expected = error ("got: " ++ show got ++ " expected: " ++ show expected)                  | otherwise       = True +prop_real_der_marshalling_id :: Double -> Bool+prop_real_der_marshalling_id v = (decodeASN1 DER . encodeASN1 DER) [Real v] `assertEq` Right [Real v]+    where assertEq got expected+                 | got /= expected = error ("got: " ++ show got ++ " expected: " ++ show expected)+                 | otherwise       = True++prop_integral_real_der_marshalling_id :: Integer -> Bool+prop_integral_real_der_marshalling_id v = (decodeASN1 DER . encodeASN1 DER) [Real (fromInteger v)]+                                          `assertEq` Right [Real (fromInteger v)]+    where assertEq got expected+                 | got /= expected = error ("got: " ++ show got ++ " expected: " ++ show expected)+                 | otherwise       = True+ marshallingTests = testGroup "Marshalling"     [ testProperty "Header" prop_header_marshalling_id     , testProperty "Event"  prop_event_marshalling_id     , testProperty "DER"    prop_asn1_der_marshalling_id+    , testProperty "Real"   prop_real_der_marshalling_id+    , testProperty "Integral Real"   prop_integral_real_der_marshalling_id     ]  main = defaultMain $ testGroup "asn1-encoding" [marshallingTests]