module Network.DomainAuth.Pubkey.Der (
decode
, Class (..)
, TLV (..)
) where
import Control.Applicative hiding (many)
import Control.Monad
import Data.Binary.Get
import Data.Bits
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as BL
data TLV = Term
| Prim { cls :: Class
, tag :: Tag
, siz :: Size
, cnt :: ByteString
}
| Cons { cls :: Class
, tag :: Tag
, siz :: Size
, tlv :: [TLV]
}
deriving Show
data Class = Univ | Appl | Cont | Priv deriving (Show, Eq, Enum)
type Tag = Int
type Size = Int
decode :: BL.ByteString -> TLV
decode = runGet der
der :: Get TLV
der = do
first <- nonZero
let clss = getClass first
cons = getConstructor first
tg <- getTag first
len <- singleLength
if cons
then construct clss tg len
else primitive clss tg len
primitive :: Class -> Tag -> Int -> Get TLV
primitive clss tg len = Prim clss tg len <$> getByteString (fromIntegral len)
construct :: Class -> Tag -> Int -> Get TLV
construct = definite
definite :: Class -> Tag -> Int -> Get TLV
definite clss tg len = do
start <- fromIntegral <$> bytesRead
let end = start + len
Cons clss tg len <$> withinLimit end []
where
withinLimit end ps = do
p <- der
end2 <- fromIntegral <$> bytesRead
if end2 == end
then return (ps ++ [p])
else withinLimit end (ps ++ [p])
getClass :: Int -> Class
getClass first = toEnum (shift (first .&. classMask) ( classShift)) :: Class
getConstructor :: Int -> Bool
getConstructor first = first .&. consFlag == consFlag
getTag :: Int -> Get Int
getTag first = if tg == tagMask
then multiTag 0
else return tg
where
tg = first .&. tagMask
multiTag :: Int -> Get Int
multiTag len = do
i <- anyInt
if (i .&. tagEnd) == 0
then return (incTag len i)
else multiTag (incTag len i)
incTag :: Int -> Int -> Int
incTag len i = len * 128 + (i .&. tagLenMask)
singleLength :: Get Int
singleLength = do
second <- anyInt
let multi = getMulti second
len = getLen second
if multi
then definiteLength len
else return len
getMulti :: Int -> Bool
getMulti second = second .&. lenFlag == lenFlag
getLen :: Int -> Int
getLen second = second .&. lenMask
definiteLength :: Int -> Get Int
definiteLength bytes = if bytes == 0
then return indefiniteMark
else multiLength 0 bytes
multiLength :: Int -> Int -> Get Int
multiLength len bytes = do
i <- anyInt
if bytes == 1
then return (incLen len i)
else multiLength (incLen len i) (bytes 1)
incLen :: Int -> Int -> Int
incLen len i = len * 256 + i
anyInt :: Get Int
anyInt = fromIntegral <$> getWord8
nonZero :: Get Int
nonZero = do
n <- anyInt
when (n == 0) (error "nonZero")
return n
classMask :: Int
classMask = 0xc0
classShift :: Int
classShift = 6
consFlag :: Int
consFlag = 0x20
tagMask :: Int
tagMask = 0x1f
tagEnd :: Int
tagEnd = 0x80
tagLenMask :: Int
tagLenMask = 0x7f
lenFlag :: Int
lenFlag = 0x80
lenMask :: Int
lenMask = 0x7f
indefiniteMark :: Int
indefiniteMark = 1