HsOpenSSL-0.8: OpenSSL/EVP/PKey.hsc
{- -*- haskell -*- -}
{-# OPTIONS_HADDOCK prune #-}
-- |An interface to asymmetric cipher keypair.
#include "HsOpenSSL.h"
module OpenSSL.EVP.PKey
( PublicKey(..)
, KeyPair(..)
, SomePublicKey
, SomeKeyPair
-- private
, PKey(..)
, EVP_PKEY
, withPKeyPtr
, withPKeyPtr'
, wrapPKeyPtr
, unsafePKeyToPtr
, touchPKey
)
where
import Data.Typeable
import Data.Maybe
import Foreign
import Foreign.C
import OpenSSL.DSA
import OpenSSL.EVP.Digest hiding (digest)
import OpenSSL.RSA
import OpenSSL.Utils
-- VaguePKey is a ForeignPtr to EVP_PKEY, that is either public key or
-- a ker pair. We can't tell which at compile time.
newtype VaguePKey = VaguePKey (ForeignPtr EVP_PKEY)
data EVP_PKEY
-- Instances of class PKey can be converted back and forth to
-- VaguePKey.
class PKey k where
-- Wrap the key (i.g. RSA) into EVP_PKEY.
toPKey :: k -> IO VaguePKey
-- Extract the concrete key from the EVP_PKEY. Returns Nothing if
-- the type mismatches.
fromPKey :: VaguePKey -> IO (Maybe k)
-- Do the same as EVP_PKEY_size().
pkeySize :: k -> Int
-- Return the default digesting algorithm for the key.
pkeyDefaultMD :: k -> IO Digest
-- |Instances of this class has at least public portion of a
-- keypair. They might or might not have the private key.
class (Eq k, Typeable k, PKey k) => PublicKey k where
-- |Wrap an arbitrary public key into polymorphic type
-- 'SomePublicKey'.
fromPublicKey :: k -> SomePublicKey
fromPublicKey = SomePublicKey
-- |Cast from the polymorphic type 'SomePublicKey' to the concrete
-- type. Return 'Nothing' if failed.
toPublicKey :: SomePublicKey -> Maybe k
toPublicKey (SomePublicKey pk) = cast pk
-- |Instances of this class has both of public and private portions of
-- a keypair.
class PublicKey a => KeyPair a where
-- |Wrap an arbitrary keypair into polymorphic type 'SomeKeyPair'.
fromKeyPair :: a -> SomeKeyPair
fromKeyPair = SomeKeyPair
-- |Cast from the polymorphic type 'SomeKeyPair' to the concrete
-- type. Return 'Nothing' if failed.
toKeyPair :: SomeKeyPair -> Maybe a
toKeyPair (SomeKeyPair pk) = cast pk
-- Reconstruct the concrete public-key type from an EVP_PKEY.
withConcretePubKey :: VaguePKey -> (forall k. PublicKey k => k -> IO a) -> IO a
withConcretePubKey pk f
= withPKeyPtr pk $ \ pkeyPtr ->
do pkeyType <- (#peek EVP_PKEY, type) pkeyPtr :: IO CInt
case pkeyType of
#ifndef OPENSSL_NO_RSA
(#const EVP_PKEY_RSA)
-> do rsaPtr <- _get1_RSA pkeyPtr
Just rsa <- absorbRSAPtr rsaPtr
f (rsa :: RSAPubKey)
#endif
#ifndef OPENSSL_NO_DSA
(#const EVP_PKEY_DSA)
-> do dsaPtr <- _get1_DSA pkeyPtr
Just dsa <- absorbDSAPtr dsaPtr
f (dsa :: DSAPubKey)
#endif
_ -> fail ("withConcretePubKey: unsupported EVP_PKEY type: " ++ show pkeyType)
-- Reconstruct the concrete keypair type from an EVP_PKEY.
withConcreteKeyPair :: VaguePKey -> (forall k. KeyPair k => k -> IO a) -> IO a
withConcreteKeyPair pk f
= withPKeyPtr pk $ \ pkeyPtr ->
do pkeyType <- (#peek EVP_PKEY, type) pkeyPtr :: IO CInt
case pkeyType of
#ifndef OPENSSL_NO_RSA
(#const EVP_PKEY_RSA)
-> do rsaPtr <- _get1_RSA pkeyPtr
Just rsa <- absorbRSAPtr rsaPtr
f (rsa :: RSAKeyPair)
#endif
#ifndef OPENSSL_NO_DSA
(#const EVP_PKEY_DSA)
-> do dsaPtr <- _get1_DSA pkeyPtr
Just dsa <- absorbDSAPtr dsaPtr
f (dsa :: DSAKeyPair)
#endif
_ -> fail ("withConcreteKeyPair: unsupported EVP_PKEY type: " ++ show pkeyType)
-- |This is an opaque type to hold an arbitrary public key in it. The
-- actual key type can be safelly type-casted using 'toPublicKey'.
data SomePublicKey = forall k. PublicKey k => SomePublicKey !k
deriving Typeable
instance Eq SomePublicKey where
(SomePublicKey a) == (SomePublicKey b)
= case cast b of
Just c -> a == c
Nothing -> False -- different types
instance PublicKey SomePublicKey where
fromPublicKey = id
toPublicKey = Just
instance PKey SomePublicKey where
toPKey (SomePublicKey k) = toPKey k
pkeySize (SomePublicKey k) = pkeySize k
pkeyDefaultMD (SomePublicKey k) = pkeyDefaultMD k
fromPKey pk
= withConcretePubKey pk (return . Just . SomePublicKey)
-- |This is an opaque type to hold an arbitrary keypair in it. The
-- actual key type can be safelly type-casted using 'toKeyPair'.
data SomeKeyPair = forall k. KeyPair k => SomeKeyPair !k
deriving Typeable
instance Eq SomeKeyPair where
(SomeKeyPair a) == (SomeKeyPair b)
= case cast b of
Just c -> a == c
Nothing -> False
instance PublicKey SomeKeyPair where
-- Cast the keypair to a public key, hiding its private part.
fromPublicKey (SomeKeyPair k)
= SomePublicKey k
-- It's impossible to cast a public key to a keypair.
toPublicKey _ = Nothing
instance KeyPair SomeKeyPair where
fromKeyPair = id
toKeyPair = Just
instance PKey SomeKeyPair where
toPKey (SomeKeyPair k) = toPKey k
pkeySize (SomeKeyPair k) = pkeySize k
pkeyDefaultMD (SomeKeyPair k) = pkeyDefaultMD k
fromPKey pk
= withConcreteKeyPair pk (return . Just . SomeKeyPair)
foreign import ccall unsafe "EVP_PKEY_new"
_pkey_new :: IO (Ptr EVP_PKEY)
foreign import ccall unsafe "&EVP_PKEY_free"
_pkey_free :: FunPtr (Ptr EVP_PKEY -> IO ())
wrapPKeyPtr :: Ptr EVP_PKEY -> IO VaguePKey
wrapPKeyPtr
= fmap VaguePKey . newForeignPtr _pkey_free
withPKeyPtr' :: PKey k => k -> (Ptr EVP_PKEY -> IO a) -> IO a
withPKeyPtr' k f = do pk <- toPKey k
withPKeyPtr pk f
withPKeyPtr :: VaguePKey -> (Ptr EVP_PKEY -> IO a) -> IO a
withPKeyPtr (VaguePKey pkey) = withForeignPtr pkey
unsafePKeyToPtr :: VaguePKey -> Ptr EVP_PKEY
unsafePKeyToPtr (VaguePKey pkey) = unsafeForeignPtrToPtr pkey
touchPKey :: VaguePKey -> IO ()
touchPKey (VaguePKey pkey) = touchForeignPtr pkey
#ifndef OPENSSL_NO_RSA
-- The resulting Ptr RSA must be freed by caller.
foreign import ccall unsafe "EVP_PKEY_get1_RSA"
_get1_RSA :: Ptr EVP_PKEY -> IO (Ptr RSA)
foreign import ccall unsafe "EVP_PKEY_set1_RSA"
_set1_RSA :: Ptr EVP_PKEY -> Ptr RSA -> IO CInt
rsaToPKey :: RSAKey k => k -> IO VaguePKey
rsaToPKey rsa
= withRSAPtr rsa $ \ rsaPtr ->
do pkeyPtr <- _pkey_new >>= failIfNull
_set1_RSA pkeyPtr rsaPtr >>= failIf_ (/= 1)
wrapPKeyPtr pkeyPtr
rsaFromPKey :: RSAKey k => VaguePKey -> IO (Maybe k)
rsaFromPKey pk
= withPKeyPtr pk $ \ pkeyPtr ->
do pkeyType <- (#peek EVP_PKEY, type) pkeyPtr :: IO CInt
case pkeyType of
(#const EVP_PKEY_RSA)
-> _get1_RSA pkeyPtr >>= absorbRSAPtr
_ -> return Nothing
instance PublicKey RSAPubKey
instance PKey RSAPubKey where
toPKey = rsaToPKey
fromPKey = rsaFromPKey
pkeySize = rsaSize
pkeyDefaultMD _ = return . fromJust =<< getDigestByName "sha1"
instance KeyPair RSAKeyPair
instance PublicKey RSAKeyPair
instance PKey RSAKeyPair where
toPKey = rsaToPKey
fromPKey = rsaFromPKey
pkeySize = rsaSize
pkeyDefaultMD _ = return . fromJust =<< getDigestByName "sha1"
#endif
#ifndef OPENSSL_NO_DSA
foreign import ccall unsafe "EVP_PKEY_get1_DSA"
_get1_DSA :: Ptr EVP_PKEY -> IO (Ptr DSA)
foreign import ccall unsafe "EVP_PKEY_set1_DSA"
_set1_DSA :: Ptr EVP_PKEY -> Ptr DSA -> IO CInt
dsaToPKey :: DSAKey k => k -> IO VaguePKey
dsaToPKey dsa
= withDSAPtr dsa $ \ dsaPtr ->
do pkeyPtr <- _pkey_new >>= failIfNull
_set1_DSA pkeyPtr dsaPtr >>= failIf_ (/= 1)
wrapPKeyPtr pkeyPtr
dsaFromPKey :: DSAKey k => VaguePKey -> IO (Maybe k)
dsaFromPKey pk
= withPKeyPtr pk $ \ pkeyPtr ->
do pkeyType <- (#peek EVP_PKEY, type) pkeyPtr :: IO CInt
case pkeyType of
(#const EVP_PKEY_DSA)
-> _get1_DSA pkeyPtr >>= absorbDSAPtr
_ -> return Nothing
instance PublicKey DSAPubKey
instance PKey DSAPubKey where
toPKey = dsaToPKey
fromPKey = dsaFromPKey
pkeySize = dsaSize
pkeyDefaultMD _ = return . fromJust =<< getDigestByName "dss1"
instance KeyPair DSAKeyPair
instance PublicKey DSAKeyPair
instance PKey DSAKeyPair where
toPKey = dsaToPKey
fromPKey = dsaFromPKey
pkeySize = dsaSize
pkeyDefaultMD _ = return . fromJust =<< getDigestByName "dss1"
#endif