module Data.ASN1.DER
( ASN1Class(..)
, ASN1(..)
, ASN1ConstructionType(..)
, enumReadRaw
, enumWriteRaw
, enumReadBytes
, enumWriteBytes
, iterateFile
, iterateByteString
, decodeASN1Stream
, encodeASN1Stream
, decodeASN1
, decodeASN1s
, encodeASN1
, encodeASN1s
) where
import Data.ASN1.Raw (ASN1Class(..), ASN1Length(..), ASN1Header(..), ASN1Event(..), ASN1Err(..))
import qualified Data.ASN1.Raw as Raw
import Data.ASN1.Prim
import Data.ASN1.Types (ofStream, toStream, ASN1t)
import qualified Data.ASN1.BER as BER
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as L
import Control.Monad.Identity
import Control.Exception
import Data.Enumerator (Iteratee, Enumeratee, ($$), (>>==))
import Data.Enumerator.IO
import qualified Data.Enumerator as E
checkLength :: ASN1Length -> Maybe ASN1Err
checkLength LenIndefinite = Just $ ASN1PolicyFailed "DER" "indefinite length not allowed"
checkLength (LenShort _) = Nothing
checkLength (LenLong n i)
| n == 1 && i < 0x80 = Just $ ASN1PolicyFailed "DER" "long length should be a short length"
| n == 1 && i >= 0x80 = Nothing
| otherwise = if i >= 2^((n1)*8) && i < 2^(n*8)
then Nothing
else Just $ ASN1PolicyFailed "DER" "long length is not shortest"
checkRawDER :: Monad m => Enumeratee Raw.ASN1Event Raw.ASN1Event m a
checkRawDER = E.checkDone $ \k -> k (E.Chunks []) >>== loop
where
loop = E.checkDone go
go k = E.head >>= \x -> case x of
Nothing -> k (E.Chunks []) >>== return
Just l -> case tyCheck l of
Nothing -> k (E.Chunks [l]) >>== loop
Just err -> E.throwError err
tyCheck (Header (ASN1Header _ _ _ len)) = checkLength len
tyCheck _ = Nothing
enumReadRaw :: Monad m => Enumeratee Raw.ASN1Event ASN1 m a
enumReadRaw = \f -> E.joinI (checkRawDER $$ BER.enumReadRaw f)
enumWriteRaw :: Monad m => Enumeratee ASN1 Raw.ASN1Event m a
enumWriteRaw = BER.enumWriteRaw
enumReadBytes :: Monad m => Enumeratee ByteString ASN1 m a
enumReadBytes = \f -> E.joinI (Raw.enumReadBytes $$ (BER.enumReadRaw f))
enumWriteBytes :: Monad m => Enumeratee ASN1 ByteString m a
enumWriteBytes = \f -> E.joinI (enumWriteRaw $$ (Raw.enumWriteBytes f))
iterateFile :: FilePath -> Iteratee ASN1 IO a -> IO (Either SomeException a)
iterateFile path p = E.run (enumFile path $$ E.joinI $ enumReadBytes $$ p)
iterateByteString :: Monad m => L.ByteString -> Iteratee ASN1 m a -> m (Either SomeException a)
iterateByteString bs p = E.run (E.enumList 1 (L.toChunks bs) $$ E.joinI $ enumReadBytes $$ p)
decodeASN1Stream :: L.ByteString -> Either ASN1Err [ASN1]
decodeASN1Stream l =
case runIdentity (iterateByteString l E.consume) of
Left err -> Left (maybe (ASN1ParsingFail "unknown") id $ fromException err)
Right x -> Right x
encodeASN1Stream :: [ASN1] -> Either ASN1Err L.ByteString
encodeASN1Stream l =
case runIdentity $ E.run (E.enumList 1 l $$ E.joinI $ enumWriteBytes $$ E.consume) of
Left err -> Left (maybe (ASN1ParsingFail "unknown") id $ fromException err)
Right x -> Right $ L.fromChunks x
decodeASN1s :: L.ByteString -> Either ASN1Err [ASN1t]
decodeASN1s = either (Left) (Right . ofStream) . decodeASN1Stream
decodeASN1 :: L.ByteString -> Either ASN1Err ASN1t
decodeASN1 = either (Left) (Right . head . ofStream) . decodeASN1Stream
encodeASN1s :: [ASN1t] -> L.ByteString
encodeASN1s s = case encodeASN1Stream $ toStream s of
Left err -> error $ show err
Right x -> x
encodeASN1 :: ASN1t -> L.ByteString
encodeASN1 s = case encodeASN1Stream $ toStream [s] of
Left err -> error $ show err
Right x -> x