module Data.Certificate.X509
        (
        
          X509(..)
        
        , SignatureALG(..)
        , HashALG(..)
        , PubKeyALG(..)
        , PubKey(..)
        , OID
        , ASN1StringType(..)
        , ASN1String
        , Certificate(..)
        , module Data.Certificate.X509.Ext
        
        , getSigningData
        
        , decodeCertificate
        , encodeCertificate
        
        , decodeDN
        , encodeDN
        ) where
import Data.Word
import Data.ASN1.DER
import Data.ASN1.Stream (getConstructedEndRepr)
import Data.ASN1.Raw (toBytes)
import Data.ASN1.BitArray
import qualified Data.ByteString.Lazy as L
import Data.Certificate.X509.Internal
import Data.Certificate.X509.Cert hiding (encodeDN)
import qualified  Data.Certificate.X509.Cert as Cert
import Data.Certificate.X509.Ext
data X509 = X509
        { x509Cert              :: Certificate          
        , x509CachedSigningData :: (Maybe L.ByteString) 
                                                        
        , x509CachedData        :: (Maybe L.ByteString) 
        , x509SignatureALG      :: SignatureALG         
        , x509Signature         :: [Word8]              
        } deriving (Show)
instance Eq X509 where
        x1 == x2 =
                (x509Cert x1         == x509Cert x2)         &&
                (x509SignatureALG x1 == x509SignatureALG x2) &&
                (x509Signature x1    == x509Signature x2)
getSigningData :: X509 -> L.ByteString
getSigningData (X509 _    (Just e) _ _ _) = e
getSigningData (X509 cert Nothing _ _ _)  = e
        where
                (Right e) = encodeASN1Stream header
                header    = asn1Container Sequence $ encodeCertificateHeader cert
decodeCertificate :: L.ByteString -> Either String X509
decodeCertificate by = either (Left . show) parseRootASN1 $ decodeASN1StreamRepr by
        where
                
                parseRootASN1 l = onContainer rootseq $ \l2 ->
                                let (certrepr,rem1)  = getConstructedEndRepr l2 in
                                let (sigalgseq,rem2) = getConstructedEndRepr rem1 in
                                let (sigseq,_)       = getConstructedEndRepr rem2 in
                                let cert = onContainer certrepr (runParseASN1 parseCertificate . map fst) in
                                case (cert, map fst sigseq) of
                                        (Right c, [BitString b]) ->
                                                let certevs = toBytes $ concatMap snd certrepr in
                                                let sigalg  = onContainer sigalgseq (parseSigAlg . map fst) in
                                                Right $ X509 c (Just certevs) (Just by) sigalg (L.unpack $ bitArrayGetData b)
                                        (Left err, _) -> Left $ ("certificate error: " ++ show err)
                                        _             -> Left $ "certificate structure error"
                        where
                                (rootseq, _) = getConstructedEndRepr l
                onContainer ((Start _, _) : l) f =
                        case reverse l of
                                ((End _, _) : l2) -> f $ reverse l2
                                _                 -> f []
                onContainer _ f = f []
                parseSigAlg [ OID oid, Null ] = oidSig oid
                parseSigAlg _                 = SignatureALG_Unknown []
encodeCertificate :: X509 -> L.ByteString
encodeCertificate (X509 _    _ (Just lbs) _      _      ) = lbs
encodeCertificate (X509 cert _ Nothing    sigalg sigbits) = case encodeASN1Stream rootSeq of
                Right x  -> x
                Left err -> error (show err)
        where
                esigalg   = asn1Container Sequence [OID (sigOID sigalg), Null]
                esig      = BitString $ toBitArray (L.pack sigbits) 0
                header    = asn1Container Sequence $ encodeCertificateHeader cert
                rootSeq   = asn1Container Sequence (header ++ esigalg ++ [esig])
decodeDN :: L.ByteString -> Either String [(OID, ASN1String)]
decodeDN by = either (Left . show) (runParseASN1 parseDN) $ decodeASN1Stream by
encodeDN :: [(OID, ASN1String)] -> Either String L.ByteString
encodeDN dn = either (Left . show) Right $ encodeASN1Stream $ Cert.encodeDN dn