Crypto-3.0.3: Codec/ASN1/TLV.hs
-----------------------------------------------------------------------------
-- |
-- Module : Codec.ASN1.TLV
-- Copyright : (c) Dominic Steinitz 2005
-- License : BSD-style (see the file ReadMe.tex)
--
-- Maintainer : dominic.steinitz@blueyonder.co.uk
-- Stability : experimental
-- Portability : portable
--
-- Decode binary BER into abstract an abstract representation of tag,
-- length and value ensuring that the lengths are consistent.
--
-----------------------------------------------------------------------------
module Codec.ASN1.TLV (
-- * Types
-- * Function types,
tlv,
tlvIO
) where
import Data.Bits
import Control.Exception
import Control.Monad.State
import Control.Monad.Error
import System.IO.Error
import qualified NewBinary.Binary as B (BinHandle, getBits, getByte)
import Codec.ASN1.BER
import Codec.Utils
-- The bit decoder will (by design) never lie about lengths
-- because it can check these. It may lie (if that's what it's being
-- told) about numbers of components because it can't check these
-- without having the ASN.1 definitions.
{-
041120 125723
When interpreting the bits into tag-length-value form,
remember to keep track of where things are.
041120 161608
We'll need isEOFBin or to catch exceptions.
041121 151059
As decoding to tag length value is monadic and then so it
decoding to the ASN.1 at some point we'll need to interleave
actions.
080105 082425
tlv should report an error if there are any Octets left over.
-}
tlv :: [Octet] -> (Length,Encoding)
tlv xs = let ((l,e),_) = runState (tlv_ undefined) (xs,0::Offset) in (l,e)
tlvIO :: BinHandle -> IO (Length, Encoding)
tlvIO = tlv_
tlv_ bin =
do tagValueVal <- getBits bin 5
tagConstructionVal <- getBits bin 1
tagTypeVal <- getBits bin 2
let tagType = toEnum $ fromIntegral tagTypeVal
tagValue = fromIntegral tagValueVal
if tagValue /= 31
then do (ll,l) <- getLength bin
f 1 tagConstructionVal
tagType tagValue ll l
else do xs <- getTagOctets bin
let longform =
fromIntegral (fromOctets 128 xs)
(ll,l) <- getLength bin
f (fromIntegral $ length xs) tagConstructionVal
tagType longform ll l
where f tl tcv tt tv ll l =
if tcv == 0
then do xs <- getOctets bin l
let x = Primitive tt tv l xs
return (tl+ll+l,x)
else do ys <- tlvs_ bin l
let x = Constructed tt tv l ys
return (tl+ll+l,x)
tlvs_ bin curLen
| curLen < 0 = fail "Codec.ASN1.TLV.tlvs_: trying to decode a negative number of octets"
| curLen == 0 = return []
| otherwise = do (l,x) <- tlv_ bin
ys <- tlvs_ bin (curLen-l)
return (x:ys)
getTagOctets bin =
do x <- getByte bin
if not (testBit x msb)
then return [x]
else do xs <- getTagOctets bin
return ((clearBit x msb):xs)
-- Need to think about testing. Here are some links:
-- http://www.eeye.com/html/Research/Advisories/AD20040210.html
-- http://www.galois.com/files/HCSS-04-ASN.1.pdf
-- http://www.larmouth.demon.co.uk/tutorials/tagging/sld003.htm
getLength bin =
do x <- getByte bin
let isShort = not (testBit x msb)
shortform = fromIntegral x
length = fromIntegral (clearBit x msb) in
if x == 0x80
then error "Indefinite length not supported"
else if isShort
then return (1,shortform)
else do xs <- getOctets bin length
let longform = fromOctets 256 xs in
return (length+1,longform)
getOctets bin l =
if l <= 0
then return []
else do x <- getByte bin
xs <- getOctets bin (l-1)
return (x:xs)
type BinHandle = B.BinHandle
type NumBits = Int
class Binary m where
getBits :: BinHandle -> NumBits -> m Octet
getByte :: BinHandle -> m Octet
instance Binary IO where
getBits = B.getBits
getByte = B.getByte
type Offset = Int
instance Binary (State ([Octet],Offset)) where
getBits = getBits'
getByte = getByte'
{-
getBits is never exported and does not need to be general. We know
we will only ever use it at an Octet boundary and we will never cross
an Octet boundary.
-}
getBits' :: MonadState ([Octet],Offset) m => BinHandle -> NumBits -> m Octet
getBits' _ n =
do (xs,offset) <- get
if null xs
then throw (IOException $
mkIOError eofErrorType
"Codec.ASN1.TLV.getBits"
Nothing Nothing)
else do let r = select offset n (head xs)
m = bitSize r
if offset + n < m
then put (xs,offset + n)
else put (tail xs,0)
return r
select :: Offset -> NumBits -> Octet -> Octet
select offset n x =
clearBits n p $ shiftR x offset
where p = bitSize n
clearBits :: Bits a => Int -> Int -> a -> a
clearBits = bits clearBit
bits :: Enum b => (a -> b -> a) -> b -> b -> a -> a
bits f m n = foldr (.) id (map (\i -> flip f i) [m..n])
getByte' :: MonadState ([Octet],Offset) m => BinHandle -> m Octet
getByte' _ =
do (xs,offset) <- get
if null xs
then throw (IOException $
mkIOError eofErrorType
"Codec.ASN1.TLV.getByte"
Nothing Nothing)
else do put (tail xs,offset)
return (head xs)