{-# LANGUAGE FlexibleContexts #-}
module Data.X509.CRL
( CRL(..)
, RevokedCertificate(..)
) where
import Control.Applicative
import Data.Hourglass (DateTime, TimezoneOffset(..))
import Data.ASN1.Types
import Data.X509.DistinguishedName
import Data.X509.AlgorithmIdentifier
import Data.X509.ExtensionRaw
import Data.X509.Internal
data CRL = CRL
{ crlVersion :: Integer
, crlSignatureAlg :: SignatureALG
, crlIssuer :: DistinguishedName
, crlThisUpdate :: DateTime
, crlNextUpdate :: Maybe DateTime
, crlRevokedCertificates :: [RevokedCertificate]
, crlExtensions :: Extensions
} deriving (Show,Eq)
data RevokedCertificate = RevokedCertificate
{ revokedSerialNumber :: Integer
, revokedDate :: DateTime
, revokedExtensions :: Extensions
} deriving (Show,Eq)
instance ASN1Object CRL where
toASN1 crl = encodeCRL crl
fromASN1 = runParseASN1State parseCRL
instance ASN1Object RevokedCertificate where
fromASN1 = runParseASN1State $
onNextContainer Sequence $
RevokedCertificate
<$> parseSerialNumber
<*> (getNext >>= toTime)
<*> getObject
where toTime (ASN1Time _ t _) = pure t
toTime _ = throwParseError "bad revocation date"
toASN1 (RevokedCertificate serial time crlEntryExtensions) = \xs ->
[ Start Sequence ] ++
[ IntVal serial ] ++
[ ASN1Time TimeGeneralized time (Just (TimezoneOffset 0)) ] ++
toASN1 crlEntryExtensions [] ++
[ End Sequence ] ++
xs
parseSerialNumber :: ParseASN1 Integer
parseSerialNumber = do
n <- getNext
case n of
IntVal v -> return v
_ -> throwParseError ("missing serial" ++ show n)
parseCRL :: ParseASN1 CRL
parseCRL = do
CRL <$> (getNext >>= getVersion)
<*> getObject
<*> getObject
<*> (getNext >>= getThisUpdate)
<*> getNextUpdate
<*> parseRevokedCertificates
<*> parseCRLExtensions
where getVersion (IntVal v) = return $ fromIntegral v
getVersion _ = throwParseError "unexpected type for version"
getThisUpdate (ASN1Time _ t1 _) = return t1
getThisUpdate _ = throwParseError "bad this update format, expecting time"
getNextUpdate = getNextMaybe timeOrNothing
timeOrNothing (ASN1Time _ tnext _) = Just tnext
timeOrNothing _ = Nothing
parseRevokedCertificates :: ParseASN1 [RevokedCertificate]
parseRevokedCertificates =
fmap (maybe [] id) $ onNextContainerMaybe Sequence $ getMany getObject
parseCRLExtensions :: ParseASN1 Extensions
parseCRLExtensions =
fmap adapt $ onNextContainerMaybe (Container Context 0) $ getObject
where adapt (Just e) = e
adapt Nothing = Extensions Nothing
encodeCRL :: CRL -> ASN1S
encodeCRL crl xs =
[IntVal $ crlVersion crl] ++
toASN1 (crlSignatureAlg crl) [] ++
toASN1 (crlIssuer crl) [] ++
[ASN1Time TimeGeneralized (crlThisUpdate crl) (Just (TimezoneOffset 0))] ++
(maybe [] (\t -> [ASN1Time TimeGeneralized t (Just (TimezoneOffset 0))]) (crlNextUpdate crl)) ++
maybeRevoked (crlRevokedCertificates crl) ++
maybeCrlExts (crlExtensions crl) ++
xs
where
maybeRevoked [] = []
maybeRevoked xs' = asn1Container Sequence $ concatMap (\e -> toASN1 e []) xs'
maybeCrlExts (Extensions Nothing) = []
maybeCrlExts exts = asn1Container (Container Context 0) $ toASN1 exts []