hsgnutls-0.2.3.2: Network/GnuTLS/X509.chs
module Network.GnuTLS.X509 (
-- * Types
Certificate(),
PrivateKey(),
-- * Certificate Functions
--newCertificate,
--newPrivateKey,
importCertificate,
importPrivateKey,
exportCertificate,
exportPrivateKey,
verifySignature,
signData,
certificateRSAParameters,
privateKeyRSAParameters,
privateKeyGetKeyId,
certificateGetKeyId,
getIssuerDnByOid,
getDnByOid,
getExtensionByOid,
-- getPeersX509DN,
checkHostname,
checkIssuer,
-- activationTime,
-- expirationTime,
-- getPeersCertificatesX509
)
where
import Foreign
import Foreign.C
import Network.GnuTLS.Attributes
import Network.GnuTLS.GnuTLS
import Network.GnuTLS.Internals
import Network.GnuTLS.GnuTLSMonad
import Network.GnuTLS.Errors
import Network.GnuTLS.OID
import System.Time(ClockTime(TOD))
import Data.ByteString.Internal
import System.Time
#include <gnutls/x509.h>
{#context prefix = "gnutls_x509" #}
{#pointer gnutls_x509_crt_t as Certificate foreign newtype #}
{#pointer gnutls_x509_privkey_t as PrivateKey foreign newtype #}
{#pointer gnutls_x509_crl_t as CertificateRevocationList foreign newtype #}
instance Show PrivateKey where
show _ = "<PrivateKey>"
instance Show Certificate where
show _ = "<Certificate>"
#c
#ifdef COMPAT_GNUTLS_1_0
typedef gnutls_x509_crt gnutls_x509_crt_t;
typedef gnutls_x509_crl gnutls_x509_crl_t;
#endif
#endc
-- Certificate Initializaion
foreign import ccall unsafe "TLS.h &gnutls_x509_crt_deinit" gnutls_x509_crt_deinit :: FunPtr (Ptr Certificate -> IO ())
foreign import ccall unsafe "TLS.h gnutls_x509_crt_init" gnutls_x509_crt_init :: Ptr (Ptr Certificate) -> IO CInt
foreign import ccall unsafe "TLS.h &gnutls_x509_privkey_deinit" gnutls_x509_privkey_deinit :: FunPtr (Ptr PrivateKey -> IO ())
foreign import ccall unsafe "TLS.h gnutls_x509_privkey_init" gnutls_x509_privkey_init :: Ptr (Ptr PrivateKey) -> IO CInt
-- | Import an encoded certificate to the native format.
importCertificate :: Datum d => d -> X509CertificateFormat -> GnuTLSMonad Certificate
importCertificate dat form = unsafePerformIO $ withDatum dat $
\rdat -> alloca $ \ptr -> do
res <- gnutls_x509_crt_init ptr
if res/=0 then return (Left $ GnuTLSError res)
else do
fp <- newForeignPtr gnutls_x509_crt_deinit =<< peek ptr
res2 <- withForeignPtr fp (\cr -> {#call unsafe gnutls_x509_crt_import#} cr rdat (enumCInt form))
if res2/=0 then return (Left $ GnuTLSError res2)
else return (Right $ Certificate fp)
importPrivateKey :: Datum d => d -> X509CertificateFormat -> GnuTLSMonad PrivateKey
importPrivateKey dat form = unsafePerformIO $ withDatum dat $
\rdat -> alloca $ \ptr -> do
res <- gnutls_x509_privkey_init ptr
if res/=0 then return (Left $ GnuTLSError res)
else do
fp <- newForeignPtr gnutls_x509_privkey_deinit =<< peek ptr
res2 <- withForeignPtr fp (\cr -> {#call unsafe gnutls_x509_privkey_import#} cr rdat (enumCInt form))
if res2/=0 then return (Left $ GnuTLSError res2)
else return (Right $ PrivateKey fp)
{-getPeersCertificatesX509 :: Session t -> IO [Certificate]
getPeersCertificatesX509 ses = mapM p =<< getPeersCertificatesRaw ses
where p raw = do c <- newCertificate
importCertificate c raw X509FmtDer
return c-}
exportCertificate :: Certificate -> X509CertificateFormat -> GnuTLSMonad ByteString
exportCertificate cert form = unsafePerformIO $ exportCertificate'' cert form 1024
exportPrivateKey :: PrivateKey -> X509CertificateFormat -> GnuTLSMonad ByteString
exportPrivateKey key form = unsafePerformIO $ export'' (exportPrivateKey' key form) 1024
export'' :: (Ptr () -> Ptr CSize -> IO CInt) -> Int -> IO (GnuTLSMonad ByteString)
export'' f sz = do
(str,res) <- createAndTrim' sz $ \ptr -> with (fromIntegral sz) $ \szptr -> do
res <- f (castPtr ptr) szptr
case () of
() | res==0 -> do
rsize <- peek szptr
return (0,fromIntegral rsize,Nothing)
| res==errorShortMemory -> return (0,0,Just Nothing)
| otherwise -> return (0,0,Just $ Just res)
case res of
Nothing -> return $ Right str
Just Nothing -> export'' f (sz+1024)
Just (Just err) -> return $ Left (GnuTLSError err)
exportCertificate'' :: Certificate -> X509CertificateFormat -> Int -> IO (GnuTLSMonad ByteString)
exportCertificate'' cert format sz = export'' (exportCertificate' cert format) sz
-- | Helper function
exportCertificate' :: Certificate -> X509CertificateFormat -> Ptr () -> Ptr CSize -> IO CInt
exportCertificate' cert format ptr sptr = withCertificate cert $ \cp -> {#call unsafe gnutls_x509_crt_export#} cp (enumCInt format) ptr (castPtr sptr)
-- | Helper function
exportPrivateKey' :: PrivateKey -> X509CertificateFormat -> Ptr () -> Ptr CSize -> IO CInt
exportPrivateKey' key format ptr sptr = withPrivateKey key $ \cp -> {#call unsafe gnutls_x509_privkey_export#} cp (enumCInt format) ptr (castPtr sptr)
{#fun unsafe gnutls_x509_crt_get_expiration_time as getExpirationTime
{withCertificate* `Certificate'} -> `ClockTime' toTime* #}
toTime :: Integral n => n -> IO ClockTime
toTime (-1) = fail "Error while getting time"
toTime x = return $ TOD (fromIntegral x) 0
verifySignature :: (Datum dat,Datum sign) => Certificate -> dat -> sign -> GnuTLSMonad Bool
verifySignature cert dat sig = unsafePerformIO $ withCertificate cert $
\rcert -> withDatum dat $
\rdat -> withDatum sig $
\rsig -> do
res <- {#call unsafe gnutls_x509_crt_verify_data#} rcert 0 rdat rsig
case () of
() | res == 0 -> return $ Right False
| res == 1 -> return $ Right True
| otherwise -> return $ Left $ GnuTLSError res
signData :: Datum d => PrivateKey -> DigestAlgorithm -> d -> GnuTLSMonad ByteString
signData key alg dat = unsafePerformIO $ signData'' key alg dat 1024
signData'' :: Datum d => PrivateKey -> DigestAlgorithm -> d -> Int -> IO (GnuTLSMonad ByteString)
signData'' key alg dat sz = do
(str,res) <- createAndTrim' sz $ \ptr -> with (fromIntegral sz) $ \szptr -> do
res <- signData' key alg dat (castPtr ptr) szptr
case () of
() | res==0 -> do
rsize <- peek szptr
return (0,fromIntegral rsize,Nothing)
| res==errorShortMemory -> return (0,0,Just Nothing)
| otherwise -> return (0,0,Just $ Just res)
case res of
Nothing -> return $ Right str
Just Nothing -> signData'' key alg dat (sz+1024)
Just (Just err) -> return $ Left (GnuTLSError err)
signData' :: Datum d => PrivateKey -> DigestAlgorithm -> d -> Ptr () -> Ptr CSize -> IO CInt
signData' priv alg dat strptr sptr = withPrivateKey priv $
\rpriv -> withDatum dat $
\rdat -> {#call unsafe gnutls_x509_privkey_sign_data#} rpriv (enumCInt alg) 0 rdat strptr (castPtr sptr)
certificateRSAParameters :: Certificate -> GnuTLSMonad (ByteString,ByteString)
certificateRSAParameters cert = unsafePerformIO $ withCertificate cert $
\rcert -> alloca $ \(ptrM::Ptr (Ptr ())) -> alloca $
\(ptrE::Ptr (Ptr ())) -> do
res <- {#call unsafe gnutls_x509_crt_get_pk_rsa_raw#} rcert (castPtr ptrM) (castPtr ptrE)
if res/=0 then return $ Left (GnuTLSError res)
else do
(cptrM,sizeM) <- peekDatum ptrM
(cptrE,sizeE) <- peekDatum ptrE
fpM <- newForeignPtr c_free_finalizer (castPtr cptrM)
fpE <- newForeignPtr c_free_finalizer (castPtr cptrE)
return $ Right (fromForeignPtr fpM 0 sizeM,fromForeignPtr fpE 0 sizeE)
privateKeyRSAParameters :: PrivateKey -> GnuTLSMonad (ByteString,ByteString,ByteString,ByteString,ByteString,ByteString)
privateKeyRSAParameters key = unsafePerformIO $ withPrivateKey key $
\rkey -> alloca $
\(ptrM::Ptr (Ptr ())) -> alloca $
\(ptrE::Ptr (Ptr ())) -> alloca $
\(ptrD::Ptr (Ptr ())) -> alloca $
\(ptrP::Ptr (Ptr ())) -> alloca $
\(ptrQ::Ptr (Ptr ())) -> alloca $
\(ptrU::Ptr (Ptr ())) -> do
res <- {#call unsafe gnutls_x509_privkey_export_rsa_raw#} rkey
(castPtr ptrM)
(castPtr ptrE)
(castPtr ptrD)
(castPtr ptrP)
(castPtr ptrQ)
(castPtr ptrU)
if res/= 0 then return $ Left (GnuTLSError res)
else do
(cptrM,sizeM) <- peekDatum ptrM
(cptrE,sizeE) <- peekDatum ptrE
(cptrD,sizeD) <- peekDatum ptrD
(cptrP,sizeP) <- peekDatum ptrP
(cptrQ,sizeQ) <- peekDatum ptrQ
(cptrU,sizeU) <- peekDatum ptrU
fpM <- newForeignPtr c_free_finalizer (castPtr cptrM)
fpE <- newForeignPtr c_free_finalizer (castPtr cptrE)
fpD <- newForeignPtr c_free_finalizer (castPtr cptrD)
fpP <- newForeignPtr c_free_finalizer (castPtr cptrP)
fpQ <- newForeignPtr c_free_finalizer (castPtr cptrQ)
fpU <- newForeignPtr c_free_finalizer (castPtr cptrU)
return $ Right
(fromForeignPtr fpM 0 sizeM
,fromForeignPtr fpE 0 sizeE
,fromForeignPtr fpD 0 sizeD
,fromForeignPtr fpP 0 sizeP
,fromForeignPtr fpQ 0 sizeQ
,fromForeignPtr fpU 0 sizeU)
getPeersX509DN :: Certificate -> IO String
getPeersX509DN cert = do
withCertificate cert $ \cp ->
alloca $ \lenp -> do
res <- {#call unsafe gnutls_x509_crt_get_dn #} cp nullPtr lenp
len <- peek lenp
if (len == 0)
then return []
else allocaBytes (fromIntegral len) $ \charp -> do
res2 <- {#call unsafe gnutls_x509_crt_get_dn #} cp charp lenp
throwGnutlsIfNeg res2
len2 <- peek lenp
peekCStringLen (charp, fromIntegral len2)
-- | Check whether the certicate hostname matches the given name.
{#fun pure unsafe gnutls_x509_crt_check_hostname as checkHostname
{withCertificate* `Certificate', withCString* `String'} -> `GnuTLSMonad Bool' gnuTLSCheckBool #}
-- | Check if the second certificate issued the first one.
{#fun pure unsafe gnutls_x509_crt_check_issuer as checkIssuer
{withCertificate* `Certificate',withCertificate* `Certificate'} -> `GnuTLSMonad Bool' gnuTLSCheckBool#}
activationTime :: ReadWriteAttr Certificate ClockTime ClockTime
activationTime = newAttr g s
where g c = withCertificate c {#call unsafe gnutls_x509_crt_get_activation_time#} >>= return . integralToClockTime
s c (TOD s _) = do withCertificate c $ \cp -> do
{#call gnutls_x509_crt_set_activation_time #} cp (fromIntegral s) >>= throwGnutlsIf
expirationTime :: ReadWriteAttr Certificate ClockTime ClockTime
expirationTime = newAttr g s
where g c = withCertificate c {#call unsafe gnutls_x509_crt_get_expiration_time#} >>= return . integralToClockTime
s c (TOD s _) = do withCertificate c $ \cp -> do
{#call unsafe gnutls_x509_crt_set_expiration_time #} cp (fromIntegral s) >>= throwGnutlsIf
getIssuerDnByOid :: Certificate -> OID -> Int -> GnuTLSMonad (Maybe ByteString)
getIssuerDnByOid cert str ind = unsafePerformIO (getDnByOid' {#call unsafe gnutls_x509_crt_get_issuer_dn_by_oid#} cert str ind)
-- | Retrieves a field by it\'s Object Identifier.
getDnByOid :: Certificate -- ^ Certificate to retrieve the field-data from
-> OID -- ^ The field name, specified by an Object Indentifier
-> Int -- ^ If there\'s more than one entry, this will be used to describe which one to use(0 gives the first entry)
-> GnuTLSMonad (Maybe ByteString)
getDnByOid cert str ind = unsafePerformIO (getDnByOid' {#call unsafe gnutls_x509_crt_get_dn_by_oid#} cert str ind)
getExtensionByOid :: Certificate
-> OID
-> Int
-> GnuTLSMonad (Maybe (ByteString,Bool))
getExtensionByOid cert str ind
= unsafePerformIO $ alloca $ \crit ->
withCString str $ \cstr ->
withCertificate cert $ \certptr ->
with (1024::Int) $ \szptr ->
createAndTrim' 1024 (\ptr -> do
res <- {#call unsafe gnutls_x509_crt_get_extension_by_oid#} certptr cstr (fromIntegral ind) (castPtr ptr) (castPtr szptr) crit
if res /= 0
then return (0,0,Left $ GnuTLSError res)
else (if ptr == nullPtr
then return $ (0,0,Right False)
else (do
size <- peek szptr
return (0,size,Right True)
))) >>= \(bs,res) -> case res of
Left err -> return $ Left err
Right True -> do
iscrit <- peek crit
return $ Right $ Just (bs,iscrit < 0)
Right False -> return $ Right Nothing
getDnByOid' f cert str ind
= withCString str $ \cstr ->
withCertificate cert $ \certptr ->
with (1024::Int) $ \szptr ->
createAndTrim' 1024 (\ptr -> do
res <- f certptr cstr (fromIntegral ind) 0 (castPtr ptr) (castPtr szptr)
if res /= 0
then return (0,0,Left $ GnuTLSError res)
else (if ptr == nullPtr
then return $ (0,0,Right False)
else (do
size <- peek szptr
return (0,size,Right True)
))) >>= \(bs,res) -> return $ case res of
Left err -> Left err
Right True -> Right $ Just bs
Right False -> Right Nothing
privateKeyGetKeyId :: PrivateKey -> GnuTLSMonad ByteString
privateKeyGetKeyId key = unsafePerformIO $
withPrivateKey key $ \rkey ->
with (20::Int) $ \szptr -> -- KeyID is a SHA1 hash, ergo always 20 bytes
createAndTrim' 20 (\ptr -> do
res <- {#call unsafe gnutls_x509_privkey_get_key_id#} rkey 0 (castPtr ptr) (castPtr szptr)
if res /= 0
then return (0,0,Just $ GnuTLSError res)
else (do
size <- peek szptr
return (0,size,Nothing)
)) >>= \(bs,res) -> case res of
Nothing -> return (Right bs)
Just err -> return (Left err)
certificateGetKeyId :: Certificate -> GnuTLSMonad ByteString
certificateGetKeyId cert = unsafePerformIO $
withCertificate cert $ \rcert ->
with (20::Int) $ \szptr ->
createAndTrim' 20 (\ptr -> do
res <- {#call unsafe gnutls_x509_crt_get_key_id#} rcert 0 (castPtr ptr) (castPtr szptr)
if res/=0
then return (0,0,Just $ GnuTLSError res)
else (do
size <- peek szptr
return (0,size,Nothing)
)) >>= \(bs,res) -> case res of
Nothing -> return (Right bs)
Just err -> return (Left err)