module OpenSSL.RSA
(
RSAKey(..)
, RSAPubKey
, RSAKeyPair
, RSA
, RSAGenKeyCallback
, generateRSAKey
, generateRSAKey'
, rsaD
, rsaP
, rsaQ
, rsaDMP1
, rsaDMQ1
, rsaIQMP
, rsaCopyPublic
, rsaKeyPairFinalize
)
where
import Control.Monad
import Data.Typeable
import Foreign.C.Types (CInt(..))
import Foreign.ForeignPtr (ForeignPtr, finalizeForeignPtr, newForeignPtr, withForeignPtr)
import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr, nullFunPtr, nullPtr)
import Foreign.Storable (Storable(..))
import OpenSSL.BN
import OpenSSL.Utils
import System.IO.Unsafe (unsafePerformIO)
newtype RSAPubKey = RSAPubKey (ForeignPtr RSA)
deriving Typeable
newtype RSAKeyPair = RSAKeyPair (ForeignPtr RSA)
deriving Typeable
data RSA
class RSAKey k where
rsaSize :: k -> Int
rsaSize rsa
= unsafePerformIO $
withRSAPtr rsa $ \ rsaPtr ->
fmap fromIntegral (_size rsaPtr)
rsaN :: k -> Integer
rsaN = peekI ((\hsc_ptr -> peekByteOff hsc_ptr 32))
rsaE :: k -> Integer
rsaE = peekI ((\hsc_ptr -> peekByteOff hsc_ptr 40))
withRSAPtr :: k -> (Ptr RSA -> IO a) -> IO a
peekRSAPtr :: Ptr RSA -> IO (Maybe k)
absorbRSAPtr :: Ptr RSA -> IO (Maybe k)
instance RSAKey RSAPubKey where
withRSAPtr (RSAPubKey fp) = withForeignPtr fp
peekRSAPtr rsaPtr = _pubDup rsaPtr >>= absorbRSAPtr
absorbRSAPtr rsaPtr = fmap (Just . RSAPubKey) (newForeignPtr _free rsaPtr)
instance RSAKey RSAKeyPair where
withRSAPtr (RSAKeyPair fp) = withForeignPtr fp
peekRSAPtr rsaPtr
= do hasP <- hasRSAPrivateKey rsaPtr
if hasP then
_privDup rsaPtr >>= absorbRSAPtr
else
return Nothing
absorbRSAPtr rsaPtr
= do hasP <- hasRSAPrivateKey rsaPtr
if hasP then
fmap (Just . RSAKeyPair) (newForeignPtr _free rsaPtr)
else
return Nothing
hasRSAPrivateKey :: Ptr RSA -> IO Bool
hasRSAPrivateKey rsaPtr
= do d <- ((\hsc_ptr -> peekByteOff hsc_ptr 48)) rsaPtr
p <- ((\hsc_ptr -> peekByteOff hsc_ptr 56)) rsaPtr
q <- ((\hsc_ptr -> peekByteOff hsc_ptr 64)) rsaPtr
return (d /= nullPtr && p /= nullPtr && q /= nullPtr)
foreign import ccall unsafe "&RSA_free"
_free :: FunPtr (Ptr RSA -> IO ())
foreign import ccall unsafe "RSAPublicKey_dup"
_pubDup :: Ptr RSA -> IO (Ptr RSA)
foreign import ccall unsafe "RSAPrivateKey_dup"
_privDup :: Ptr RSA -> IO (Ptr RSA)
foreign import ccall unsafe "RSA_size"
_size :: Ptr RSA -> IO CInt
rsaCopyPublic :: RSAKey key => key -> IO RSAPubKey
rsaCopyPublic key = withRSAPtr key (fmap RSAPubKey . (newForeignPtr _free =<<) . _pubDup)
rsaKeyPairFinalize :: RSAKeyPair -> IO ()
rsaKeyPairFinalize (RSAKeyPair fp) = finalizeForeignPtr fp
type RSAGenKeyCallback = Int -> Int -> IO ()
type RSAGenKeyCallback' = Int -> Int -> Ptr () -> IO ()
foreign import ccall "wrapper"
mkGenKeyCallback :: RSAGenKeyCallback' -> IO (FunPtr RSAGenKeyCallback')
foreign import ccall safe "RSA_generate_key"
_generate_key :: CInt -> CInt -> FunPtr RSAGenKeyCallback' -> Ptr a -> IO (Ptr RSA)
generateRSAKey :: Int
-> Int
-> Maybe RSAGenKeyCallback
-> IO RSAKeyPair
generateRSAKey nbits e Nothing
= do ptr <- _generate_key (fromIntegral nbits) (fromIntegral e) nullFunPtr nullPtr
failIfNull_ ptr
fmap RSAKeyPair (newForeignPtr _free ptr)
generateRSAKey nbits e (Just cb)
= do cbPtr <- mkGenKeyCallback
$ \ arg1 arg2 _ -> cb arg1 arg2
ptr <- _generate_key (fromIntegral nbits) (fromIntegral e) cbPtr nullPtr
freeHaskellFunPtr cbPtr
failIfNull_ ptr
fmap RSAKeyPair (newForeignPtr _free ptr)
generateRSAKey' :: Int
-> Int
-> IO RSAKeyPair
generateRSAKey' nbits e
= generateRSAKey nbits e Nothing
peekI :: RSAKey a => (Ptr RSA -> IO (Ptr BIGNUM)) -> a -> Integer
peekI peeker rsa
= unsafePerformIO $
withRSAPtr rsa $ \ rsaPtr ->
do bn <- peeker rsaPtr
when (bn == nullPtr) $ fail "peekI: got a nullPtr"
peekBN (wrapBN bn)
peekMI :: RSAKey a => (Ptr RSA -> IO (Ptr BIGNUM)) -> a -> Maybe Integer
peekMI peeker rsa
= unsafePerformIO $
withRSAPtr rsa $ \ rsaPtr ->
do bn <- peeker rsaPtr
if bn == nullPtr then
return Nothing
else
fmap Just (peekBN (wrapBN bn))
rsaD :: RSAKeyPair -> Integer
rsaD = peekI ((\hsc_ptr -> peekByteOff hsc_ptr 48))
rsaP :: RSAKeyPair -> Integer
rsaP = peekI ((\hsc_ptr -> peekByteOff hsc_ptr 56))
rsaQ :: RSAKeyPair -> Integer
rsaQ = peekI ((\hsc_ptr -> peekByteOff hsc_ptr 64))
rsaDMP1 :: RSAKeyPair -> Maybe Integer
rsaDMP1 = peekMI ((\hsc_ptr -> peekByteOff hsc_ptr 72))
rsaDMQ1 :: RSAKeyPair -> Maybe Integer
rsaDMQ1 = peekMI ((\hsc_ptr -> peekByteOff hsc_ptr 80))
rsaIQMP :: RSAKeyPair -> Maybe Integer
rsaIQMP = peekMI ((\hsc_ptr -> peekByteOff hsc_ptr 88))
instance Eq RSAPubKey where
a == b
= rsaN a == rsaN b &&
rsaE a == rsaE b
instance Eq RSAKeyPair where
a == b
= rsaN a == rsaN b &&
rsaE a == rsaE b &&
rsaD a == rsaD b &&
rsaP a == rsaP b &&
rsaQ a == rsaQ b
instance Ord RSAPubKey where
a `compare` b
| rsaN a < rsaN b = LT
| rsaN a > rsaN b = GT
| rsaE a < rsaE b = LT
| rsaE a > rsaE b = GT
| otherwise = EQ
instance Ord RSAKeyPair where
a `compare` b
| rsaN a < rsaN b = LT
| rsaN a > rsaN b = GT
| rsaE a < rsaE b = LT
| rsaE a > rsaE b = GT
| rsaD a < rsaD b = LT
| rsaD a > rsaD b = GT
| rsaP a < rsaP b = LT
| rsaP a > rsaP b = GT
| rsaQ a < rsaQ b = LT
| rsaQ a > rsaQ b = GT
| otherwise = EQ
instance Show RSAPubKey where
show a
= concat [ "RSAPubKey {"
, "rsaN = ", show (rsaN a), ", "
, "rsaE = ", show (rsaE a)
, "}"
]
instance Show RSAKeyPair where
show a
= concat [ "RSAKeyPair {"
, "rsaN = ", show (rsaN a), ", "
, "rsaE = ", show (rsaE a), ", "
, "rsaD = ", show (rsaD a), ", "
, "rsaP = ", show (rsaP a), ", "
, "rsaQ = ", show (rsaQ a)
, "}"
]