    -- |
    -- Module      : Data.X509
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- Read/Write X509 Certificate, CRL and their signed equivalents.
    --
    -- Follows RFC5280 / RFC6818
    --
    module Data.X509
        (
        -- * Types
          SignedCertificate
        , SignedCRL
        , Certificate(..)
        , PubKey(..)
        , PubKeyEC(..)
        , SerializedPoint(..)
        , PrivKey(..)
        , PrivKeyEC(..)
        , pubkeyToAlg
        , privkeyToAlg
        , module Data.X509.AlgorithmIdentifier
        , module Data.X509.Ext
        , module Data.X509.ExtensionRaw

        -- * Certificate Revocation List (CRL)
        , module Data.X509.CRL

        -- * Naming
        , DistinguishedName(..)
        , DnElement(..)
        , ASN1CharacterString(..)
        , getDnElement

        -- * Certificate Chain
        , module Data.X509.CertificateChain

        -- * Signed types and marshalling
        , Signed(..)
        , SignedExact
        , getSigned
        , getSignedData
        , objectToSignedExact
        , objectToSignedExactF
        , encodeSignedObject
        , decodeSignedObject

        -- * Parametrized Signed accessor
        , getCertificate
        , getCRL
        , decodeSignedCertificate
        , decodeSignedCRL

        -- * Hash distinguished names related function
        , hashDN
        , hashDN_old
        ) where

    import Control.Arrow (second)

    import Data.ASN1.Types
    import Data.ASN1.Encoding
    import Data.ASN1.BinaryEncoding
    import qualified Data.ByteString as B
    import qualified Data.ByteArray as BA

    import Data.X509.Cert
    import Data.X509.Ext
    import Data.X509.ExtensionRaw
    import Data.X509.CRL
    import Data.X509.CertificateChain
    import Data.X509.DistinguishedName
    import Data.X509.Signed
    import Data.X509.PublicKey
    import Data.X509.PrivateKey
    import Data.X509.AlgorithmIdentifier

    import Crypto.Hash

    -- | A Signed Certificate
    type SignedCertificate = SignedExact Certificate

    -- | A Signed CRL
    type SignedCRL         = SignedExact CRL

    -- | Get the Certificate associated to a SignedCertificate
    getCertificate :: SignedCertificate -> Certificate
    getCertificate :: SignedCertificate -> Certificate
    getCertificate = Signed Certificate -> Certificate
    forall a. (Show a, Eq a, ASN1Object a) => Signed a -> a
    signedObject (Signed Certificate -> Certificate)
    -> (SignedCertificate -> Signed Certificate)
    -> SignedCertificate
    -> Certificate
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . SignedCertificate -> Signed Certificate
    forall a. (Show a, Eq a, ASN1Object a) => SignedExact a -> Signed a
    getSigned

    -- | Get the CRL associated to a SignedCRL
    getCRL :: SignedCRL -> CRL
    getCRL :: SignedCRL -> CRL
    getCRL = Signed CRL -> CRL
    forall a. (Show a, Eq a, ASN1Object a) => Signed a -> a
    signedObject (Signed CRL -> CRL)
    -> (SignedCRL -> Signed CRL) -> SignedCRL -> CRL
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . SignedCRL -> Signed CRL
    forall a. (Show a, Eq a, ASN1Object a) => SignedExact a -> Signed a
    getSigned

    -- | Try to decode a bytestring to a SignedCertificate
    decodeSignedCertificate :: B.ByteString -> Either String SignedCertificate
    decodeSignedCertificate :: ByteString -> Either String SignedCertificate
    decodeSignedCertificate = ByteString -> Either String SignedCertificate
    forall a.
    (Show a, Eq a, ASN1Object a) =>
    ByteString -> Either String (SignedExact a)
    decodeSignedObject

    -- | Try to decode a bytestring to a SignedCRL
    decodeSignedCRL :: B.ByteString -> Either String SignedCRL
    decodeSignedCRL :: ByteString -> Either String SignedCRL
    decodeSignedCRL = ByteString -> Either String SignedCRL
    forall a.
    (Show a, Eq a, ASN1Object a) =>
    ByteString -> Either String (SignedExact a)
    decodeSignedObject

    -- | Make an OpenSSL style hash of distinguished name
    --
    -- OpenSSL algorithm is odd, and has been replicated here somewhat.
    -- only lower the case of ascii character.
    hashDN :: DistinguishedName -> B.ByteString
    hashDN :: DistinguishedName -> ByteString
    hashDN = Digest SHA1 -> ByteString
    forall a. Digest a -> ByteString
    shorten (Digest SHA1 -> ByteString)
    -> (DistinguishedName -> Digest SHA1)
    -> DistinguishedName
    -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . SHA1 -> ByteString -> Digest SHA1
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith SHA1
    SHA1 (ByteString -> Digest SHA1)
    -> (DistinguishedName -> ByteString)
    -> DistinguishedName
    -> Digest SHA1
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DER -> [ASN1] -> ByteString
    forall a. ASN1Encoding a => a -> [ASN1] -> ByteString
    encodeASN1' DER
    DER ([ASN1] -> ByteString)
    -> (DistinguishedName -> [ASN1]) -> DistinguishedName -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (DistinguishedNameInner -> [ASN1] -> [ASN1])
    -> [ASN1] -> DistinguishedNameInner -> [ASN1]
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip DistinguishedNameInner -> [ASN1] -> [ASN1]
    forall a. ASN1Object a => a -> [ASN1] -> [ASN1]
    toASN1 [] (DistinguishedNameInner -> [ASN1])
    -> (DistinguishedName -> DistinguishedNameInner)
    -> DistinguishedName
    -> [ASN1]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DistinguishedName -> DistinguishedNameInner
    DistinguishedNameInner (DistinguishedName -> DistinguishedNameInner)
    -> (DistinguishedName -> DistinguishedName)
    -> DistinguishedName
    -> DistinguishedNameInner
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DistinguishedName -> DistinguishedName
    dnLowerUTF8
        where dnLowerUTF8 :: DistinguishedName -> DistinguishedName
    dnLowerUTF8 (DistinguishedName l :: [(OID, ASN1CharacterString)]
    l) = [(OID, ASN1CharacterString)] -> DistinguishedName
    DistinguishedName ([(OID, ASN1CharacterString)] -> DistinguishedName)
    -> [(OID, ASN1CharacterString)] -> DistinguishedName
    forall a b. (a -> b) -> a -> b
    $ ((OID, ASN1CharacterString) -> (OID, ASN1CharacterString))
    -> [(OID, ASN1CharacterString)] -> [(OID, ASN1CharacterString)]
    forall a b. (a -> b) -> [a] -> [b]
    map ((ASN1CharacterString -> ASN1CharacterString)
    -> (OID, ASN1CharacterString) -> (OID, ASN1CharacterString)
    forall (a :: * -> * -> *) b c d.
    Arrow a =>
    a b c -> a (d, b) (d, c)
    second ASN1CharacterString -> ASN1CharacterString
    toLowerUTF8) [(OID, ASN1CharacterString)]
    l
              toLowerUTF8 :: ASN1CharacterString -> ASN1CharacterString
    toLowerUTF8 (ASN1CharacterString _ s :: ByteString
    s) = ASN1StringEncoding -> ByteString -> ASN1CharacterString
    ASN1CharacterString ASN1StringEncoding
    UTF8 ((Word8 -> Word8) -> ByteString -> ByteString
    B.map Word8 -> Word8
    asciiToLower ByteString
    s)
              asciiToLower :: Word8 -> Word8
    asciiToLower c :: Word8
    c
                | Word8
    c Word8 -> Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Word8
    w8A Bool -> Bool -> Bool
    && Word8
    c Word8 -> Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Word8
    w8Z = Int -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Word8 -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Word8
    c Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Char -> Int
    forall a. Enum a => a -> Int
    fromEnum 'A' Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Char -> Int
    forall a. Enum a => a -> Int
    fromEnum 'a')
                | Bool
    otherwise            = Word8
    c
              w8A :: Word8
    w8A = Int -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int -> Word8) -> Int -> Word8
    forall a b. (a -> b) -> a -> b
    $ Char -> Int
    forall a. Enum a => a -> Int
    fromEnum 'A'
              w8Z :: Word8
    w8Z = Int -> Word8
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral (Int -> Word8) -> Int -> Word8
    forall a b. (a -> b) -> a -> b
    $ Char -> Int
    forall a. Enum a => a -> Int
    fromEnum 'Z'

    -- | Create an openssl style old hash of distinguished name
    hashDN_old :: DistinguishedName -> B.ByteString
    hashDN_old :: DistinguishedName -> ByteString
    hashDN_old = Digest MD5 -> ByteString
    forall a. Digest a -> ByteString
    shorten (Digest MD5 -> ByteString)
    -> (DistinguishedName -> Digest MD5)
    -> DistinguishedName
    -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . MD5 -> ByteString -> Digest MD5
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith MD5
    MD5 (ByteString -> Digest MD5)
    -> (DistinguishedName -> ByteString)
    -> DistinguishedName
    -> Digest MD5
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . DER -> [ASN1] -> ByteString
    forall a. ASN1Encoding a => a -> [ASN1] -> ByteString
    encodeASN1' DER
    DER ([ASN1] -> ByteString)
    -> (DistinguishedName -> [ASN1]) -> DistinguishedName -> ByteString
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (DistinguishedName -> [ASN1] -> [ASN1])
    -> [ASN1] -> DistinguishedName -> [ASN1]
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip DistinguishedName -> [ASN1] -> [ASN1]
    forall a. ASN1Object a => a -> [ASN1] -> [ASN1]
    toASN1 []

    shorten :: Digest a -> B.ByteString
    shorten :: Digest a -> ByteString
    shorten b :: Digest a
    b = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ (Int -> Word8) -> [Int] -> [Word8]
    forall a b. (a -> b) -> [a] -> [b]
    map Int -> Word8
    i [3,2,1,0]
        where i :: Int -> Word8
    i n :: Int
    n = Digest a -> Int -> Word8
    forall a. ByteArrayAccess a => a -> Int -> Word8
    BA.index Digest a
    b Int
    n