module OpenSSL.X509.Revocation
(
CRL
, X509_CRL
, RevokedCertificate(..)
, newCRL
, wrapCRL
, withCRLPtr
, signCRL
, verifyCRL
, printCRL
, sortCRL
, getVersion
, setVersion
, getLastUpdate
, setLastUpdate
, getNextUpdate
, setNextUpdate
, getIssuerName
, setIssuerName
, getRevokedList
, addRevoked
, getRevoked
)
where
import Control.Monad
import Data.List
import Data.Time.Clock
import Data.Typeable
import Foreign
import Foreign.C
import OpenSSL.ASN1
import OpenSSL.BIO
import OpenSSL.EVP.Digest hiding (digest)
import OpenSSL.EVP.PKey
import OpenSSL.EVP.Verify
import OpenSSL.EVP.Internal
import OpenSSL.Stack
import OpenSSL.Utils
import OpenSSL.X509.Name
newtype CRL = CRL (ForeignPtr X509_CRL)
data X509_CRL
data X509_REVOKED
data RevokedCertificate
= RevokedCertificate {
revSerialNumber :: Integer
, revRevocationDate :: UTCTime
}
deriving (Show, Eq, Typeable)
foreign import ccall unsafe "X509_CRL_new"
_new :: IO (Ptr X509_CRL)
foreign import ccall unsafe "&X509_CRL_free"
_free :: FunPtr (Ptr X509_CRL -> IO ())
foreign import ccall unsafe "X509_CRL_sign"
_sign :: Ptr X509_CRL -> Ptr EVP_PKEY -> Ptr EVP_MD -> IO CInt
foreign import ccall unsafe "X509_CRL_verify"
_verify :: Ptr X509_CRL -> Ptr EVP_PKEY -> IO CInt
foreign import ccall unsafe "X509_CRL_print"
_print :: Ptr BIO_ -> Ptr X509_CRL -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_CRL_get_version"
_get_version :: Ptr X509_CRL -> IO CLong
foreign import ccall unsafe "X509_CRL_set_version"
_set_version :: Ptr X509_CRL -> CLong -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_CRL_get_lastUpdate"
_get_lastUpdate :: Ptr X509_CRL -> IO (Ptr ASN1_TIME)
foreign import ccall unsafe "X509_CRL_set_lastUpdate"
_set_lastUpdate :: Ptr X509_CRL -> Ptr ASN1_TIME -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_CRL_get_nextUpdate"
_get_nextUpdate :: Ptr X509_CRL -> IO (Ptr ASN1_TIME)
foreign import ccall unsafe "X509_CRL_set_nextUpdate"
_set_nextUpdate :: Ptr X509_CRL -> Ptr ASN1_TIME -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_CRL_get_issuer"
_get_issuer_name :: Ptr X509_CRL -> IO (Ptr X509_NAME)
foreign import ccall unsafe "X509_CRL_set_issuer_name"
_set_issuer_name :: Ptr X509_CRL -> Ptr X509_NAME -> IO CInt
foreign import ccall unsafe "HsOpenSSL_X509_CRL_get_REVOKED"
_get_REVOKED :: Ptr X509_CRL -> IO (Ptr STACK)
foreign import ccall unsafe "X509_CRL_add0_revoked"
_add0_revoked :: Ptr X509_CRL -> Ptr X509_REVOKED -> IO CInt
foreign import ccall unsafe "X509_CRL_sort"
_sort :: Ptr X509_CRL -> IO CInt
foreign import ccall unsafe "X509_REVOKED_new"
_new_revoked :: IO (Ptr X509_REVOKED)
foreign import ccall unsafe "X509_REVOKED_free"
freeRevoked :: Ptr X509_REVOKED -> IO ()
foreign import ccall unsafe "X509_REVOKED_set_serialNumber"
_set_serialNumber :: Ptr X509_REVOKED -> Ptr ASN1_INTEGER -> IO CInt
foreign import ccall unsafe "X509_REVOKED_set_revocationDate"
_set_revocationDate :: Ptr X509_REVOKED -> Ptr ASN1_TIME -> IO CInt
newCRL :: IO CRL
newCRL = _new >>= wrapCRL
wrapCRL :: Ptr X509_CRL -> IO CRL
wrapCRL = fmap CRL . newForeignPtr _free
withCRLPtr :: CRL -> (Ptr X509_CRL -> IO a) -> IO a
withCRLPtr (CRL crl) = withForeignPtr crl
signCRL :: KeyPair key =>
CRL
-> key
-> Maybe Digest
-> IO ()
signCRL crl key mDigest
= withCRLPtr crl $ \ crlPtr ->
withPKeyPtr' key $ \ pkeyPtr ->
do digest <- case mDigest of
Just md -> return md
Nothing -> pkeyDefaultMD key
withMDPtr digest $ \ digestPtr ->
_sign crlPtr pkeyPtr digestPtr
>>= failIf_ (== 0)
return ()
verifyCRL :: PublicKey key => CRL -> key -> IO VerifyStatus
verifyCRL crl key
= withCRLPtr crl $ \ crlPtr ->
withPKeyPtr' key $ \ pkeyPtr ->
_verify crlPtr pkeyPtr
>>= interpret
where
interpret :: CInt -> IO VerifyStatus
interpret 1 = return VerifySuccess
interpret 0 = return VerifyFailure
interpret _ = raiseOpenSSLError
printCRL :: CRL -> IO String
printCRL crl
= do mem <- newMem
withBioPtr mem $ \ memPtr ->
withCRLPtr crl $ \ crlPtr ->
_print memPtr crlPtr
>>= failIf_ (/= 1)
bioRead mem
getVersion :: CRL -> IO Int
getVersion crl
= withCRLPtr crl $ \ crlPtr ->
liftM fromIntegral $ _get_version crlPtr
setVersion :: CRL -> Int -> IO ()
setVersion crl ver
= withCRLPtr crl $ \ crlPtr ->
_set_version crlPtr (fromIntegral ver)
>>= failIf (/= 1)
>> return ()
getLastUpdate :: CRL -> IO UTCTime
getLastUpdate crl
= withCRLPtr crl $ \ crlPtr ->
_get_lastUpdate crlPtr
>>= peekASN1Time
setLastUpdate :: CRL -> UTCTime -> IO ()
setLastUpdate crl utc
= withCRLPtr crl $ \ crlPtr ->
withASN1Time utc $ \ time ->
_set_lastUpdate crlPtr time
>>= failIf (/= 1)
>> return ()
getNextUpdate :: CRL -> IO UTCTime
getNextUpdate crl
= withCRLPtr crl $ \ crlPtr ->
_get_nextUpdate crlPtr
>>= peekASN1Time
setNextUpdate :: CRL -> UTCTime -> IO ()
setNextUpdate crl utc
= withCRLPtr crl $ \ crlPtr ->
withASN1Time utc $ \ time ->
_set_nextUpdate crlPtr time
>>= failIf (/= 1)
>> return ()
getIssuerName :: CRL -> Bool -> IO [(String, String)]
getIssuerName crl wantLongName
= withCRLPtr crl $ \ crlPtr ->
do namePtr <- _get_issuer_name crlPtr
peekX509Name namePtr wantLongName
setIssuerName :: CRL -> [(String, String)] -> IO ()
setIssuerName crl issuer
= withCRLPtr crl $ \ crlPtr ->
withX509Name issuer $ \ namePtr ->
_set_issuer_name crlPtr namePtr
>>= failIf (/= 1)
>> return ()
getRevokedList :: CRL -> IO [RevokedCertificate]
getRevokedList crl
= withCRLPtr crl $ \ crlPtr ->
_get_REVOKED crlPtr >>= mapStack peekRevoked
peekRevoked :: Ptr X509_REVOKED -> IO RevokedCertificate
peekRevoked rev = do
serial <- peekASN1Integer =<< ((\hsc_ptr -> peekByteOff hsc_ptr 0)) rev
date <- peekASN1Time =<< ((\hsc_ptr -> peekByteOff hsc_ptr 4)) rev
return RevokedCertificate { revSerialNumber = serial
, revRevocationDate = date
}
newRevoked :: RevokedCertificate -> IO (Ptr X509_REVOKED)
newRevoked revoked
= do revPtr <- _new_revoked
seriRet <- withASN1Integer (revSerialNumber revoked) $
_set_serialNumber revPtr
dateRet <- withASN1Time (revRevocationDate revoked) $
_set_revocationDate revPtr
if seriRet /= 1 || dateRet /= 1 then
freeRevoked revPtr >> raiseOpenSSLError
else
return revPtr
addRevoked :: CRL -> RevokedCertificate -> IO ()
addRevoked crl revoked
= withCRLPtr crl $ \ crlPtr ->
do revPtr <- newRevoked revoked
ret <- _add0_revoked crlPtr revPtr
case ret of
1 -> return ()
_ -> freeRevoked revPtr >> raiseOpenSSLError
getRevoked :: CRL -> Integer -> IO (Maybe RevokedCertificate)
getRevoked crl serial = find p `fmap` getRevokedList crl
where
p :: RevokedCertificate -> Bool
p = ((==) serial) . revSerialNumber
sortCRL :: CRL -> IO ()
sortCRL crl
= withCRLPtr crl $ \ crlPtr ->
_sort crlPtr >>= failIf_ (/= 1)